codex-weixin 0.2.1

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 (93) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE +5 -0
  4. package/README.en.md +200 -0
  5. package/README.md +210 -0
  6. package/dist/bridge/access.d.ts +21 -0
  7. package/dist/bridge/access.js +31 -0
  8. package/dist/bridge/access.js.map +1 -0
  9. package/dist/bridge/actions.d.ts +21 -0
  10. package/dist/bridge/actions.js +120 -0
  11. package/dist/bridge/actions.js.map +1 -0
  12. package/dist/bridge/errors.d.ts +1 -0
  13. package/dist/bridge/errors.js +15 -0
  14. package/dist/bridge/errors.js.map +1 -0
  15. package/dist/bridge/format.d.ts +14 -0
  16. package/dist/bridge/format.js +62 -0
  17. package/dist/bridge/format.js.map +1 -0
  18. package/dist/bridge/prompt-buffer.d.ts +45 -0
  19. package/dist/bridge/prompt-buffer.js +56 -0
  20. package/dist/bridge/prompt-buffer.js.map +1 -0
  21. package/dist/bridge/service.d.ts +38 -0
  22. package/dist/bridge/service.js +292 -0
  23. package/dist/bridge/service.js.map +1 -0
  24. package/dist/codex/app-server-runner.d.ts +74 -0
  25. package/dist/codex/app-server-runner.js +514 -0
  26. package/dist/codex/app-server-runner.js.map +1 -0
  27. package/dist/codex/exec-runner.d.ts +36 -0
  28. package/dist/codex/exec-runner.js +153 -0
  29. package/dist/codex/exec-runner.js.map +1 -0
  30. package/dist/codex/runner.d.ts +22 -0
  31. package/dist/codex/runner.js +57 -0
  32. package/dist/codex/runner.js.map +1 -0
  33. package/dist/codex/sandbox.d.ts +3 -0
  34. package/dist/codex/sandbox.js +15 -0
  35. package/dist/codex/sandbox.js.map +1 -0
  36. package/dist/index.d.ts +15 -0
  37. package/dist/index.js +16 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/server/account-manager.d.ts +94 -0
  40. package/dist/server/account-manager.js +468 -0
  41. package/dist/server/account-manager.js.map +1 -0
  42. package/dist/server/arguments.d.ts +3 -0
  43. package/dist/server/arguments.js +25 -0
  44. package/dist/server/arguments.js.map +1 -0
  45. package/dist/server/http-server.d.ts +23 -0
  46. package/dist/server/http-server.js +481 -0
  47. package/dist/server/http-server.js.map +1 -0
  48. package/dist/server/index.d.ts +2 -0
  49. package/dist/server/index.js +69 -0
  50. package/dist/server/index.js.map +1 -0
  51. package/dist/server/login-manager.d.ts +27 -0
  52. package/dist/server/login-manager.js +49 -0
  53. package/dist/server/login-manager.js.map +1 -0
  54. package/dist/server/process-lock.d.ts +5 -0
  55. package/dist/server/process-lock.js +63 -0
  56. package/dist/server/process-lock.js.map +1 -0
  57. package/dist/state/config.d.ts +19 -0
  58. package/dist/state/config.js +41 -0
  59. package/dist/state/config.js.map +1 -0
  60. package/dist/state/json-store.d.ts +3 -0
  61. package/dist/state/json-store.js +23 -0
  62. package/dist/state/json-store.js.map +1 -0
  63. package/dist/state/paths.d.ts +12 -0
  64. package/dist/state/paths.js +28 -0
  65. package/dist/state/paths.js.map +1 -0
  66. package/dist/state/runtime-state.d.ts +57 -0
  67. package/dist/state/runtime-state.js +212 -0
  68. package/dist/state/runtime-state.js.map +1 -0
  69. package/dist/web/app.js +958 -0
  70. package/dist/web/index.html +273 -0
  71. package/dist/web/styles.css +324 -0
  72. package/dist/web/vendor/lucide.min.js +12 -0
  73. package/dist/web/vendor/marked.umd.js +79 -0
  74. package/dist/web/vendor/purify.min.js +3 -0
  75. package/dist/weixin/accounts.d.ts +22 -0
  76. package/dist/weixin/accounts.js +66 -0
  77. package/dist/weixin/accounts.js.map +1 -0
  78. package/dist/weixin/api.d.ts +75 -0
  79. package/dist/weixin/api.js +191 -0
  80. package/dist/weixin/api.js.map +1 -0
  81. package/dist/weixin/login.d.ts +29 -0
  82. package/dist/weixin/login.js +100 -0
  83. package/dist/weixin/login.js.map +1 -0
  84. package/dist/weixin/media.d.ts +32 -0
  85. package/dist/weixin/media.js +266 -0
  86. package/dist/weixin/media.js.map +1 -0
  87. package/dist/weixin/messages.d.ts +24 -0
  88. package/dist/weixin/messages.js +87 -0
  89. package/dist/weixin/messages.js.map +1 -0
  90. package/dist/weixin/monitor.d.ts +22 -0
  91. package/dist/weixin/monitor.js +110 -0
  92. package/dist/weixin/monitor.js.map +1 -0
  93. package/package.json +61 -0
@@ -0,0 +1,273 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="color-scheme" content="light">
7
+ <title>codex-weixin</title>
8
+ <link rel="stylesheet" href="/styles.css">
9
+ <script defer src="/vendor/lucide.min.js"></script>
10
+ <script defer src="/vendor/marked.umd.js"></script>
11
+ <script defer src="/vendor/purify.min.js"></script>
12
+ <script defer src="/app.js"></script>
13
+ </head>
14
+ <body>
15
+ <header class="app-header">
16
+ <div class="header-inner">
17
+ <a class="brand" href="#accounts" aria-label="codex-weixin 首页">
18
+ <span class="brand-mark" aria-hidden="true">
19
+ <i data-lucide="terminal-square"></i>
20
+ <i data-lucide="message-circle"></i>
21
+ </span>
22
+ <span class="brand-name">codex-weixin</span>
23
+ </a>
24
+ <nav class="view-tabs" aria-label="管理视图">
25
+ <button class="tab is-active" type="button" data-view="accounts">微信账号</button>
26
+ <button class="tab" type="button" data-view="sessions">会话</button>
27
+ <button class="tab" type="button" data-view="settings">设置</button>
28
+ </nav>
29
+ <button class="button button-primary header-action" id="addAccountButton" type="button" title="添加微信" aria-label="添加微信">
30
+ <i data-lucide="scan-line"></i>
31
+ <span>添加微信</span>
32
+ </button>
33
+ </div>
34
+ </header>
35
+
36
+ <main>
37
+ <section class="status-band" aria-label="服务概览">
38
+ <div class="status-inner">
39
+ <div class="service-state"><span class="pulse-dot" id="serviceDot"></span><span id="serviceStateText">正在检查 Codex</span></div>
40
+ <dl class="metrics">
41
+ <div><dt>运行账号</dt><dd id="runningAccountMetric">0</dd></div>
42
+ <div><dt>受管会话</dt><dd id="sessionMetric">0</dd></div>
43
+ <div class="workspace-metric"><dt>Codex 工作目录</dt><dd id="workspaceMetric">--</dd></div>
44
+ </dl>
45
+ </div>
46
+ </section>
47
+
48
+ <section class="view is-visible" id="accountsView" data-view-panel="accounts">
49
+ <div class="section-heading">
50
+ <div>
51
+ <p class="eyebrow">WECHAT ACCOUNTS</p>
52
+ <h1>微信账号</h1>
53
+ </div>
54
+ <button class="icon-button" id="refreshAccountsButton" type="button" title="刷新账号状态" aria-label="刷新账号状态">
55
+ <i data-lucide="refresh-cw"></i>
56
+ </button>
57
+ </div>
58
+ <div id="accountsList" class="account-list" aria-live="polite"></div>
59
+ </section>
60
+
61
+ <section class="view" id="sessionsView" data-view-panel="sessions" hidden>
62
+ <div class="section-heading">
63
+ <div>
64
+ <p class="eyebrow">CODEX SESSIONS</p>
65
+ <h1>会话</h1>
66
+ </div>
67
+ <button class="button button-secondary" id="newSessionButton" type="button">
68
+ <i data-lucide="plus"></i>
69
+ <span>新建会话</span>
70
+ </button>
71
+ </div>
72
+ <div class="session-account-bar" aria-label="微信账号筛选">
73
+ <span class="session-account-bar-label"><i data-lucide="users"></i><span>微信账号</span></span>
74
+ <div class="session-account-tabs" id="sessionAccountTabs" aria-label="按微信账号筛选会话"></div>
75
+ </div>
76
+ <div class="session-workbench">
77
+ <aside class="session-sidebar" aria-label="Codex 会话列表">
78
+ <div class="session-sidebar-head">
79
+ <span>历史会话</span>
80
+ <span id="sessionListCount">0</span>
81
+ </div>
82
+ <div id="sessionsList" class="session-list" aria-live="polite"></div>
83
+ </aside>
84
+ <section class="chat-panel" id="sessionChatPanel" aria-label="会话消息">
85
+ <header class="chat-header" id="chatHeader">
86
+ <div>
87
+ <p class="eyebrow">THREAD HISTORY</p>
88
+ <h2 id="chatTitle">选择一个会话</h2>
89
+ <p class="chat-context" id="chatContext">查看历史消息并继续聊天</p>
90
+ </div>
91
+ <button class="icon-button" id="refreshMessagesButton" type="button" title="刷新消息" aria-label="刷新消息" disabled>
92
+ <i data-lucide="refresh-cw"></i>
93
+ </button>
94
+ </header>
95
+ <div class="chat-messages" id="chatMessages" aria-live="polite"></div>
96
+ <form class="chat-composer" id="chatComposer">
97
+ <div class="composer-files" id="composerFiles" aria-live="polite" hidden></div>
98
+ <div class="composer-controls">
99
+ <button class="icon-button chat-attach" id="chatAttachButton" type="button" title="添加附件" aria-label="添加附件" disabled>
100
+ <i data-lucide="paperclip"></i>
101
+ </button>
102
+ <input id="chatFileInput" type="file" multiple hidden disabled>
103
+ <label class="sr-only" for="chatInput">继续聊天</label>
104
+ <textarea id="chatInput" rows="2" maxlength="100000" placeholder="给 Codex 发送消息…" disabled></textarea>
105
+ <button class="button button-primary chat-send" id="chatSendButton" type="submit" disabled>
106
+ <i data-lucide="send"></i>
107
+ <span>发送</span>
108
+ </button>
109
+ </div>
110
+ <p class="composer-hint">Enter 发送 · Shift + Enter 换行</p>
111
+ </form>
112
+ </section>
113
+ </div>
114
+ </section>
115
+
116
+ <section class="view settings-view" id="settingsView" data-view-panel="settings" hidden>
117
+ <div class="section-heading">
118
+ <div>
119
+ <p class="eyebrow">LOCAL CODEX</p>
120
+ <h1>设置</h1>
121
+ </div>
122
+ </div>
123
+ <form id="settingsForm" class="settings-form">
124
+ <div class="settings-group">
125
+ <h2>工作区</h2>
126
+ <label>
127
+ <span>默认工作目录</span>
128
+ <input id="defaultCwdInput" name="defaultCwd" autocomplete="off" required>
129
+ </label>
130
+ <label>
131
+ <span>允许的工作目录</span>
132
+ <textarea id="allowedWorkspacesInput" name="allowedWorkspaces" rows="5" required></textarea>
133
+ </label>
134
+ </div>
135
+ <div class="settings-group">
136
+ <h2>Codex</h2>
137
+ <div class="field-grid">
138
+ <label>
139
+ <span>后端</span>
140
+ <select id="backendInput" name="codexBackend">
141
+ <option value="auto">自动</option>
142
+ <option value="app-server">App server</option>
143
+ <option value="exec">Exec</option>
144
+ </select>
145
+ </label>
146
+ <label>
147
+ <span>Exec 沙箱</span>
148
+ <select id="sandboxInput" name="codexExecSandbox">
149
+ <option value="">沿用 Codex 设置</option>
150
+ <option value="read-only">只读</option>
151
+ <option value="workspace-write">工作区可写</option>
152
+ <option value="danger-full-access">完整访问</option>
153
+ </select>
154
+ </label>
155
+ <label>
156
+ <span>模型</span>
157
+ <select id="modelInput" name="model">
158
+ <option value="">沿用 Codex 设置</option>
159
+ </select>
160
+ <small class="field-hint effective-setting"><span>当前生效</span><strong id="effectiveModelValue">正在读取</strong></small>
161
+ </label>
162
+ <label>
163
+ <span>推理强度</span>
164
+ <select id="effortInput" name="effort">
165
+ <option value="">沿用模型默认</option>
166
+ </select>
167
+ <small class="field-hint effective-setting"><span>当前生效</span><strong id="effectiveEffortValue">正在读取</strong></small>
168
+ </label>
169
+ </div>
170
+ </div>
171
+ <div class="form-actions">
172
+ <button class="button button-primary" type="submit">
173
+ <i data-lucide="save"></i>
174
+ <span>保存设置</span>
175
+ </button>
176
+ </div>
177
+ </form>
178
+ </section>
179
+ </main>
180
+
181
+ <dialog class="modal qr-modal" id="qrDialog">
182
+ <div class="modal-header">
183
+ <div>
184
+ <p class="eyebrow">WECHAT LOGIN</p>
185
+ <h2>微信扫码登录</h2>
186
+ </div>
187
+ <button class="icon-button" type="button" data-close-dialog="qrDialog" title="关闭" aria-label="关闭扫码窗口">
188
+ <i data-lucide="x"></i>
189
+ </button>
190
+ </div>
191
+ <div class="qr-stage">
192
+ <div class="qr-frame" id="qrFrame">
193
+ <div class="spinner" aria-label="正在生成二维码"></div>
194
+ </div>
195
+ <p id="qrStatus" class="qr-status">正在生成二维码</p>
196
+ </div>
197
+ <div class="modal-footer">
198
+ <button class="button button-secondary" id="refreshQrButton" type="button">
199
+ <i data-lucide="refresh-cw"></i>
200
+ <span>刷新二维码</span>
201
+ </button>
202
+ </div>
203
+ </dialog>
204
+
205
+ <dialog class="modal" id="accountDialog">
206
+ <form id="accountForm" method="dialog">
207
+ <div class="modal-header">
208
+ <div>
209
+ <p class="eyebrow">ACCOUNT REMARK</p>
210
+ <h2>修改微信账号备注</h2>
211
+ </div>
212
+ <button class="icon-button" type="button" data-close-dialog="accountDialog" title="关闭" aria-label="关闭账号备注窗口">
213
+ <i data-lucide="x"></i>
214
+ </button>
215
+ </div>
216
+ <input id="editingRemarkAccountId" type="hidden">
217
+ <div class="modal-body form-stack">
218
+ <label>
219
+ <span>账号备注</span>
220
+ <input id="accountRemarkInput" maxlength="40" autocomplete="off" placeholder="例如:张三、李四">
221
+ <small class="field-hint">备注只保存在本机;清空后恢复默认名称。</small>
222
+ </label>
223
+ </div>
224
+ <div class="modal-footer">
225
+ <button class="button button-secondary" type="button" data-close-dialog="accountDialog">取消</button>
226
+ <button class="button button-primary" type="submit">
227
+ <i data-lucide="check"></i>
228
+ <span>保存</span>
229
+ </button>
230
+ </div>
231
+ </form>
232
+ </dialog>
233
+
234
+ <dialog class="modal session-modal" id="sessionDialog">
235
+ <form id="sessionForm" method="dialog">
236
+ <div class="modal-header">
237
+ <div>
238
+ <p class="eyebrow">MANAGED SESSION</p>
239
+ <h2 id="sessionDialogTitle">新建会话</h2>
240
+ </div>
241
+ <button class="icon-button" type="button" data-close-dialog="sessionDialog" title="关闭" aria-label="关闭会话窗口">
242
+ <i data-lucide="x"></i>
243
+ </button>
244
+ </div>
245
+ <input id="editingSessionId" type="hidden">
246
+ <input id="editingAccountId" type="hidden">
247
+ <div class="modal-body form-stack">
248
+ <label id="senderField">
249
+ <span>微信账号</span>
250
+ <select id="sessionSenderInput" required></select>
251
+ </label>
252
+ <label>
253
+ <span>会话名称</span>
254
+ <input id="sessionTitleInput" maxlength="80" autocomplete="off" required>
255
+ </label>
256
+ <label id="sessionWorkspaceField">
257
+ <span>工作目录</span>
258
+ <input id="sessionWorkspaceInput" autocomplete="off" required>
259
+ </label>
260
+ </div>
261
+ <div class="modal-footer">
262
+ <button class="button button-secondary" type="button" data-close-dialog="sessionDialog">取消</button>
263
+ <button class="button button-primary" type="submit">
264
+ <i data-lucide="check"></i>
265
+ <span>保存</span>
266
+ </button>
267
+ </div>
268
+ </form>
269
+ </dialog>
270
+
271
+ <div class="toast-region" id="toastRegion" aria-live="polite"></div>
272
+ </body>
273
+ </html>
@@ -0,0 +1,324 @@
1
+ :root {
2
+ --canvas: #f3f5f2;
3
+ --surface: #ffffff;
4
+ --surface-muted: #e9eeea;
5
+ --ink: #151b19;
6
+ --ink-soft: #59635f;
7
+ --line: #d9dfdb;
8
+ --green: #12a150;
9
+ --green-dark: #087239;
10
+ --green-soft: #dff4e6;
11
+ --amber: #b36b09;
12
+ --amber-soft: #fff0d5;
13
+ --red: #c8403d;
14
+ --red-soft: #fde8e7;
15
+ --focus: #146bd1;
16
+ --shadow: 0 16px 48px rgb(23 34 29 / 14%);
17
+ --radius: 6px;
18
+ --content: 1180px;
19
+ font-family: "Avenir Next", Avenir, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
20
+ color: var(--ink);
21
+ background: var(--canvas);
22
+ font-synthesis: none;
23
+ }
24
+
25
+ * { box-sizing: border-box; }
26
+ html { min-width: 320px; background: var(--canvas); }
27
+ body { margin: 0; min-height: 100vh; background: var(--canvas); letter-spacing: 0; }
28
+ button, input, select, textarea { font: inherit; letter-spacing: 0; }
29
+ button, select { cursor: pointer; }
30
+ button:disabled { cursor: not-allowed; opacity: .48; }
31
+ :focus-visible { outline: 3px solid color-mix(in srgb, var(--focus) 30%, transparent); outline-offset: 2px; }
32
+
33
+ .app-header { position: sticky; top: 0; z-index: 20; border-bottom: 1px solid var(--line); background: rgb(255 255 255 / 94%); backdrop-filter: blur(14px); }
34
+ .header-inner { width: min(calc(100% - 40px), var(--content)); min-height: 66px; margin: 0 auto; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 24px; }
35
+ .brand { min-width: 0; color: var(--ink); text-decoration: none; display: inline-flex; align-items: center; gap: 11px; }
36
+ .brand-mark { width: 38px; height: 38px; position: relative; flex: 0 0 38px; border-radius: var(--radius); color: white; background: var(--ink); }
37
+ .brand-mark svg { position: absolute; width: 20px; height: 20px; stroke-width: 1.8; }
38
+ .brand-mark svg:first-child { left: 6px; top: 6px; }
39
+ .brand-mark svg:last-child { right: 3px; bottom: 3px; padding: 2px; color: white; border-radius: 50%; background: var(--green); }
40
+ .brand-name { overflow: hidden; font-size: 15px; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
41
+ .view-tabs { height: 38px; display: inline-flex; align-items: center; padding: 3px; gap: 2px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--canvas); }
42
+ .tab { min-width: 82px; height: 30px; border: 0; border-radius: 4px; color: var(--ink-soft); background: transparent; font-size: 13px; font-weight: 650; }
43
+ .tab:hover { color: var(--ink); }
44
+ .tab.is-active { color: var(--ink); background: var(--surface); box-shadow: 0 1px 3px rgb(20 27 25 / 12%); }
45
+ .header-action { justify-self: end; }
46
+
47
+ .button { min-height: 38px; padding: 0 14px; border: 1px solid transparent; border-radius: var(--radius); display: inline-flex; align-items: center; justify-content: center; gap: 8px; font-size: 13px; font-weight: 700; white-space: nowrap; transition: background-color .16s ease, border-color .16s ease, transform .16s ease; }
48
+ .button svg { width: 17px; height: 17px; }
49
+ .button:active:not(:disabled) { transform: translateY(1px); }
50
+ .button-primary { color: white; background: var(--green-dark); }
51
+ .button-primary:hover { background: #075f31; }
52
+ .button-secondary { color: var(--ink); border-color: var(--line); background: var(--surface); }
53
+ .button-secondary:hover { border-color: #b7c0bb; background: #f8faf8; }
54
+ .button-danger { color: var(--red); border-color: #efc5c3; background: var(--surface); }
55
+ .icon-button { width: 38px; height: 38px; padding: 0; border: 1px solid var(--line); border-radius: var(--radius); color: var(--ink-soft); background: var(--surface); display: inline-grid; place-items: center; }
56
+ .icon-button:hover { color: var(--ink); border-color: #aeb8b2; }
57
+ .icon-button svg { width: 18px; height: 18px; }
58
+ .icon-button.is-danger:hover { color: var(--red); border-color: #e3aaa7; background: var(--red-soft); }
59
+
60
+ .status-band { border-bottom: 1px solid var(--line); background: #e8ece9; }
61
+ .status-inner { width: min(calc(100% - 40px), var(--content)); min-height: 84px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 36px; }
62
+ .service-state { display: flex; align-items: center; gap: 10px; font-size: 13px; font-weight: 700; }
63
+ .pulse-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 5px rgb(18 161 80 / 12%); }
64
+ .pulse-dot.is-error { background: var(--red); box-shadow: 0 0 0 5px rgb(200 64 61 / 12%); }
65
+ .metrics { margin: 0; display: grid; grid-template-columns: 100px 100px minmax(240px, 1fr); gap: 28px; }
66
+ .metrics div { min-width: 0; }
67
+ .metrics dt { margin-bottom: 5px; color: var(--ink-soft); font-size: 11px; font-weight: 650; text-transform: uppercase; }
68
+ .metrics dd { margin: 0; overflow: hidden; font-size: 14px; font-weight: 750; text-overflow: ellipsis; white-space: nowrap; }
69
+ .workspace-metric { max-width: 430px; }
70
+
71
+ main { min-height: calc(100vh - 151px); }
72
+ .view { width: min(calc(100% - 40px), var(--content)); margin: 0 auto; padding: 44px 0 80px; }
73
+ .section-heading { min-height: 54px; margin-bottom: 24px; display: flex; align-items: end; justify-content: space-between; gap: 20px; }
74
+ .eyebrow { margin: 0 0 5px; color: var(--green-dark); font-size: 10px; font-weight: 800; letter-spacing: .12em; }
75
+ h1, h2, p { letter-spacing: 0; }
76
+ h1 { margin: 0; font-size: 30px; line-height: 1.15; font-weight: 750; }
77
+ h2 { margin: 0; font-size: 19px; line-height: 1.3; }
78
+
79
+ .account-list { display: grid; gap: 12px; }
80
+ .account-card { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); overflow: hidden; }
81
+ .account-main { min-height: 98px; padding: 20px; display: grid; grid-template-columns: minmax(220px, 1.3fr) minmax(140px, .7fr) minmax(180px, .8fr) auto; align-items: center; gap: 22px; }
82
+ .account-identity { min-width: 0; display: flex; align-items: center; gap: 14px; }
83
+ .account-avatar { width: 46px; height: 46px; flex: 0 0 46px; border-radius: var(--radius); color: var(--green-dark); background: var(--green-soft); display: grid; place-items: center; }
84
+ .account-avatar svg { width: 24px; height: 24px; }
85
+ .account-name { min-width: 0; }
86
+ .account-name strong { display: block; overflow: hidden; font-size: 14px; text-overflow: ellipsis; white-space: nowrap; }
87
+ .account-description { margin-top: 4px; color: var(--ink-soft); font-size: 11px; }
88
+ .status-label { display: inline-flex; align-items: center; gap: 7px; color: var(--ink-soft); font-size: 12px; font-weight: 700; }
89
+ .status-label::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: #8f9994; }
90
+ .status-running { color: var(--green-dark); }
91
+ .status-running::before { background: var(--green); }
92
+ .status-error { color: var(--red); }
93
+ .status-error::before { background: var(--red); }
94
+ .account-stat span { display: block; color: var(--ink-soft); font-size: 11px; }
95
+ .account-stat strong { display: block; margin-top: 4px; font-size: 15px; }
96
+ .account-actions { display: flex; gap: 8px; }
97
+ .account-detail { padding: 14px 20px; border-top: 1px solid var(--line); background: #fafbfa; display: flex; align-items: center; justify-content: space-between; gap: 18px; }
98
+ .authorization-state { width: 100%; min-width: 0; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 12px; }
99
+ .authorization-icon { width: 34px; height: 34px; border-radius: 50%; color: var(--ink-soft); background: var(--surface-muted); display: grid; place-items: center; }
100
+ .authorization-icon svg { width: 17px; height: 17px; }
101
+ .authorization-copy { min-width: 0; }
102
+ .authorization-copy strong { display: block; font-size: 12px; }
103
+ .authorization-copy span { display: block; margin-top: 2px; overflow: hidden; color: var(--ink-soft); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
104
+ .authorization-state.is-authorized .authorization-icon { color: var(--green-dark); background: var(--green-soft); }
105
+ .authorization-state.is-authorized .authorization-copy strong { color: var(--green-dark); }
106
+ .authorization-state.is-pending .authorization-icon { color: var(--amber); background: var(--amber-soft); }
107
+ .authorization-state.is-pending .authorization-copy strong { color: #85520b; }
108
+ .authorization-action { min-height: 34px; }
109
+ .pending-access { padding: 12px 20px; border-top: 1px solid #ecd4aa; background: var(--amber-soft); display: flex; align-items: center; justify-content: space-between; gap: 16px; }
110
+ .pending-access div { min-width: 0; }
111
+ .pending-access strong { display: block; font-size: 12px; }
112
+ .pending-access span { display: block; margin-top: 3px; overflow: hidden; color: #78521a; font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
113
+
114
+ .session-account-bar { min-height: 54px; padding: 10px 12px; border: 1px solid #cdd5d0; border-top-color: var(--ink); border-bottom: 0; border-radius: var(--radius) var(--radius) 0 0; background: #f7f9f7; display: flex; align-items: center; gap: 14px; }
115
+ .session-account-bar-label { flex: 0 0 auto; color: var(--ink-soft); display: inline-flex; align-items: center; gap: 7px; font-size: 10px; font-weight: 800; letter-spacing: .08em; }
116
+ .session-account-bar-label svg { width: 15px; height: 15px; }
117
+ .session-workbench { min-height: 650px; border: 1px solid #cdd5d0; border-radius: 0 0 var(--radius) var(--radius); background: var(--surface); display: grid; grid-template-columns: minmax(270px, 330px) minmax(0, 1fr); overflow: hidden; box-shadow: 0 12px 32px rgb(23 34 29 / 7%); }
118
+ .session-sidebar { min-width: 0; border-right: 1px solid var(--line); background: #f7f9f7; display: grid; grid-template-rows: auto minmax(0, 1fr); }
119
+ .session-sidebar-head { min-height: 48px; padding: 0 15px; border-bottom: 1px solid var(--line); color: var(--ink-soft); display: flex; align-items: center; justify-content: space-between; font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; }
120
+ .session-sidebar-head span:last-child { min-width: 24px; height: 22px; padding: 0 7px; border-radius: 11px; color: var(--green-dark); background: var(--green-soft); display: inline-flex; align-items: center; justify-content: center; }
121
+ .session-account-tabs { min-width: 0; flex: 1; display: flex; gap: 6px; overflow-x: auto; scrollbar-width: thin; }
122
+ .session-account-tab { min-width: max-content; height: 32px; padding: 0 9px; border: 1px solid var(--line); border-radius: 5px; color: var(--ink-soft); background: transparent; display: inline-flex; align-items: center; gap: 6px; font-size: 10px; font-weight: 750; }
123
+ .session-account-tab:hover { color: var(--ink); background: white; }
124
+ .session-account-tab.is-active { color: #075f31; border-color: #9ed4ae; background: var(--green-soft); }
125
+ .session-account-tab svg { width: 14px; height: 14px; }
126
+ .session-account-tab b { min-width: 17px; height: 17px; border-radius: 9px; color: inherit; background: rgb(255 255 255 / 72%); display: inline-flex; align-items: center; justify-content: center; font-size: 9px; }
127
+ .session-list { min-height: 0; max-height: 700px; overflow-y: auto; }
128
+ .session-list .empty-state { min-height: 360px; margin: 12px; }
129
+ .session-card { position: relative; border-bottom: 1px solid var(--line); background: transparent; transition: background-color .16s ease, box-shadow .16s ease; }
130
+ .session-card:hover { background: white; }
131
+ .session-card.is-selected { background: white; box-shadow: inset 3px 0 0 var(--green); }
132
+ .session-open { width: 100%; padding: 17px 14px 12px 17px; border: 0; color: var(--ink); background: transparent; text-align: left; display: grid; gap: 8px; }
133
+ .session-card-top { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 10px; }
134
+ .session-card-top strong { min-width: 0; overflow: hidden; display: flex; align-items: center; gap: 7px; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
135
+ .session-card-top time { flex: 0 0 auto; color: var(--ink-soft); font-size: 9px; }
136
+ .active-mark { width: 7px; height: 7px; flex: 0 0 7px; border-radius: 50%; background: var(--green); }
137
+ .session-owner { min-width: 0; color: var(--ink-soft); display: flex; gap: 6px; font-size: 10px; }
138
+ .session-owner strong, .session-owner span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
139
+ .session-owner strong { color: var(--ink); }
140
+ .session-workspace, .session-thread { overflow: hidden; color: var(--ink-soft); font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 9px; text-overflow: ellipsis; white-space: nowrap; }
141
+ .session-thread.is-responding { color: var(--green-dark); font-family: inherit; font-weight: 750; }
142
+ .session-actions { padding: 0 12px 12px 17px; display: flex; gap: 5px; }
143
+ .session-actions .icon-button { width: 29px; height: 29px; background: transparent; }
144
+ .session-actions .icon-button svg { width: 15px; height: 15px; }
145
+
146
+ .chat-panel { min-width: 0; min-height: 650px; background: linear-gradient(180deg, #fff 0, #fbfcfb 100%); display: grid; grid-template-rows: auto minmax(0, 1fr) auto; }
147
+ .chat-header { min-height: 82px; padding: 17px 20px; border-bottom: 1px solid var(--line); display: flex; align-items: center; justify-content: space-between; gap: 18px; }
148
+ .chat-header h2 { max-width: 620px; overflow: hidden; font-size: 17px; text-overflow: ellipsis; white-space: nowrap; }
149
+ .chat-context { max-width: 720px; margin: 5px 0 0; overflow: hidden; color: var(--ink-soft); font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 9px; text-overflow: ellipsis; white-space: nowrap; }
150
+ .chat-messages { min-height: 0; max-height: 560px; padding: 26px 24px 30px; overflow-y: auto; scroll-behavior: smooth; background-image: radial-gradient(circle at 1px 1px, rgb(31 57 44 / 5%) 1px, transparent 0); background-size: 24px 24px; }
151
+ .chat-message { width: fit-content; min-width: 0; max-width: min(78%, 720px); margin-bottom: 22px; display: grid; gap: 6px; }
152
+ .chat-message.is-user { margin-left: auto; justify-items: end; }
153
+ .chat-message.is-assistant { margin-right: auto; justify-items: start; }
154
+ .chat-message.has-attachments { width: min(78%, 720px); }
155
+ .message-meta { padding: 0 3px; color: var(--ink-soft); display: flex; align-items: center; gap: 8px; font-size: 9px; font-weight: 750; }
156
+ .message-meta time { font-weight: 550; }
157
+ .message-bubble { max-width: 100%; padding: 13px 15px; border: 1px solid var(--line); border-radius: 7px; background: white; box-shadow: 0 4px 14px rgb(23 34 29 / 6%); font-size: 13px; line-height: 1.65; overflow-wrap: anywhere; }
158
+ .chat-message.is-user .message-bubble { border-color: #9ddcaf; color: #083d21; background: #bcf0c9; box-shadow: none; }
159
+ .message-bubble > :first-child { margin-top: 0; }
160
+ .message-bubble > :last-child { margin-bottom: 0; }
161
+ .message-bubble p { margin: 0 0 10px; }
162
+ .message-bubble h1, .message-bubble h2, .message-bubble h3, .message-bubble h4 { margin: 16px 0 8px; font-size: 1em; }
163
+ .message-bubble ul, .message-bubble ol { margin: 8px 0; padding-left: 22px; }
164
+ .message-bubble li + li { margin-top: 4px; }
165
+ .message-bubble blockquote { margin: 10px 0; padding: 2px 0 2px 12px; border-left: 3px solid #94a49c; color: var(--ink-soft); }
166
+ .message-bubble code { padding: 2px 5px; border-radius: 4px; color: #873d12; background: #f2eee8; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: .9em; }
167
+ .message-bubble pre { max-width: 100%; margin: 10px 0; padding: 12px; border-radius: 6px; color: #e8f3ec; background: #17211d; overflow-x: auto; }
168
+ .message-bubble pre code { padding: 0; color: inherit; background: transparent; }
169
+ .message-bubble a { color: #075f87; text-decoration-thickness: 1px; text-underline-offset: 2px; }
170
+ .message-bubble table { display: block; max-width: 100%; border-collapse: collapse; overflow-x: auto; }
171
+ .message-bubble th, .message-bubble td { padding: 6px 8px; border: 1px solid var(--line); text-align: left; }
172
+ .chat-message.has-attachments .message-bubble { width: 100%; }
173
+ .message-attachments { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); display: grid; gap: 12px; }
174
+ .message-bubble > .message-attachments:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
175
+ .message-attachment { min-width: 0; }
176
+ .message-attachment video, .message-attachment img { display: block; width: 100%; max-height: 360px; margin-bottom: 10px; border-radius: 6px; background: #111; object-fit: contain; }
177
+ .message-attachment video { aspect-ratio: 16 / 9; }
178
+ .message-attachment img { background: #f2f5f3; }
179
+ .message-attachment.is-file .attachment-meta, .message-attachment.is-missing .attachment-meta { padding: 10px; border: 1px solid var(--line); border-radius: 6px; background: #f8faf8; }
180
+ .attachment-meta { min-width: 0; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 9px; }
181
+ .attachment-type-icon { width: 32px; height: 32px; border-radius: 5px; color: var(--green-dark); background: var(--green-soft); display: grid; place-items: center; }
182
+ .attachment-type-icon svg { width: 16px; height: 16px; }
183
+ .attachment-copy { min-width: 0; }
184
+ .attachment-copy strong, .attachment-copy small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
185
+ .attachment-copy strong { font-size: 11px; }
186
+ .attachment-copy small { margin-top: 2px; color: var(--ink-soft); font-size: 9px; }
187
+ .attachment-download { width: 32px; height: 32px; background: transparent; }
188
+ .attachment-download svg { width: 15px; height: 15px; }
189
+ .message-attachment.is-missing .attachment-type-icon { color: var(--red); background: var(--red-soft); }
190
+ .chat-typing { width: fit-content; min-height: 38px; margin: 0 0 18px; padding: 0 13px; border: 1px solid var(--line); border-radius: 7px; color: var(--ink-soft); background: white; display: inline-flex; align-items: center; gap: 9px; font-size: 10px; font-weight: 700; box-shadow: 0 4px 14px rgb(23 34 29 / 6%); }
191
+ .typing-dots { width: 25px; height: 12px; display: inline-flex; align-items: center; justify-content: space-between; }
192
+ .typing-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--green); animation: typing-dot 1.1s ease-in-out infinite; }
193
+ .typing-dots i:nth-child(2) { animation-delay: .14s; }
194
+ .typing-dots i:nth-child(3) { animation-delay: .28s; }
195
+ .chat-loading { min-height: 100%; color: var(--ink-soft); display: grid; place-content: center; justify-items: center; gap: 13px; font-size: 11px; font-weight: 700; }
196
+ .chat-empty { min-height: 100%; color: var(--ink-soft); display: grid; place-content: center; justify-items: center; text-align: center; }
197
+ .chat-empty > span { width: 48px; height: 48px; margin-bottom: 14px; border: 1px solid var(--line); border-radius: 50%; background: white; display: grid; place-items: center; }
198
+ .chat-empty svg { width: 21px; height: 21px; }
199
+ .chat-empty strong { color: var(--ink); font-size: 13px; }
200
+ .chat-empty p { max-width: 320px; margin: 6px 0 0; font-size: 10px; }
201
+ .chat-composer { padding: 12px 16px 9px; border-top: 1px solid var(--line); background: white; display: grid; gap: 9px; }
202
+ .composer-controls { min-width: 0; display: grid; grid-template-columns: 42px minmax(0, 1fr) auto; align-items: start; gap: 9px; }
203
+ .chat-composer textarea { min-height: 74px; max-height: 180px; padding: 12px 14px; resize: vertical; }
204
+ .chat-attach { width: 42px; height: 42px; color: var(--green-dark); background: var(--green-soft); }
205
+ .chat-send { min-width: 78px; height: 42px; }
206
+ .composer-hint { margin: 0 0 0 51px; color: var(--ink-soft); font-size: 9px; }
207
+ .composer-files { padding-left: 51px; display: flex; flex-wrap: wrap; gap: 7px; }
208
+ .composer-files[hidden] { display: none; }
209
+ .composer-file { min-width: 0; max-width: min(100%, 290px); height: 36px; padding: 0 4px 0 9px; border: 1px solid var(--line); border-radius: 5px; background: #f7f9f7; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 7px; }
210
+ .composer-file > svg { width: 15px; height: 15px; color: var(--green-dark); }
211
+ .composer-file-copy { min-width: 0; overflow: hidden; display: flex; align-items: baseline; gap: 6px; }
212
+ .composer-file-copy strong { overflow: hidden; font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
213
+ .composer-file-copy small { flex: 0 0 auto; color: var(--ink-soft); font-size: 8px; }
214
+ .composer-file-remove { width: 27px; height: 27px; border: 0; background: transparent; }
215
+ .composer-file-remove svg { width: 14px; height: 14px; }
216
+
217
+ .empty-state { min-height: 280px; padding: 48px 24px; border: 1px dashed #bbc4bf; border-radius: var(--radius); display: grid; place-items: center; text-align: center; }
218
+ .empty-state-inner { max-width: 360px; }
219
+ .empty-icon { width: 54px; height: 54px; margin: 0 auto 18px; border-radius: var(--radius); color: var(--ink-soft); background: var(--surface-muted); display: grid; place-items: center; }
220
+ .empty-icon svg { width: 25px; height: 25px; }
221
+ .empty-state h2 { font-size: 16px; }
222
+ .empty-state p { margin: 7px 0 20px; color: var(--ink-soft); font-size: 12px; }
223
+
224
+ .settings-form { border-top: 1px solid var(--ink); }
225
+ .settings-group { padding: 26px 0 30px; border-bottom: 1px solid var(--line); display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: 30px; }
226
+ .settings-group h2 { font-size: 14px; }
227
+ .settings-group > label, .field-grid { width: 100%; max-width: 720px; grid-column: 2; }
228
+ .field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
229
+ label { min-width: 0; display: grid; gap: 8px; color: var(--ink-soft); font-size: 11px; font-weight: 700; }
230
+ .field-hint { color: var(--ink-soft); font-size: 10px; font-weight: 500; line-height: 1.5; }
231
+ .effective-setting { min-width: 0; display: flex; align-items: center; gap: 7px; }
232
+ .effective-setting span { flex: 0 0 auto; }
233
+ .effective-setting strong { min-width: 0; overflow: hidden; color: var(--green-dark); font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
234
+ input, select, textarea { width: 100%; border: 1px solid #bdc6c1; border-radius: var(--radius); color: var(--ink); background: var(--surface); }
235
+ input, select { min-height: 42px; padding: 0 12px; }
236
+ textarea { min-height: 110px; padding: 11px 12px; resize: vertical; line-height: 1.5; }
237
+ input:hover, select:hover, textarea:hover { border-color: #8e9a93; }
238
+ input:focus, select:focus, textarea:focus { border-color: var(--focus); outline: 3px solid color-mix(in srgb, var(--focus) 15%, transparent); }
239
+ .form-actions { padding-top: 24px; display: flex; justify-content: end; }
240
+
241
+ .modal { width: min(calc(100% - 32px), 480px); max-height: min(760px, calc(100vh - 32px)); padding: 0; border: 1px solid #c7cfca; border-radius: 8px; color: var(--ink); background: var(--surface); box-shadow: var(--shadow); }
242
+ .modal::backdrop { background: rgb(15 22 19 / 46%); backdrop-filter: blur(3px); }
243
+ .modal-header { min-height: 78px; padding: 18px 20px; border-bottom: 1px solid var(--line); display: flex; align-items: center; justify-content: space-between; gap: 18px; }
244
+ .modal-header h2 { font-size: 18px; }
245
+ .modal-body { padding: 22px 20px; }
246
+ .modal-footer { min-height: 70px; padding: 14px 20px; border-top: 1px solid var(--line); display: flex; align-items: center; justify-content: end; gap: 10px; }
247
+ .qr-stage { min-height: 390px; padding: 28px 20px 22px; display: grid; justify-items: center; align-content: center; }
248
+ .qr-frame { width: 300px; height: 300px; border: 1px solid var(--line); border-radius: var(--radius); background: white; display: grid; place-items: center; overflow: hidden; }
249
+ .qr-frame img { width: 100%; height: 100%; object-fit: contain; }
250
+ .qr-status { min-height: 20px; margin: 16px 0 0; color: var(--ink-soft); font-size: 12px; font-weight: 700; }
251
+ .qr-status.is-success { color: var(--green-dark); }
252
+ .qr-status.is-error { color: var(--red); }
253
+ .spinner { width: 30px; height: 30px; border: 3px solid var(--surface-muted); border-top-color: var(--green); border-radius: 50%; animation: spin .8s linear infinite; }
254
+ .form-stack { display: grid; gap: 18px; }
255
+ .toast-region { position: fixed; z-index: 80; right: 20px; bottom: 20px; width: min(360px, calc(100% - 40px)); display: grid; gap: 8px; pointer-events: none; }
256
+ .toast { min-height: 48px; padding: 13px 15px; border: 1px solid #bac4be; border-radius: var(--radius); color: white; background: #28312e; box-shadow: 0 10px 28px rgb(20 27 25 / 20%); font-size: 12px; font-weight: 650; animation: toast-in .2s ease-out; }
257
+ .toast.is-error { background: #8e2927; border-color: #b85551; }
258
+ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
259
+
260
+ @keyframes spin { to { transform: rotate(360deg); } }
261
+ @keyframes toast-in { from { opacity: 0; transform: translateY(7px); } }
262
+ @keyframes typing-dot { 0%, 60%, 100% { opacity: .35; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }
263
+
264
+ @media (max-width: 900px) {
265
+ .header-inner { grid-template-columns: 1fr auto; }
266
+ .view-tabs { order: 3; grid-column: 1 / -1; width: 100%; margin-bottom: 10px; }
267
+ .tab { flex: 1; }
268
+ .app-header { position: static; }
269
+ .metrics { grid-template-columns: 88px 88px minmax(180px, 1fr); gap: 18px; }
270
+ .account-main { grid-template-columns: minmax(220px, 1fr) 110px auto; }
271
+ .account-main .account-stat:first-of-type { display: none; }
272
+ .session-workbench { grid-template-columns: minmax(250px, 290px) minmax(0, 1fr); }
273
+ .chat-message { max-width: 88%; }
274
+ }
275
+
276
+ @media (max-width: 760px) {
277
+ .session-workbench { min-height: 0; grid-template-columns: 1fr; }
278
+ .session-sidebar { border-right: 0; border-bottom: 1px solid var(--line); }
279
+ .session-list { max-height: 310px; }
280
+ .chat-panel { min-height: 620px; }
281
+ }
282
+
283
+ @media (max-width: 640px) {
284
+ .header-inner, .status-inner, .view { width: min(calc(100% - 24px), var(--content)); }
285
+ .header-inner { min-height: 62px; gap: 12px; }
286
+ .brand-name { max-width: 178px; font-size: 13px; }
287
+ .brand-mark { width: 34px; height: 34px; flex-basis: 34px; }
288
+ .header-action { width: 38px; padding: 0; }
289
+ .header-action span { display: none; }
290
+ .status-inner { padding: 16px 0; align-items: start; display: grid; gap: 15px; }
291
+ .metrics { width: 100%; grid-template-columns: 1fr 1fr; }
292
+ .workspace-metric { grid-column: 1 / -1; max-width: none; }
293
+ .view { padding: 30px 0 56px; }
294
+ h1 { font-size: 25px; }
295
+ .section-heading { align-items: center; }
296
+ .account-main { padding: 16px; grid-template-columns: 1fr auto; gap: 16px; }
297
+ .account-main .account-stat { display: none; }
298
+ .account-actions { grid-column: 2; grid-row: 1; }
299
+ .account-detail, .pending-access { padding: 12px 16px; align-items: start; flex-direction: column; }
300
+ .authorization-state { grid-template-columns: auto minmax(0, 1fr); }
301
+ .authorization-action { width: 100%; grid-column: 1 / -1; }
302
+ .pending-access .button { width: 100%; }
303
+ .session-account-bar { padding: 10px; align-items: stretch; flex-direction: column; gap: 8px; }
304
+ .session-account-bar-label { padding-inline: 2px; }
305
+ .session-account-tabs { width: 100%; }
306
+ .chat-header { padding: 15px 16px; }
307
+ .chat-messages { padding: 22px 14px 26px; }
308
+ .chat-message { max-width: 92%; }
309
+ .chat-composer { padding-inline: 12px; }
310
+ .composer-controls { grid-template-columns: 38px minmax(0, 1fr) 70px; gap: 7px; }
311
+ .chat-attach { width: 38px; }
312
+ .chat-send { min-width: 70px; padding-inline: 10px; }
313
+ .composer-files { padding-left: 45px; }
314
+ .composer-hint { margin-left: 45px; }
315
+ .settings-group { grid-template-columns: 1fr; gap: 16px; }
316
+ .settings-group > label, .field-grid { grid-column: 1; }
317
+ .field-grid { grid-template-columns: 1fr; }
318
+ .qr-stage { min-height: 342px; padding-inline: 16px; }
319
+ .qr-frame { width: min(280px, calc(100vw - 72px)); height: min(280px, calc(100vw - 72px)); }
320
+ }
321
+
322
+ @media (prefers-reduced-motion: reduce) {
323
+ *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
324
+ }