@mooncompany/uplink-chat 0.5.0

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.

Potentially problematic release.


This version of @mooncompany/uplink-chat might be problematic. Click here for more details.

Files changed (158) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +185 -0
  3. package/bin/uplink.js +279 -0
  4. package/middleware/error-handler.js +69 -0
  5. package/package.json +93 -0
  6. package/public/css/agents.36b98c0f.css +1469 -0
  7. package/public/css/agents.css +1469 -0
  8. package/public/css/app.a6a7f8f5.css +2731 -0
  9. package/public/css/app.css +2731 -0
  10. package/public/css/artifacts.css +444 -0
  11. package/public/css/commands.css +55 -0
  12. package/public/css/connection.css +131 -0
  13. package/public/css/dashboard.css +233 -0
  14. package/public/css/developer.css +328 -0
  15. package/public/css/files.css +123 -0
  16. package/public/css/markdown.css +156 -0
  17. package/public/css/message-actions.css +278 -0
  18. package/public/css/mobile.css +614 -0
  19. package/public/css/panels-unified.css +483 -0
  20. package/public/css/premium.css +415 -0
  21. package/public/css/realtime.css +189 -0
  22. package/public/css/satellites.css +401 -0
  23. package/public/css/shortcuts.css +185 -0
  24. package/public/css/split-view.4def0262.css +673 -0
  25. package/public/css/split-view.css +673 -0
  26. package/public/css/theme-generator.css +391 -0
  27. package/public/css/themes.css +387 -0
  28. package/public/css/timestamps.css +54 -0
  29. package/public/css/variables.css +78 -0
  30. package/public/dist/bundle.b55050c4.js +15757 -0
  31. package/public/favicon.svg +24 -0
  32. package/public/img/agents/ada.png +0 -0
  33. package/public/img/agents/clarice.png +0 -0
  34. package/public/img/agents/dennis-nedry.png +0 -0
  35. package/public/img/agents/elliot-alderson.png +0 -0
  36. package/public/img/agents/main.png +0 -0
  37. package/public/img/agents/scotty.png +0 -0
  38. package/public/img/agents/top-flight-security.png +0 -0
  39. package/public/index.html +1083 -0
  40. package/public/js/agents-data.js +234 -0
  41. package/public/js/agents-ui.js +72 -0
  42. package/public/js/agents.js +1525 -0
  43. package/public/js/app.js +79 -0
  44. package/public/js/appearance-settings.js +111 -0
  45. package/public/js/artifacts.js +432 -0
  46. package/public/js/audio-queue.js +168 -0
  47. package/public/js/bootstrap.js +54 -0
  48. package/public/js/chat.js +1211 -0
  49. package/public/js/commands.js +581 -0
  50. package/public/js/connection-api.js +121 -0
  51. package/public/js/connection.js +1231 -0
  52. package/public/js/context-tracker.js +271 -0
  53. package/public/js/core.js +172 -0
  54. package/public/js/dashboard.js +452 -0
  55. package/public/js/developer.js +432 -0
  56. package/public/js/encryption.js +124 -0
  57. package/public/js/errors.js +122 -0
  58. package/public/js/event-bus.js +77 -0
  59. package/public/js/fetch-utils.js +171 -0
  60. package/public/js/file-handler.js +229 -0
  61. package/public/js/files.js +352 -0
  62. package/public/js/gateway-chat.js +538 -0
  63. package/public/js/logger.js +112 -0
  64. package/public/js/markdown.js +190 -0
  65. package/public/js/message-actions.js +431 -0
  66. package/public/js/message-renderer.js +288 -0
  67. package/public/js/missed-messages.js +235 -0
  68. package/public/js/mobile-debug.js +95 -0
  69. package/public/js/notifications.js +367 -0
  70. package/public/js/offline-queue.js +178 -0
  71. package/public/js/onboarding.js +543 -0
  72. package/public/js/panels.js +156 -0
  73. package/public/js/premium.js +412 -0
  74. package/public/js/realtime-voice.js +844 -0
  75. package/public/js/satellite-sync.js +256 -0
  76. package/public/js/satellite-ui.js +175 -0
  77. package/public/js/satellites.js +1516 -0
  78. package/public/js/settings.js +1087 -0
  79. package/public/js/shortcuts.js +381 -0
  80. package/public/js/split-chat.js +1234 -0
  81. package/public/js/split-resize.js +211 -0
  82. package/public/js/splitview.js +340 -0
  83. package/public/js/storage.js +408 -0
  84. package/public/js/streaming-handler.js +324 -0
  85. package/public/js/stt-settings.js +316 -0
  86. package/public/js/theme-generator.js +661 -0
  87. package/public/js/themes.js +164 -0
  88. package/public/js/timestamps.js +198 -0
  89. package/public/js/tts-settings.js +575 -0
  90. package/public/js/ui.js +267 -0
  91. package/public/js/update-notifier.js +143 -0
  92. package/public/js/utils/constants.js +165 -0
  93. package/public/js/utils/sanitize.js +93 -0
  94. package/public/js/utils/sse-parser.js +195 -0
  95. package/public/js/voice.js +883 -0
  96. package/public/manifest.json +58 -0
  97. package/public/moon_texture.jpg +0 -0
  98. package/public/sw.js +221 -0
  99. package/public/three.min.js +6 -0
  100. package/server/channel.js +529 -0
  101. package/server/chat.js +270 -0
  102. package/server/config-store.js +362 -0
  103. package/server/config.js +159 -0
  104. package/server/context.js +131 -0
  105. package/server/gateway-commands.js +211 -0
  106. package/server/gateway-proxy.js +318 -0
  107. package/server/index.js +22 -0
  108. package/server/logger.js +89 -0
  109. package/server/middleware/auth.js +188 -0
  110. package/server/middleware.js +218 -0
  111. package/server/openclaw-discover.js +308 -0
  112. package/server/premium/index.js +156 -0
  113. package/server/premium/license.js +140 -0
  114. package/server/realtime/bridge.js +837 -0
  115. package/server/realtime/index.js +349 -0
  116. package/server/realtime/tts-stream.js +446 -0
  117. package/server/routes/agents.js +564 -0
  118. package/server/routes/artifacts.js +174 -0
  119. package/server/routes/chat.js +311 -0
  120. package/server/routes/config-settings.js +345 -0
  121. package/server/routes/config.js +603 -0
  122. package/server/routes/files.js +307 -0
  123. package/server/routes/index.js +18 -0
  124. package/server/routes/media.js +451 -0
  125. package/server/routes/missed-messages.js +107 -0
  126. package/server/routes/premium.js +75 -0
  127. package/server/routes/push.js +156 -0
  128. package/server/routes/satellite.js +406 -0
  129. package/server/routes/status.js +251 -0
  130. package/server/routes/stt.js +35 -0
  131. package/server/routes/voice.js +260 -0
  132. package/server/routes/webhooks.js +203 -0
  133. package/server/routes.js +206 -0
  134. package/server/runtime-config.js +336 -0
  135. package/server/share.js +305 -0
  136. package/server/stt/faster-whisper.js +72 -0
  137. package/server/stt/groq.js +51 -0
  138. package/server/stt/index.js +196 -0
  139. package/server/stt/openai.js +49 -0
  140. package/server/sync.js +244 -0
  141. package/server/tailscale-https.js +175 -0
  142. package/server/tts.js +646 -0
  143. package/server/update-checker.js +172 -0
  144. package/server/utils/filename.js +129 -0
  145. package/server/utils.js +147 -0
  146. package/server/watchdog.js +318 -0
  147. package/server/websocket/broadcast.js +359 -0
  148. package/server/websocket/connections.js +339 -0
  149. package/server/websocket/index.js +215 -0
  150. package/server/websocket/routing.js +277 -0
  151. package/server/websocket/sync.js +102 -0
  152. package/server.js +404 -0
  153. package/utils/detect-tool-usage.js +93 -0
  154. package/utils/errors.js +158 -0
  155. package/utils/html-escape.js +84 -0
  156. package/utils/id-sanitize.js +94 -0
  157. package/utils/response.js +130 -0
  158. package/utils/with-retry.js +105 -0
@@ -0,0 +1,2731 @@
1
+ * { box-sizing: border-box; margin: 0; padding: 0; }
2
+
3
+ /* ============================================================================
4
+ TABLE OF CONTENTS (M-33: Added for navigation in 2.1k line file)
5
+ ============================================================================
6
+
7
+ 1. GLOBAL RESET & BASE STYLES
8
+ 2. CSS CUSTOM PROPERTIES (:root variables)
9
+ 3. ACCESSIBILITY UTILITIES (sr-only, skip-link, focus indicators)
10
+ 4. ONBOARDING & OVERLAY SCREENS (unlock, onboarding)
11
+ 5. FORM COMPONENTS (inputs, labels, errors, buttons)
12
+ 6. LAYOUT STRUCTURE (app, header, panels, side panel)
13
+ 7. HEADER & LOGO
14
+ 8. MESSAGE AREA & CHAT BUBBLES
15
+ 9. MESSAGE ACTIONS
16
+ 10. INPUT AREA (text input, voice input, file upload)
17
+ 11. MODE TABS (text/voice toggle)
18
+ 12. PANELS (settings, history, shortcuts, satellites, dashboard)
19
+ 13. BUTTONS & INTERACTIVE ELEMENTS
20
+ 14. ANIMATIONS & TRANSITIONS
21
+ 15. RESPONSIVE BREAKPOINTS (mobile, tablet, desktop)
22
+ 16. UTILITY CLASSES
23
+
24
+ ============================================================================ */
25
+
26
+ /* ============================================================================
27
+ 1. GLOBAL RESET & BASE STYLES
28
+ ============================================================================ */
29
+
30
+ /* Screen reader only - visually hidden but accessible */
31
+ /* ============================================================================
32
+ 3. ACCESSIBILITY UTILITIES
33
+ ============================================================================ */
34
+ .sr-only {
35
+ position: absolute;
36
+ width: 1px;
37
+ height: 1px;
38
+ padding: 0;
39
+ margin: -1px;
40
+ overflow: hidden;
41
+ clip: rect(0, 0, 0, 0);
42
+ white-space: nowrap;
43
+ border: 0;
44
+ }
45
+
46
+ :root {
47
+ /* Core colors */
48
+ --bg: #000;
49
+ --bg-secondary: rgba(10, 20, 40, 0.95);
50
+ --bg-input: rgba(0, 30, 60, 0.8);
51
+ --accent: #00f0ff;
52
+ --accent-hover: #00c8d8;
53
+ --accent-secondary: #8b5cf6;
54
+ --text: #f1f5f9;
55
+ --text-muted: #8acad8;
56
+ --text-dim: #8ab8c8; /* WCAG AA 4.5:1+ contrast against --bg */
57
+ --text-inverse: #000;
58
+ --border: var(--accent-15);
59
+
60
+ /* Opacity variants for common colors (M-08: extracted from inline rgba values) */
61
+ --black-05: rgba(0, 0, 0, 0.05);
62
+ --black-10: rgba(0, 0, 0, 0.1);
63
+ --black-20: rgba(0, 0, 0, 0.2);
64
+ --black-30: rgba(0, 0, 0, 0.3);
65
+ --black-40: rgba(0, 0, 0, 0.4);
66
+ --black-50: rgba(0, 0, 0, 0.5);
67
+ --black-80: rgba(0, 0, 0, 0.8);
68
+ --black-85: rgba(0, 0, 0, 0.85);
69
+
70
+ --white-05: rgba(255, 255, 255, 0.05);
71
+ --white-10: rgba(255, 255, 255, 0.1);
72
+ --white-20: rgba(255, 255, 255, 0.2);
73
+
74
+ --accent-05: rgba(0, 240, 255, 0.05);
75
+ --accent-10: rgba(0, 240, 255, 0.1);
76
+ --accent-15: rgba(0, 240, 255, 0.15);
77
+ --accent-20: rgba(0, 240, 255, 0.2);
78
+ --accent-30: rgba(0, 240, 255, 0.3);
79
+ --accent-40: rgba(0, 240, 255, 0.4);
80
+ --accent-50: rgba(0, 240, 255, 0.5);
81
+
82
+ --error-10: rgba(255, 68, 102, 0.1);
83
+ --error-20: rgba(255, 68, 102, 0.2);
84
+ --error-30: rgba(255, 68, 102, 0.3);
85
+
86
+ --success-20: rgba(0, 255, 136, 0.2);
87
+
88
+ /* User/Assistant bubbles */
89
+ --user-bubble: rgba(76, 29, 149, 0.4);
90
+ --user-accent: #4c1d95;
91
+ --user-accent-hover: #3b1577;
92
+ --assistant-bubble: rgba(0, 40, 60, 0.9);
93
+
94
+ /* Status colors */
95
+ --success: #4ade80;
96
+ --warning: #fbbf24;
97
+ --info: #60a5fa;
98
+ --recording: #ff3366;
99
+ --error: #ef4444;
100
+ --neutral: #6b7280;
101
+
102
+ /* Decorative */
103
+ --star-color: #fff;
104
+
105
+ /* Syntax highlighting */
106
+ --syntax-keyword: #c792ea;
107
+ --syntax-string: #c3e88d;
108
+ --syntax-comment: #546e7a;
109
+ --syntax-number: #f78c6c;
110
+
111
+ /* File type colors */
112
+ --file-js: #f7df1e;
113
+ --file-ts: #3178c6;
114
+ --file-py: #3776ab;
115
+ --file-json: #00ff88;
116
+ --file-html: #e34f26;
117
+ --file-css: #1572b6;
118
+ --file-md: #083fa1;
119
+
120
+ /* Spacing scale (4px base) */
121
+ --space-1: 4px;
122
+ --space-2: 8px;
123
+ --space-3: 12px;
124
+ --space-4: 16px;
125
+ --space-5: 20px;
126
+ --space-6: 24px;
127
+ --space-8: 32px;
128
+ --space-10: 40px;
129
+ --space-12: 48px;
130
+
131
+ /* Typography scale */
132
+ --text-xs: 0.75rem; /* 12px */
133
+ --text-sm: 0.875rem; /* 14px */
134
+ --text-base: 1rem; /* 16px */
135
+ --text-lg: 1.125rem; /* 18px */
136
+ --text-xl: 1.25rem; /* 20px */
137
+ --text-2xl: 1.5rem; /* 24px */
138
+ --text-3xl: 2rem; /* 32px */
139
+
140
+ /* Border radius scale */
141
+ --radius-sm: 4px;
142
+ --radius-md: 8px;
143
+ --radius-lg: 12px;
144
+ --radius-xl: 16px;
145
+ --radius-2xl: 24px;
146
+ --radius-full: 9999px;
147
+ }
148
+
149
+ /* Accessibility: Skip link */
150
+ .skip-link {
151
+ position: absolute;
152
+ top: -100px;
153
+ left: var(--space-4);
154
+ background: var(--accent);
155
+ color: var(--text-inverse);
156
+ padding: var(--space-3) var(--space-4);
157
+ border-radius: var(--radius-md);
158
+ z-index: var(--z-tooltip);
159
+ text-decoration: none;
160
+ font-weight: 600;
161
+ transition: top 0.2s;
162
+ }
163
+
164
+ .skip-link:focus {
165
+ top: var(--space-4);
166
+ }
167
+
168
+ /* Accessibility: Focus indicators */
169
+ :focus-visible {
170
+ outline: 2px solid var(--accent);
171
+ outline-offset: 2px;
172
+ }
173
+
174
+ button:focus-visible,
175
+ .header-btn:focus-visible,
176
+ .mode-tab:focus-visible,
177
+ .toggle:focus-visible {
178
+ outline: 2px solid var(--accent);
179
+ outline-offset: 2px;
180
+ border-radius: var(--radius-sm);
181
+ }
182
+
183
+ /* Touch optimization: Prevent double-tap zoom on interactive elements */
184
+ button,
185
+ a,
186
+ input,
187
+ textarea,
188
+ select,
189
+ .toggle,
190
+ .mode-tab,
191
+ .header-btn,
192
+ .send-btn,
193
+ .voice-btn,
194
+ .input-btn,
195
+ .setting-row,
196
+ [role="button"],
197
+ [role="tab"],
198
+ [role="switch"] {
199
+ touch-action: manipulation;
200
+ }
201
+
202
+ /* Onboarding & Unlock Screens */
203
+ /* ============================================================================
204
+ 4. ONBOARDING & OVERLAY SCREENS
205
+ ============================================================================ */
206
+ .overlay-screen {
207
+ position: fixed;
208
+ top: 0; left: 0; right: 0; bottom: 0;
209
+ background: var(--black-85);
210
+ z-index: var(--z-overlay);
211
+ display: none;
212
+ align-items: center;
213
+ justify-content: center;
214
+ padding: var(--space-5);
215
+ overflow-y: auto;
216
+ -webkit-overflow-scrolling: touch;
217
+ }
218
+
219
+ .overlay-screen.visible {
220
+ display: flex;
221
+ }
222
+
223
+ .overlay-card {
224
+ background: linear-gradient(180deg, var(--bg-secondary) 0%, var(--bg) 100%);
225
+ border: 1px solid var(--border);
226
+ border-radius: var(--radius-2xl);
227
+ padding: var(--space-8);
228
+ max-width: 400px;
229
+ width: 100%;
230
+ box-shadow: 0 20px 60px var(--black-50), 0 0 40px var(--accent-10);
231
+ }
232
+
233
+ .overlay-header {
234
+ text-align: center;
235
+ margin-bottom: 28px;
236
+ }
237
+
238
+ .overlay-logo {
239
+ font-size: var(--text-3xl);
240
+ margin-bottom: var(--space-3);
241
+ }
242
+
243
+ .overlay-title {
244
+ font-family: 'Space Mono', monospace;
245
+ font-size: var(--text-2xl);
246
+ font-weight: 400;
247
+ letter-spacing: 4px;
248
+ text-transform: uppercase;
249
+ color: var(--text);
250
+ margin-bottom: var(--space-2);
251
+ }
252
+
253
+ .overlay-subtitle {
254
+ color: var(--text-muted);
255
+ font-size: var(--text-base);
256
+ }
257
+
258
+ .form-group {
259
+ margin-bottom: var(--space-5);
260
+ }
261
+
262
+ .form-label {
263
+ display: block;
264
+ font-size: var(--text-sm);
265
+ color: var(--text-muted);
266
+ margin-bottom: var(--space-2);
267
+ letter-spacing: 0.5px;
268
+ }
269
+
270
+ .form-input {
271
+ width: 100%;
272
+ background: var(--bg-input);
273
+ border: 1px solid var(--border);
274
+ border-radius: var(--radius-lg);
275
+ padding: 14px var(--space-4);
276
+ color: var(--text);
277
+ font-size: var(--text-base);
278
+ outline: none;
279
+ transition: all 0.2s;
280
+ }
281
+
282
+ .form-input:focus {
283
+ border-color: var(--accent);
284
+ box-shadow: 0 0 20px var(--accent-15);
285
+ }
286
+
287
+ .form-input:focus-visible {
288
+ outline: 2px solid var(--accent);
289
+ outline-offset: 2px;
290
+ }
291
+
292
+ .form-input.error {
293
+ border-color: var(--error);
294
+ }
295
+
296
+ .form-input.success {
297
+ border-color: var(--success);
298
+ }
299
+
300
+ .form-hint {
301
+ font-size: var(--text-xs);
302
+ color: var(--text-dim);
303
+ margin-top: var(--space-2);
304
+ }
305
+
306
+ .form-error {
307
+ font-size: var(--text-sm);
308
+ color: var(--error);
309
+ margin-top: var(--space-2);
310
+ display: none;
311
+ }
312
+
313
+ .form-error.visible {
314
+ display: block;
315
+ }
316
+
317
+ .url-input-group {
318
+ position: relative;
319
+ }
320
+
321
+ .url-status {
322
+ position: absolute;
323
+ right: 14px;
324
+ top: 50%;
325
+ transform: translateY(-50%);
326
+ font-size: var(--text-lg);
327
+ }
328
+
329
+ .url-status.checking::after {
330
+ content: '\23F3';
331
+ animation: spin 1s linear infinite;
332
+ }
333
+
334
+ .url-status.valid::after {
335
+ content: '\2713';
336
+ color: var(--success);
337
+ }
338
+
339
+ .url-status.invalid::after {
340
+ content: '\2717';
341
+ color: var(--error);
342
+ }
343
+
344
+ @keyframes spin {
345
+ from { transform: translateY(-50%) rotate(0deg); }
346
+ to { transform: translateY(-50%) rotate(360deg); }
347
+ }
348
+
349
+ .encrypt-toggle-row {
350
+ display: flex;
351
+ align-items: center;
352
+ justify-content: space-between;
353
+ padding: var(--space-4) 0;
354
+ border-top: 1px solid var(--border);
355
+ margin-top: var(--space-2);
356
+ }
357
+
358
+ .encrypt-label {
359
+ font-size: var(--text-base);
360
+ }
361
+
362
+ .encrypt-desc {
363
+ font-size: var(--text-xs);
364
+ color: var(--text-dim);
365
+ margin-top: 2px;
366
+ }
367
+
368
+ .password-fields {
369
+ display: none;
370
+ margin-top: var(--space-4);
371
+ padding-top: var(--space-4);
372
+ border-top: 1px solid var(--border);
373
+ }
374
+
375
+ .password-fields.visible {
376
+ display: block;
377
+ }
378
+
379
+ /* ============================================================================
380
+ 5. FORM COMPONENTS
381
+ ============================================================================ */
382
+ .btn-primary {
383
+ width: 100%;
384
+ background: linear-gradient(135deg, var(--accent) 0%, var(--accent-hover) 100%);
385
+ border: none;
386
+ border-radius: var(--radius-lg);
387
+ padding: var(--space-4);
388
+ color: var(--text-inverse);
389
+ font-size: var(--text-base);
390
+ font-weight: 600;
391
+ cursor: pointer;
392
+ transition: all 0.2s;
393
+ margin-top: var(--space-2);
394
+ }
395
+
396
+ .btn-primary:hover {
397
+ transform: translateY(-1px);
398
+ box-shadow: 0 4px 20px var(--accent-30);
399
+ }
400
+
401
+ .btn-primary:disabled {
402
+ opacity: 0.5;
403
+ cursor: not-allowed;
404
+ transform: none;
405
+ }
406
+
407
+ .btn-secondary {
408
+ width: 100%;
409
+ background: transparent;
410
+ border: 1px solid var(--border);
411
+ border-radius: var(--radius-lg);
412
+ padding: 14px;
413
+ color: var(--text-muted);
414
+ font-size: var(--text-base);
415
+ cursor: pointer;
416
+ transition: all 0.2s;
417
+ margin-top: var(--space-3);
418
+ }
419
+
420
+ .btn-secondary:hover {
421
+ border-color: var(--text-muted);
422
+ color: var(--text);
423
+ }
424
+
425
+ .forgot-link {
426
+ display: block;
427
+ width: 100%;
428
+ text-align: center;
429
+ margin-top: var(--space-4);
430
+ padding: 0;
431
+ border: none;
432
+ background: none;
433
+ color: var(--text-dim);
434
+ font-size: var(--text-sm);
435
+ font-family: inherit;
436
+ cursor: pointer;
437
+ transition: color 0.2s;
438
+ }
439
+
440
+ .forgot-link:hover {
441
+ color: var(--text-muted);
442
+ }
443
+
444
+ /* Forgot password confirmation */
445
+ .forgot-confirm {
446
+ display: none;
447
+ text-align: center;
448
+ padding: var(--space-5);
449
+ background: var(--error-10);
450
+ border: 1px solid var(--error-30);
451
+ border-radius: var(--radius-lg);
452
+ margin-top: var(--space-4);
453
+ }
454
+
455
+ .forgot-confirm.visible {
456
+ display: block;
457
+ }
458
+
459
+ .forgot-confirm p {
460
+ font-size: var(--text-sm);
461
+ color: var(--text);
462
+ margin-bottom: var(--space-3);
463
+ }
464
+
465
+ .forgot-confirm .reassurance {
466
+ font-size: var(--text-sm);
467
+ color: var(--success);
468
+ margin-bottom: var(--space-4);
469
+ }
470
+
471
+ .forgot-buttons {
472
+ display: flex;
473
+ gap: var(--space-2);
474
+ }
475
+
476
+ .forgot-buttons button {
477
+ flex: 1;
478
+ padding: var(--space-2);
479
+ border-radius: var(--radius-md);
480
+ font-size: var(--text-sm);
481
+ cursor: pointer;
482
+ transition: all 0.2s;
483
+ }
484
+
485
+ .btn-danger {
486
+ background: var(--error);
487
+ border: none;
488
+ color: var(--text);
489
+ }
490
+
491
+ @media (hover: hover) {
492
+ .btn-danger:hover {
493
+ background: var(--recording);
494
+ }
495
+ }
496
+
497
+ .btn-cancel {
498
+ background: transparent;
499
+ border: 1px solid var(--border);
500
+ color: var(--text-muted);
501
+ }
502
+
503
+ @media (hover: hover) {
504
+ .btn-cancel:hover {
505
+ border-color: var(--text-muted);
506
+ }
507
+ }
508
+
509
+ html, body {
510
+ height: 100%;
511
+ overflow: hidden;
512
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
513
+ background: var(--bg);
514
+ color: var(--text);
515
+ }
516
+
517
+ /* Starfield background */
518
+ #stars {
519
+ position: fixed;
520
+ top: 0; left: 0; right: 0; bottom: 0;
521
+ pointer-events: none;
522
+ z-index: 0; /* Below everything - intentionally 0 */
523
+ background: var(--bg);
524
+ }
525
+
526
+ .star {
527
+ position: absolute;
528
+ background: var(--star-color);
529
+ border-radius: 50%;
530
+ animation: twinkle var(--duration) ease-in-out infinite;
531
+ }
532
+
533
+ @keyframes twinkle {
534
+ 0%, 100% { opacity: var(--opacity); }
535
+ 50% { opacity: 0.2; }
536
+ }
537
+
538
+ /* Accessibility: Respect reduced motion preference */
539
+ @media (prefers-reduced-motion: reduce) {
540
+ .star {
541
+ animation: none;
542
+ opacity: var(--opacity, 0.6);
543
+ }
544
+
545
+ * {
546
+ animation-duration: 0.01ms !important;
547
+ animation-iteration-count: 1 !important;
548
+ transition-duration: 0.01ms !important;
549
+ }
550
+ }
551
+
552
+ /* Layout wrapper for split view */
553
+
554
+ /* ============================================================================
555
+ 6. LAYOUT STRUCTURE
556
+ ============================================================================ */
557
+ .layout-wrapper {
558
+ display: flex;
559
+ height: 100%;
560
+ width: 100%;
561
+ position: relative;
562
+ }
563
+
564
+ /* Main layout */
565
+ .app {
566
+ display: flex;
567
+ flex-direction: column;
568
+ height: 100%;
569
+ max-width: 800px;
570
+ margin: 0 auto;
571
+ position: relative;
572
+ z-index: var(--z-dropdown);
573
+ transition: margin 0.3s ease, max-width 0.3s ease;
574
+ }
575
+
576
+ /* Side panel - hidden by default */
577
+ .side-panel {
578
+ display: none;
579
+ flex-direction: column;
580
+ height: 100%;
581
+ background: var(--bg);
582
+ border-left: 1px solid var(--border);
583
+ overflow: hidden;
584
+ }
585
+
586
+ .side-panel-header {
587
+ display: flex;
588
+ align-items: center;
589
+ justify-content: space-between;
590
+ padding: var(--space-2) var(--space-5);
591
+ border-bottom: 1px solid var(--border);
592
+ background: var(--bg);
593
+ flex-shrink: 0;
594
+ }
595
+
596
+ .side-panel-title {
597
+ font-family: 'Space Mono', monospace;
598
+ font-size: var(--text-sm);
599
+ color: var(--accent);
600
+ letter-spacing: 1px;
601
+ text-transform: uppercase;
602
+ }
603
+
604
+ .side-panel-close {
605
+ background: none;
606
+ border: none;
607
+ color: var(--text-muted);
608
+ font-size: var(--text-xl);
609
+ cursor: pointer;
610
+ min-width: 44px;
611
+ min-height: 44px;
612
+ display: flex;
613
+ align-items: center;
614
+ justify-content: center;
615
+ line-height: 1;
616
+ border-radius: var(--radius-md);
617
+ transition: color 0.15s;
618
+ }
619
+
620
+ .side-panel-close:hover {
621
+ color: var(--text);
622
+ }
623
+
624
+ .side-panel-content {
625
+ flex: 1;
626
+ overflow-y: auto;
627
+ padding: var(--space-4);
628
+ }
629
+
630
+ /* Header - clean, minimal, no fill */
631
+
632
+ /* ============================================================================
633
+ 7. HEADER & LOGO
634
+ ============================================================================ */
635
+ .header {
636
+ display: flex;
637
+ align-items: center;
638
+ justify-content: space-between;
639
+ padding: var(--space-4) var(--space-5);
640
+ padding-top: calc(var(--space-4) + env(safe-area-inset-top, 0px));
641
+ background: transparent;
642
+ position: relative;
643
+ z-index: var(--z-base);
644
+ }
645
+
646
+ .header-left {
647
+ display: flex;
648
+ align-items: center;
649
+ gap: var(--space-3);
650
+ min-width: 0;
651
+ overflow: hidden;
652
+ }
653
+
654
+ .logo-icon {
655
+ font-size: var(--text-xl);
656
+ line-height: 1;
657
+ }
658
+
659
+ .logo {
660
+ font-family: 'Space Mono', monospace;
661
+ font-size: var(--text-sm);
662
+ font-weight: 700;
663
+ letter-spacing: 2px;
664
+ text-transform: uppercase;
665
+ color: var(--accent);
666
+ }
667
+
668
+ .status-badge {
669
+ display: flex;
670
+ align-items: center;
671
+ gap: var(--space-2);
672
+ font-family: 'Space Mono', monospace;
673
+ font-size: var(--text-xs);
674
+ letter-spacing: 1px;
675
+ color: var(--text-dim);
676
+ text-transform: uppercase;
677
+ }
678
+
679
+ .encryption-badge {
680
+ color: var(--success);
681
+ }
682
+
683
+ .encryption-badge.warning {
684
+ color: var(--error);
685
+ }
686
+
687
+ .header-right {
688
+ display: flex;
689
+ gap: var(--space-2);
690
+ flex-shrink: 0;
691
+ }
692
+
693
+ /* Header center — mode toggle (desktop only) */
694
+ .header-center {
695
+ display: none; /* Hidden on mobile — bottom tabs used instead */
696
+ }
697
+
698
+ .header-mode-btn {
699
+ background: none;
700
+ border: none;
701
+ color: var(--text-dim);
702
+ padding: var(--space-2);
703
+ min-width: 40px;
704
+ min-height: 40px;
705
+ display: flex;
706
+ align-items: center;
707
+ justify-content: center;
708
+ cursor: pointer;
709
+ border-radius: var(--radius-md);
710
+ transition: all 0.2s;
711
+ }
712
+
713
+ .header-mode-btn:hover {
714
+ color: var(--text);
715
+ background: var(--accent-10);
716
+ }
717
+
718
+ .header-mode-btn.active {
719
+ color: var(--accent);
720
+ background: var(--accent-10);
721
+ }
722
+
723
+ .header-mode-btn:focus-visible {
724
+ outline: 2px solid var(--accent);
725
+ outline-offset: 2px;
726
+ }
727
+
728
+ .header-btn {
729
+ background: none;
730
+ border: none;
731
+ color: var(--text-dim);
732
+ padding: var(--space-2);
733
+ min-width: 44px;
734
+ min-height: 44px;
735
+ display: flex;
736
+ align-items: center;
737
+ justify-content: center;
738
+ cursor: pointer;
739
+ transition: all 0.2s;
740
+ border-radius: var(--radius-md);
741
+ }
742
+
743
+ .header-btn:hover {
744
+ color: var(--accent);
745
+ background: var(--accent-10);
746
+ transform: scale(1.05);
747
+ }
748
+
749
+ .header-btn.active {
750
+ color: var(--accent);
751
+ }
752
+
753
+ /* Messages area */
754
+
755
+ /* ============================================================================
756
+ 8. MESSAGE AREA & CHAT BUBBLES
757
+ ============================================================================ */
758
+ .messages {
759
+ flex: 1;
760
+ overflow-y: auto;
761
+ overflow-x: hidden; /* Prevent horizontal scroll */
762
+ padding: var(--space-4);
763
+ padding-bottom: 160px; /* Fallback — JS dynamically adjusts via ui.js updateMessagesPadding */
764
+ display: flex;
765
+ flex-direction: column;
766
+ gap: var(--space-2); /* Tighter message spacing */
767
+ }
768
+
769
+ /* Spacer pushes messages to bottom when few, without breaking scroll */
770
+ .messages::before {
771
+ content: '';
772
+ flex: 1;
773
+ }
774
+
775
+ .messages::-webkit-scrollbar { width: 8px; }
776
+ .messages::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.1); }
777
+ .messages::-webkit-scrollbar-thumb {
778
+ background: var(--accent-30);
779
+ border-radius: 4px;
780
+ }
781
+ .messages::-webkit-scrollbar-thumb:hover {
782
+ background: var(--accent-50);
783
+ }
784
+
785
+ .message {
786
+ max-width: 85%;
787
+ padding: var(--space-2) var(--space-3);
788
+ border-radius: var(--radius-xl);
789
+ line-height: 1.45;
790
+ font-size: inherit;
791
+ font-family: 'Nunito Sans', -apple-system, BlinkMacSystemFont, sans-serif;
792
+ word-wrap: break-word;
793
+ }
794
+
795
+ #messages.text-small .message { font-size: var(--text-sm); }
796
+ #messages.text-medium .message { font-size: var(--text-base); }
797
+ #messages.text-large .message { font-size: var(--text-lg); }
798
+
799
+ .message.user {
800
+ align-self: flex-end;
801
+ background: linear-gradient(135deg, var(--user-bubble), var(--user-accent-hover));
802
+ border: 1px solid var(--accent-30);
803
+ color: var(--text);
804
+ border-bottom-right-radius: var(--radius-sm);
805
+ }
806
+
807
+ .message.assistant {
808
+ align-self: flex-start;
809
+ background: var(--assistant-bubble);
810
+ border: 1px solid var(--accent-10);
811
+ border-bottom-left-radius: var(--radius-sm);
812
+ }
813
+
814
+ .message.assistant::before {
815
+ content: '';
816
+ font-size: var(--text-sm);
817
+ }
818
+
819
+ /* Agent avatar styles moved to top-level below .messages block */
820
+
821
+ /* Queued message indicator */
822
+ .message.queued {
823
+ opacity: 0.7;
824
+ border-style: dashed;
825
+ }
826
+ .message.queued::after {
827
+ content: '⏳';
828
+ position: absolute;
829
+ top: var(--space-1);
830
+ right: var(--space-2);
831
+ font-size: var(--text-xs);
832
+ }
833
+
834
+ .message img:not(.agent-avatar-img) {
835
+ max-width: 100%;
836
+ border-radius: var(--radius-md);
837
+ margin-top: var(--space-2);
838
+ /* Hide broken images that fail to load (reduces visual flash before onerror) */
839
+ min-height: 0;
840
+ }
841
+
842
+ /* Suppress broken image icon in browsers */
843
+ .message img:not(.agent-avatar-img)::before {
844
+ content: none;
845
+ }
846
+
847
+ .message-image-expired {
848
+ padding: var(--space-3) var(--space-4);
849
+ background: var(--bg-input);
850
+ border: 1px dashed var(--border);
851
+ border-radius: var(--radius-md);
852
+ color: var(--text-dim);
853
+ font-size: var(--text-sm);
854
+ margin-top: var(--space-2);
855
+ }
856
+
857
+ .message a {
858
+ color: var(--accent);
859
+ text-decoration: underline;
860
+ text-decoration-style: dotted;
861
+ text-underline-offset: 2px;
862
+ }
863
+
864
+ .message a:hover {
865
+ text-decoration-style: solid;
866
+ }
867
+
868
+ /* Streaming message indicator - rainbow glow */
869
+ .message.streaming {
870
+ position: relative;
871
+ animation: streamingRainbow 4s linear infinite;
872
+ }
873
+
874
+ @keyframes streamingRainbow {
875
+ 0% { border-color: #ff4466; box-shadow: 0 0 8px rgba(255,68,102,0.4), 0 0 20px rgba(255,68,102,0.15); }
876
+ 16% { border-color: #ff9f43; box-shadow: 0 0 8px rgba(255,159,67,0.4), 0 0 20px rgba(255,159,67,0.15); }
877
+ 33% { border-color: #ffd32a; box-shadow: 0 0 8px rgba(255,211,42,0.4), 0 0 20px rgba(255,211,42,0.15); }
878
+ 50% { border-color: #4ade80; box-shadow: 0 0 8px rgba(74,222,128,0.4), 0 0 20px rgba(74,222,128,0.15); }
879
+ 66% { border-color: #00b4d8; box-shadow: 0 0 8px rgba(0,180,216,0.4), 0 0 20px rgba(0,180,216,0.15); }
880
+ 83% { border-color: #c084fc; box-shadow: 0 0 8px rgba(192,132,252,0.4), 0 0 20px rgba(192,132,252,0.15); }
881
+ 100% { border-color: #ff4466; box-shadow: 0 0 8px rgba(255,68,102,0.4), 0 0 20px rgba(255,68,102,0.15); }
882
+ }
883
+
884
+ /* streamPulse dot removed — rainbow border is sufficient */
885
+
886
+ /* Stop generation button */
887
+ .stop-generation-btn {
888
+ display: flex;
889
+ align-items: center;
890
+ justify-content: center;
891
+ gap: var(--space-2);
892
+ margin: 0 auto var(--space-2);
893
+ padding: var(--space-2) var(--space-4);
894
+ background: var(--error-20);
895
+ border: 1px solid var(--error);
896
+ border-radius: var(--radius-2xl);
897
+ color: var(--error);
898
+ font-size: var(--text-sm);
899
+ cursor: pointer;
900
+ transition: all 0.2s;
901
+ animation: stopBtnAppear 0.2s ease-out;
902
+ }
903
+
904
+ .stop-generation-btn:hover {
905
+ background: var(--error-30);
906
+ transform: scale(1.02);
907
+ }
908
+
909
+ @keyframes stopBtnAppear {
910
+ from { opacity: 0; transform: translateY(10px); }
911
+ to { opacity: 1; transform: translateY(0); }
912
+ }
913
+
914
+ .message.system {
915
+ align-self: center;
916
+ background: none;
917
+ color: var(--text-dim);
918
+ font-size: var(--text-sm);
919
+ padding: var(--space-1) 0;
920
+ }
921
+ /* Message wrapper for timestamp positioning below bubble */
922
+ .message-wrapper {
923
+ display: flex;
924
+ flex-direction: column;
925
+ align-items: flex-start;
926
+ max-width: 85%;
927
+ }
928
+
929
+ .message-wrapper.user {
930
+ align-self: flex-end;
931
+ align-items: flex-end;
932
+ }
933
+
934
+ .message-wrapper.assistant {
935
+ align-self: flex-start;
936
+ align-items: flex-start;
937
+ }
938
+
939
+ .message-wrapper .message {
940
+ align-self: auto;
941
+ max-width: 100%;
942
+ }
943
+
944
+ /* Timestamp styles consolidated in timestamps.css */
945
+
946
+
947
+ /* Background task indicator */
948
+ .message.background-task {
949
+ background: linear-gradient(135deg, var(--accent) 0%, var(--accent-secondary) 100%);
950
+ color: var(--text);
951
+ border-radius: var(--radius-md);
952
+ padding: var(--space-2) var(--space-3);
953
+ margin: var(--space-2) 0;
954
+ font-size: var(--text-sm);
955
+ display: flex;
956
+ align-items: center;
957
+ gap: var(--space-2);
958
+ }
959
+
960
+ .message.background-task::before {
961
+ content: '???';
962
+ animation: pulse 2s infinite;
963
+ }
964
+
965
+ .message.background-task.completed {
966
+ background: linear-gradient(135deg, var(--success) 0%, #059669 100%);
967
+ }
968
+
969
+ .message.background-task.completed::before {
970
+ content: '?';
971
+ animation: none;
972
+ }
973
+
974
+ @keyframes pulse {
975
+ 0%, 100% { opacity: 1; }
976
+ 50% { opacity: 0.5; }
977
+ }
978
+
979
+ /* Typing indicator */
980
+ .typing {
981
+ align-self: flex-start;
982
+ background: var(--assistant-bubble);
983
+ padding: var(--space-3) var(--space-4);
984
+ border-radius: var(--radius-xl);
985
+ border-bottom-left-radius: var(--radius-sm);
986
+ display: flex;
987
+ gap: var(--space-1);
988
+ }
989
+
990
+ .typing span {
991
+ width: var(--space-2);
992
+ height: var(--space-2);
993
+ background: var(--text-muted);
994
+ border-radius: var(--radius-full);
995
+ animation: bounce 1.4s infinite ease-in-out;
996
+ }
997
+
998
+ .typing span:nth-child(1) { animation-delay: 0s; }
999
+ .typing span:nth-child(2) { animation-delay: 0.2s; }
1000
+ .typing span:nth-child(3) { animation-delay: 0.4s; }
1001
+
1002
+ /* Thinking state - shows text instead of dots */
1003
+ .typing .thinking-text,
1004
+ .typing .tool-text {
1005
+ color: var(--text-muted);
1006
+ font-size: var(--text-sm);
1007
+ white-space: nowrap;
1008
+ }
1009
+
1010
+ /* Legacy CSS class approach (kept for compatibility) */
1011
+ .typing.thinking span:not(.thinking-text) { display: none; }
1012
+ .typing.using-tool span:not(.tool-text) { display: none; }
1013
+
1014
+ @keyframes bounce {
1015
+ 0%, 80%, 100% { transform: translateY(0); }
1016
+ 40% { transform: translateY(-6px); }
1017
+ }
1018
+
1019
+ /* Empty state */
1020
+ .empty-state {
1021
+ flex: 1;
1022
+ display: flex;
1023
+ flex-direction: column;
1024
+ align-items: center;
1025
+ justify-content: center;
1026
+ color: var(--text-dim);
1027
+ text-align: center;
1028
+ padding: var(--space-10);
1029
+ }
1030
+
1031
+ .empty-state svg {
1032
+ width: 64px;
1033
+ height: 64px;
1034
+ margin-bottom: var(--space-4);
1035
+ opacity: 0.5;
1036
+ }
1037
+
1038
+ .empty-state h3 {
1039
+ font-size: var(--text-lg);
1040
+ margin-bottom: var(--space-2);
1041
+ color: var(--text-muted);
1042
+ }
1043
+
1044
+ .empty-state p {
1045
+ font-size: var(--text-base);
1046
+ }
1047
+
1048
+ /* Mode toggle tabs - at bottom */
1049
+
1050
+ /* Agent avatar — small circle next to timestamp on assistant bubbles */
1051
+ .agent-avatar {
1052
+ display: none;
1053
+ }
1054
+
1055
+ .message.assistant .agent-avatar {
1056
+ display: inline-block;
1057
+ width: 28px;
1058
+ height: 28px;
1059
+ min-width: 28px;
1060
+ border-radius: 50%;
1061
+ overflow: hidden;
1062
+ vertical-align: middle;
1063
+ margin-right: 6px;
1064
+ margin-bottom: 2px;
1065
+ }
1066
+
1067
+ .agent-avatar img,
1068
+ .agent-avatar-img {
1069
+ width: 28px !important;
1070
+ height: 28px !important;
1071
+ max-width: 28px !important;
1072
+ max-height: 28px !important;
1073
+ object-fit: cover;
1074
+ border-radius: 50%;
1075
+ display: block;
1076
+ margin: 0 !important;
1077
+ }
1078
+
1079
+ /* ============================================================================
1080
+ 11. MODE TABS
1081
+ ============================================================================ */
1082
+ .mode-tabs {
1083
+ display: flex;
1084
+ background: var(--bg);
1085
+ border-top: 1px solid var(--border);
1086
+ padding-bottom: env(safe-area-inset-bottom);
1087
+ margin-top: 0;
1088
+ position: relative;
1089
+ }
1090
+
1091
+ .mode-tab {
1092
+ flex: 1;
1093
+ padding: 14px var(--space-3);
1094
+ background: none;
1095
+ border: none;
1096
+ color: var(--text-muted);
1097
+ font-size: var(--text-sm);
1098
+ font-weight: 500;
1099
+ cursor: pointer;
1100
+ transition: all 0.2s ease;
1101
+ display: flex;
1102
+ align-items: center;
1103
+ justify-content: center;
1104
+ gap: var(--space-2);
1105
+ position: relative;
1106
+ z-index: 1;
1107
+ }
1108
+
1109
+ /* Inactive tab underline */
1110
+ .mode-tab::after {
1111
+ content: '';
1112
+ position: absolute;
1113
+ bottom: 0;
1114
+ left: 50%;
1115
+ transform: translateX(-50%);
1116
+ width: 0;
1117
+ height: 2px;
1118
+ background: var(--accent);
1119
+ border-radius: var(--radius-full);
1120
+ transition: width 0.2s ease, box-shadow 0.2s ease;
1121
+ }
1122
+
1123
+ .mode-tab:hover {
1124
+ color: var(--text);
1125
+ background: var(--accent-05);
1126
+ }
1127
+
1128
+ .mode-tab.active {
1129
+ color: var(--accent);
1130
+ background: var(--accent-10);
1131
+ font-weight: 600;
1132
+ }
1133
+
1134
+ /* Active tab underline indicator */
1135
+ .mode-tab.active::after {
1136
+ width: 60%;
1137
+ box-shadow: 0 0 8px var(--accent);
1138
+ }
1139
+
1140
+ .mode-tab.active svg {
1141
+ filter: drop-shadow(0 0 4px var(--accent));
1142
+ }
1143
+
1144
+ /* Icon styling for tabs */
1145
+ .mode-tab svg {
1146
+ transition: transform 0.2s ease, filter 0.2s ease;
1147
+ }
1148
+
1149
+ .mode-tab:hover svg {
1150
+ transform: scale(1.1);
1151
+ }
1152
+
1153
+ .mode-tab.active svg {
1154
+ transform: scale(1.15);
1155
+ }
1156
+
1157
+ .mode-tab.has-error {
1158
+ animation: tab-error-pulse 0.5s ease;
1159
+ color: var(--error);
1160
+ }
1161
+
1162
+ .mode-tab.has-tool {
1163
+ animation: tab-tool-pulse 0.3s ease;
1164
+ color: var(--success);
1165
+ }
1166
+
1167
+ @keyframes tab-error-pulse {
1168
+ 0%, 100% { background: transparent; }
1169
+ 50% { background: rgba(239, 68, 68, 0.2); }
1170
+ }
1171
+
1172
+ @keyframes tab-tool-pulse {
1173
+ 0%, 100% { background: transparent; }
1174
+ 50% { background: rgba(16, 185, 129, 0.2); }
1175
+ }
1176
+
1177
+ /* Activity button indicators */
1178
+ .header-btn.has-error {
1179
+ animation: btn-error-pulse 0.5s ease;
1180
+ color: var(--error);
1181
+ }
1182
+
1183
+ .header-btn.has-tool {
1184
+ animation: btn-tool-pulse 0.3s ease;
1185
+ color: var(--success);
1186
+ }
1187
+
1188
+ @keyframes btn-error-pulse {
1189
+ 0%, 100% { background: transparent; }
1190
+ 50% { background: rgba(239, 68, 68, 0.3); }
1191
+ }
1192
+
1193
+ @keyframes btn-tool-pulse {
1194
+ 0%, 100% { background: transparent; }
1195
+ 50% { background: rgba(16, 185, 129, 0.3); }
1196
+ }
1197
+
1198
+ /* Input area */
1199
+
1200
+ /* ============================================================================
1201
+ 10. INPUT AREA
1202
+ ============================================================================ */
1203
+ .input-area {
1204
+ padding: var(--space-3) var(--space-4) var(--space-3);
1205
+ background: transparent;
1206
+ border-top: 1px solid var(--border);
1207
+ margin-bottom: 0;
1208
+ }
1209
+
1210
+ /* Image preview */
1211
+ .image-preview {
1212
+ display: none;
1213
+ position: relative;
1214
+ margin-bottom: var(--space-3);
1215
+ width: fit-content;
1216
+ max-width: 100%;
1217
+ background: transparent;
1218
+ min-height: 0;
1219
+ overflow: visible;
1220
+ }
1221
+
1222
+ .image-preview.visible {
1223
+ display: inline-block;
1224
+ }
1225
+
1226
+ .image-preview img {
1227
+ display: block;
1228
+ max-height: 120px;
1229
+ max-width: 200px;
1230
+ border-radius: var(--radius-lg);
1231
+ border: 1px solid var(--border);
1232
+ object-fit: cover;
1233
+ }
1234
+
1235
+ .preview-remove {
1236
+ position: absolute;
1237
+ top: -8px;
1238
+ right: -8px;
1239
+ width: 32px;
1240
+ height: 32px;
1241
+ border-radius: var(--radius-full);
1242
+ background: var(--recording);
1243
+ color: white;
1244
+ border: none;
1245
+ cursor: pointer;
1246
+ font-size: var(--text-sm);
1247
+ display: flex;
1248
+ align-items: center;
1249
+ justify-content: center;
1250
+ }
1251
+
1252
+ .file-label {
1253
+ display: none;
1254
+ padding: var(--space-3) var(--space-4);
1255
+ background: var(--bg-input);
1256
+ border: 1px solid var(--border);
1257
+ border-radius: var(--radius-lg);
1258
+ font-size: var(--text-sm);
1259
+ color: var(--text);
1260
+ max-width: 200px;
1261
+ overflow: hidden;
1262
+ text-overflow: ellipsis;
1263
+ white-space: nowrap;
1264
+ }
1265
+
1266
+ /* Text input */
1267
+ .text-input-row {
1268
+ display: none;
1269
+ gap: var(--space-2);
1270
+ align-items: flex-end;
1271
+ }
1272
+
1273
+ .text-input-row.active {
1274
+ display: flex;
1275
+ }
1276
+
1277
+ .input-actions {
1278
+ display: flex;
1279
+ gap: var(--space-1);
1280
+ }
1281
+
1282
+ .input-btn {
1283
+ width: 44px;
1284
+ height: 44px;
1285
+ min-width: 44px;
1286
+ min-height: 44px;
1287
+ border-radius: var(--radius-full);
1288
+ border: none;
1289
+ background: var(--bg-input);
1290
+ color: var(--text-muted);
1291
+ cursor: pointer;
1292
+ display: flex;
1293
+ align-items: center;
1294
+ justify-content: center;
1295
+ transition: all 0.15s;
1296
+ }
1297
+
1298
+ .input-btn:hover {
1299
+ background: var(--border);
1300
+ color: var(--text);
1301
+ }
1302
+
1303
+ .input-btn svg {
1304
+ width: var(--space-5);
1305
+ height: var(--space-5);
1306
+ }
1307
+
1308
+ .text-field {
1309
+ flex: 1;
1310
+ background: var(--bg-input);
1311
+ border: 1px solid var(--border);
1312
+ border-radius: var(--radius-2xl);
1313
+ padding: var(--space-2) var(--space-4);
1314
+ color: var(--text);
1315
+ font-size: var(--text-base);
1316
+ outline: none;
1317
+ transition: border-color 0.15s;
1318
+ font-family: 'Nunito Sans', -apple-system, BlinkMacSystemFont, sans-serif;
1319
+ resize: none;
1320
+ min-height: var(--space-10);
1321
+ max-height: 150px;
1322
+ overflow-y: auto;
1323
+ line-height: 1.4;
1324
+ }
1325
+
1326
+ .text-field:focus {
1327
+ border-color: var(--accent);
1328
+ box-shadow: 0 0 15px var(--accent-20);
1329
+ }
1330
+
1331
+ .text-field:focus-visible {
1332
+ outline: 2px solid var(--accent);
1333
+ outline-offset: 2px;
1334
+ }
1335
+
1336
+ .text-field::placeholder {
1337
+ color: var(--text-dim);
1338
+ }
1339
+
1340
+ .send-btn {
1341
+ width: 44px;
1342
+ height: 44px;
1343
+ min-width: 44px;
1344
+ min-height: 44px;
1345
+ border-radius: var(--radius-full);
1346
+ border: none;
1347
+ background: var(--accent);
1348
+ color: var(--text-inverse, #fff);
1349
+ cursor: pointer;
1350
+ display: flex;
1351
+ align-items: center;
1352
+ justify-content: center;
1353
+ transition: all 0.15s;
1354
+ box-shadow: 0 0 20px var(--accent-30);
1355
+ }
1356
+
1357
+ .send-btn:hover {
1358
+ background: var(--accent-hover);
1359
+ box-shadow: 0 0 30px var(--accent-40);
1360
+ }
1361
+
1362
+ .send-btn:disabled {
1363
+ opacity: 0.4;
1364
+ cursor: not-allowed;
1365
+ }
1366
+
1367
+ .send-btn svg {
1368
+ width: 18px;
1369
+ height: 18px;
1370
+ }
1371
+
1372
+ /* Voice input */
1373
+ .voice-input-row {
1374
+ display: none;
1375
+ flex-direction: column;
1376
+ align-items: center;
1377
+ gap: var(--space-4);
1378
+ padding: var(--space-5) 0;
1379
+ }
1380
+
1381
+ .voice-input-row.active {
1382
+ display: flex;
1383
+ }
1384
+
1385
+ /* Voice orb container with rings */
1386
+ .voice-orb-container {
1387
+ position: relative;
1388
+ display: flex;
1389
+ align-items: center;
1390
+ justify-content: center;
1391
+ width: 120px;
1392
+ height: 120px;
1393
+ }
1394
+
1395
+ .voice-ring {
1396
+ position: absolute;
1397
+ border-radius: var(--radius-full);
1398
+ border: 1px solid var(--white-10);
1399
+ }
1400
+
1401
+ .voice-ring::before {
1402
+ content: '';
1403
+ position: absolute;
1404
+ width: var(--space-1);
1405
+ height: var(--space-1);
1406
+ background: var(--accent);
1407
+ border-radius: var(--radius-full);
1408
+ top: -2px;
1409
+ left: 50%;
1410
+ transform: translateX(-50%);
1411
+ box-shadow: 0 0 8px var(--accent);
1412
+ }
1413
+
1414
+ .voice-ring-1 {
1415
+ width: 100px;
1416
+ height: 100px;
1417
+ animation: orbit 20s linear infinite;
1418
+ }
1419
+
1420
+ .voice-ring-2 {
1421
+ width: 120px;
1422
+ height: 120px;
1423
+ animation: orbit 30s linear infinite reverse;
1424
+ }
1425
+
1426
+ @keyframes orbit {
1427
+ from { transform: rotate(0deg); }
1428
+ to { transform: rotate(360deg); }
1429
+ }
1430
+
1431
+ .voice-btn {
1432
+ width: 80px;
1433
+ height: 80px;
1434
+ border-radius: var(--radius-full);
1435
+ border: 2px solid rgba(180, 180, 200, 0.3);
1436
+ background: transparent;
1437
+ cursor: pointer;
1438
+ display: flex;
1439
+ align-items: center;
1440
+ justify-content: center;
1441
+ transition: all 0.2s;
1442
+ position: relative;
1443
+ z-index: 2;
1444
+ box-shadow: 0 0 20px rgba(200,200,220,0.2);
1445
+ overflow: hidden;
1446
+ padding: 0;
1447
+ }
1448
+
1449
+ .voice-btn canvas {
1450
+ width: 100%;
1451
+ height: 100%;
1452
+ border-radius: var(--radius-full);
1453
+ }
1454
+
1455
+ .voice-btn:hover {
1456
+ border-color: var(--accent);
1457
+ color: var(--accent);
1458
+ }
1459
+
1460
+ .voice-btn.recording {
1461
+ border-color: var(--recording);
1462
+ box-shadow: 0 0 30px rgba(255,80,100,0.5), 0 0 60px rgba(255,80,100,0.3);
1463
+ animation: pulse-record 1s infinite;
1464
+ }
1465
+
1466
+ .voice-btn.realtime-active {
1467
+ animation: voiceRainbow 4s linear infinite;
1468
+ }
1469
+
1470
+ .voice-btn.realtime-active.mic-muted {
1471
+ animation: voiceRainbow 4s linear infinite;
1472
+ opacity: 0.6;
1473
+ }
1474
+
1475
+ @keyframes voiceRainbow {
1476
+ 0% { border-color: #ff4466; box-shadow: 0 0 20px rgba(255,68,102,0.5), 0 0 50px rgba(255,68,102,0.2); }
1477
+ 16% { border-color: #ff9f43; box-shadow: 0 0 20px rgba(255,159,67,0.5), 0 0 50px rgba(255,159,67,0.2); }
1478
+ 33% { border-color: #ffd32a; box-shadow: 0 0 20px rgba(255,211,42,0.5), 0 0 50px rgba(255,211,42,0.2); }
1479
+ 50% { border-color: #4ade80; box-shadow: 0 0 20px rgba(74,222,128,0.5), 0 0 50px rgba(74,222,128,0.2); }
1480
+ 66% { border-color: #00b4d8; box-shadow: 0 0 20px rgba(0,180,216,0.5), 0 0 50px rgba(0,180,216,0.2); }
1481
+ 83% { border-color: #a78bfa; box-shadow: 0 0 20px rgba(167,139,250,0.5), 0 0 50px rgba(167,139,250,0.2); }
1482
+ 100% { border-color: #ff4466; box-shadow: 0 0 20px rgba(255,68,102,0.5), 0 0 50px rgba(255,68,102,0.2); }
1483
+ }
1484
+
1485
+ .voice-btn.auto-hold {
1486
+ box-shadow: 0 0 25px rgba(200,200,220,0.5), 0 0 50px rgba(200,200,220,0.25);
1487
+ }
1488
+
1489
+ .voice-btn.auto-hold.recording {
1490
+ box-shadow: 0 0 30px rgba(255,80,100,0.6), 0 0 60px rgba(255,80,100,0.3);
1491
+ }
1492
+
1493
+ .voice-orb-container:has(.voice-btn.recording) .voice-ring {
1494
+ border-color: rgba(255, 51, 102, 0.2);
1495
+ }
1496
+
1497
+ .voice-orb-container:has(.voice-btn.recording) .voice-ring::before {
1498
+ background: var(--recording);
1499
+ box-shadow: 0 0 8px var(--recording);
1500
+ }
1501
+
1502
+ .voice-btn.processing {
1503
+ border-color: var(--accent);
1504
+ color: var(--accent);
1505
+ animation: processing-spin 2s linear infinite;
1506
+ }
1507
+
1508
+ @keyframes pulse-record {
1509
+ 0%, 100% { transform: scale(1); }
1510
+ 50% { transform: scale(1.05); }
1511
+ }
1512
+
1513
+ @keyframes processing-spin {
1514
+ from { filter: hue-rotate(0deg); }
1515
+ to { filter: hue-rotate(360deg); }
1516
+ }
1517
+
1518
+ .voice-status {
1519
+ font-size: var(--text-sm);
1520
+ color: var(--text-muted);
1521
+ }
1522
+
1523
+ .voice-status.recording {
1524
+ color: var(--recording);
1525
+ }
1526
+
1527
+ .voice-timer {
1528
+ font-family: monospace;
1529
+ font-size: var(--text-lg);
1530
+ color: var(--recording);
1531
+ display: none;
1532
+ }
1533
+
1534
+ .voice-timer.visible {
1535
+ display: block;
1536
+ }
1537
+
1538
+ /* Hidden file input */
1539
+ input[type="file"] {
1540
+ display: none;
1541
+ }
1542
+
1543
+ /* Panel backdrop overlay */
1544
+
1545
+ /* ============================================================================
1546
+ 12. PANELS
1547
+ ============================================================================ */
1548
+ .panel-backdrop {
1549
+ position: fixed;
1550
+ top: 0;
1551
+ left: 0;
1552
+ right: 0;
1553
+ bottom: 0;
1554
+ background: var(--black-50);
1555
+ z-index: calc(var(--z-dropdown) - 10);
1556
+ opacity: 0;
1557
+ pointer-events: none;
1558
+ transition: opacity 0.2s ease;
1559
+ }
1560
+
1561
+ .panel-backdrop.visible {
1562
+ opacity: 1;
1563
+ pointer-events: auto;
1564
+ }
1565
+
1566
+ /* ============================================
1567
+ UNIFIED PANEL SYSTEM
1568
+ All panels (Settings, Activity, Satellites)
1569
+ share these base styles for consistency
1570
+ ============================================ */
1571
+
1572
+ /* Base panel styles - positioned by split-view on desktop, mobile.css handles < 1024px */
1573
+ .panel {
1574
+ background: var(--bg);
1575
+ overflow: hidden;
1576
+ display: none;
1577
+ flex-direction: column;
1578
+ width: 100%;
1579
+ height: 100%;
1580
+ }
1581
+
1582
+ .panel.visible {
1583
+ display: flex;
1584
+ }
1585
+
1586
+ .panel-header {
1587
+ display: flex;
1588
+ align-items: center;
1589
+ justify-content: space-between;
1590
+ padding: var(--space-4) var(--space-5);
1591
+ border-bottom: 1px solid var(--border);
1592
+ background: var(--bg);
1593
+ position: sticky;
1594
+ top: 0;
1595
+ z-index: 1;
1596
+ flex-shrink: 0;
1597
+ }
1598
+
1599
+ .panel-title {
1600
+ font-size: var(--text-base);
1601
+ font-weight: 600;
1602
+ color: var(--text);
1603
+ }
1604
+
1605
+ .panel-close {
1606
+ min-width: 44px;
1607
+ min-height: 44px;
1608
+ display: flex;
1609
+ align-items: center;
1610
+ justify-content: center;
1611
+ background: none;
1612
+ border: none;
1613
+ color: var(--text-muted);
1614
+ font-size: var(--text-xl);
1615
+ cursor: pointer;
1616
+ border-radius: var(--radius-md);
1617
+ transition: color 0.2s, background 0.2s;
1618
+ }
1619
+
1620
+ .panel-close:hover {
1621
+ color: var(--text);
1622
+ background: var(--white-10);
1623
+ }
1624
+
1625
+ .panel-content {
1626
+ flex: 1;
1627
+ overflow-y: auto;
1628
+ padding: var(--space-4) var(--space-5);
1629
+ }
1630
+
1631
+ .panel-row {
1632
+ display: flex;
1633
+ align-items: center;
1634
+ justify-content: space-between;
1635
+ gap: var(--space-4);
1636
+ padding: var(--space-4) 0;
1637
+ border-bottom: 1px solid var(--border);
1638
+ }
1639
+
1640
+ .panel-row:last-child {
1641
+ border-bottom: none;
1642
+ }
1643
+
1644
+ .panel-row-info {
1645
+ flex: 1;
1646
+ }
1647
+
1648
+ .panel-row-label {
1649
+ font-size: var(--text-base);
1650
+ font-weight: 500;
1651
+ color: var(--text);
1652
+ }
1653
+
1654
+ .panel-row-desc {
1655
+ font-size: var(--text-sm);
1656
+ color: var(--text-muted);
1657
+ margin-top: var(--space-1);
1658
+ }
1659
+
1660
+ /* Settings panel — uses unified .panel for positioning/layout.
1661
+ Only settings-specific visual overrides below. */
1662
+ .settings-panel-header {
1663
+ display: flex;
1664
+ align-items: center;
1665
+ justify-content: space-between;
1666
+ padding: var(--space-2) var(--space-5);
1667
+ border-bottom: 1px solid var(--border);
1668
+ background: var(--bg);
1669
+ position: sticky;
1670
+ top: 0;
1671
+ z-index: 1;
1672
+ flex-shrink: 0;
1673
+ }
1674
+
1675
+ .settings-panel-title {
1676
+ font-family: 'Space Mono', monospace;
1677
+ font-size: var(--text-sm);
1678
+ color: var(--accent);
1679
+ letter-spacing: 1px;
1680
+ text-transform: uppercase;
1681
+ }
1682
+
1683
+ .settings-panel-close {
1684
+ background: none;
1685
+ border: none;
1686
+ color: var(--text-muted);
1687
+ font-size: var(--text-xl);
1688
+ cursor: pointer;
1689
+ min-width: 44px;
1690
+ min-height: 44px;
1691
+ display: flex;
1692
+ align-items: center;
1693
+ justify-content: center;
1694
+ line-height: 1;
1695
+ border-radius: var(--radius-md);
1696
+ transition: color 0.2s, background 0.2s;
1697
+ }
1698
+
1699
+ .settings-panel-close:hover {
1700
+ color: var(--text);
1701
+ background: var(--white-10);
1702
+ }
1703
+
1704
+ .settings-panel-content {
1705
+ flex: 1;
1706
+ overflow-y: auto;
1707
+ overflow-x: hidden;
1708
+ padding: var(--space-4) var(--space-5);
1709
+ }
1710
+
1711
+ /* Settings sections - collapsible groups */
1712
+ .settings-section {
1713
+ border-bottom: 1px solid var(--border);
1714
+ }
1715
+
1716
+ .settings-section:last-child {
1717
+ border-bottom: none;
1718
+ }
1719
+
1720
+ .settings-section-header {
1721
+ display: flex;
1722
+ align-items: center;
1723
+ gap: var(--space-3);
1724
+ width: 100%;
1725
+ padding: var(--space-3) var(--space-1);
1726
+ background: none;
1727
+ border: none;
1728
+ color: var(--text);
1729
+ cursor: pointer;
1730
+ font-size: var(--text-sm);
1731
+ font-weight: 600;
1732
+ letter-spacing: 0.3px;
1733
+ transition: color 0.15s;
1734
+ }
1735
+
1736
+ .settings-section-header:hover {
1737
+ color: var(--accent);
1738
+ }
1739
+
1740
+ .settings-section-header:focus-visible {
1741
+ outline: 2px solid var(--accent);
1742
+ outline-offset: 2px;
1743
+ border-radius: var(--radius-sm);
1744
+ }
1745
+
1746
+ .settings-section-icon {
1747
+ flex-shrink: 0;
1748
+ width: 20px;
1749
+ display: flex;
1750
+ align-items: center;
1751
+ justify-content: center;
1752
+ color: var(--accent);
1753
+ }
1754
+
1755
+ .settings-section-icon svg {
1756
+ width: 18px;
1757
+ height: 18px;
1758
+ }
1759
+
1760
+ .settings-section-title {
1761
+ flex: 1;
1762
+ text-align: left;
1763
+ }
1764
+
1765
+ .settings-section-chevron {
1766
+ display: flex;
1767
+ align-items: center;
1768
+ justify-content: center;
1769
+ color: var(--text-dim);
1770
+ transition: transform 0.2s ease;
1771
+ flex-shrink: 0;
1772
+ }
1773
+
1774
+ .settings-section-chevron svg {
1775
+ width: 16px;
1776
+ height: 16px;
1777
+ }
1778
+
1779
+ .settings-section-header[aria-expanded="true"] .settings-section-chevron {
1780
+ transform: rotate(90deg);
1781
+ }
1782
+
1783
+ .settings-section-body {
1784
+ overflow: hidden;
1785
+ transition: max-height 0.25s ease, opacity 0.2s ease;
1786
+ max-height: 1200px;
1787
+ opacity: 1;
1788
+ }
1789
+
1790
+ .settings-section-body.collapsed {
1791
+ max-height: 0;
1792
+ opacity: 0;
1793
+ pointer-events: none;
1794
+ }
1795
+
1796
+ .settings-section-body .setting-row:first-child {
1797
+ padding-top: var(--space-2);
1798
+ }
1799
+
1800
+ /* Danger zone section highlight */
1801
+ .settings-section-danger .settings-section-icon {
1802
+ color: var(--warning);
1803
+ }
1804
+
1805
+ .settings-section-danger .settings-section-header {
1806
+ color: var(--text-dim);
1807
+ }
1808
+
1809
+ .settings-section-danger .settings-section-header:hover {
1810
+ color: var(--error);
1811
+ }
1812
+
1813
+ .settings-section-danger .settings-section-header:hover .settings-section-icon {
1814
+ color: var(--error);
1815
+ }
1816
+
1817
+ /* Status indicator dot (Edge TTS, Piper, Gateway, etc.) */
1818
+ .status-indicator {
1819
+ width: 10px;
1820
+ height: 10px;
1821
+ border-radius: var(--radius-full);
1822
+ background: var(--text-dim);
1823
+ flex-shrink: 0;
1824
+ }
1825
+
1826
+ .status-indicator.connected {
1827
+ background: var(--success);
1828
+ }
1829
+
1830
+ .status-indicator.disconnected {
1831
+ background: var(--error);
1832
+ }
1833
+
1834
+ /* TTS Provider config sub-panels */
1835
+ .tts-provider-config {
1836
+ border-top: 1px solid var(--border);
1837
+ padding-top: var(--space-1);
1838
+ margin-top: var(--space-1);
1839
+ }
1840
+
1841
+ .tts-provider-config .setting-row:last-child {
1842
+ border-bottom: none;
1843
+ }
1844
+
1845
+ /* Setting rows - align with unified panel-row styles */
1846
+ .setting-row {
1847
+ display: flex;
1848
+ align-items: center;
1849
+ justify-content: space-between;
1850
+ gap: var(--space-4);
1851
+ padding: var(--space-4) 0;
1852
+ border-bottom: 1px solid var(--border);
1853
+ min-width: 0;
1854
+ }
1855
+
1856
+ .setting-row > div:first-child {
1857
+ min-width: 0;
1858
+ flex-shrink: 1;
1859
+ }
1860
+
1861
+ .setting-row:last-child {
1862
+ border-bottom: none;
1863
+ }
1864
+
1865
+ .setting-label {
1866
+ font-size: var(--text-base);
1867
+ }
1868
+
1869
+ .setting-desc {
1870
+ font-size: var(--text-xs);
1871
+ color: var(--text-dim);
1872
+ margin-top: 2px;
1873
+ }
1874
+
1875
+ .setting-input {
1876
+ background: var(--bg-input);
1877
+ border: 1px solid var(--border);
1878
+ border-radius: var(--radius-md);
1879
+ padding: var(--space-2) var(--space-3);
1880
+ color: var(--text);
1881
+ font-size: var(--text-sm);
1882
+ width: 120px;
1883
+ outline: none;
1884
+ }
1885
+
1886
+ .setting-input:focus {
1887
+ border-color: var(--accent);
1888
+ }
1889
+
1890
+ .setting-input:focus-visible {
1891
+ outline: 2px solid var(--accent);
1892
+ outline-offset: -2px;
1893
+ }
1894
+
1895
+ .setting-select {
1896
+ background: var(--bg-input);
1897
+ border: 1px solid var(--border);
1898
+ border-radius: var(--radius-md);
1899
+ padding: var(--space-2) var(--space-3);
1900
+ color: var(--text);
1901
+ font-size: var(--text-sm);
1902
+ outline: none;
1903
+ cursor: pointer;
1904
+ flex-shrink: 0;
1905
+ }
1906
+
1907
+ .setting-select:focus-visible {
1908
+ outline: 2px solid var(--accent);
1909
+ outline-offset: -2px;
1910
+ }
1911
+
1912
+ /* Setting buttons - unified styles */
1913
+ .setting-btn {
1914
+ display: inline-flex;
1915
+ align-items: center;
1916
+ justify-content: center;
1917
+ padding: var(--space-2) var(--space-4);
1918
+ border-radius: 8px;
1919
+ font-size: var(--text-sm);
1920
+ font-weight: 500;
1921
+ cursor: pointer;
1922
+ border: none;
1923
+ transition: background 0.15s, opacity 0.15s, box-shadow 0.15s, transform 0.1s;
1924
+ white-space: nowrap;
1925
+ line-height: 1.4;
1926
+ flex-shrink: 0;
1927
+ min-height: 44px;
1928
+ }
1929
+
1930
+ .setting-btn:focus-visible {
1931
+ outline: 2px solid var(--accent);
1932
+ outline-offset: 2px;
1933
+ }
1934
+
1935
+ .setting-btn:active {
1936
+ transform: scale(0.97);
1937
+ }
1938
+
1939
+ /* Primary - accent background, white text (Save, Push) */
1940
+ .setting-btn-primary {
1941
+ background: var(--accent);
1942
+ color: white;
1943
+ }
1944
+
1945
+ .setting-btn-primary:hover {
1946
+ opacity: 0.85;
1947
+ }
1948
+
1949
+ .setting-btn-primary:active {
1950
+ opacity: 0.75;
1951
+ }
1952
+
1953
+ /* Secondary - subtle background, border, muted text (Clear, Change, View, Pull) */
1954
+ .setting-btn-secondary {
1955
+ background: var(--bg-input);
1956
+ border: 1px solid var(--border);
1957
+ color: var(--text-muted);
1958
+ }
1959
+
1960
+ .setting-btn-secondary:hover {
1961
+ background: var(--white-10);
1962
+ color: var(--text);
1963
+ }
1964
+
1965
+ .setting-btn-secondary:active {
1966
+ background: var(--white-5);
1967
+ }
1968
+
1969
+ /* Danger - red background, white text (Logout) */
1970
+ .setting-btn-danger {
1971
+ background: var(--error);
1972
+ color: white;
1973
+ font-weight: 500;
1974
+ }
1975
+
1976
+ .setting-btn-danger:hover {
1977
+ opacity: 0.85;
1978
+ }
1979
+
1980
+ .setting-btn-danger:active {
1981
+ opacity: 0.75;
1982
+ }
1983
+
1984
+ /* Setting utility classes */
1985
+ .setting-input-wide {
1986
+ max-width: 180px;
1987
+ width: 100%;
1988
+ }
1989
+
1990
+ .setting-full-width {
1991
+ flex: 1;
1992
+ }
1993
+
1994
+ .setting-input-group {
1995
+ display: flex;
1996
+ gap: 8px;
1997
+ margin-top: 8px;
1998
+ min-width: 0;
1999
+ max-width: 100%;
2000
+ }
2001
+
2002
+ .setting-input-mono {
2003
+ flex: 1;
2004
+ min-width: 0;
2005
+ font-family: monospace;
2006
+ }
2007
+
2008
+ .setting-label-danger {
2009
+ color: var(--error);
2010
+ }
2011
+
2012
+ .setting-label-accent {
2013
+ color: var(--accent);
2014
+ }
2015
+
2016
+ /* Conditional rows — hidden by default, shown via JS inline style override */
2017
+ .setting-hidden {
2018
+ display: none;
2019
+ }
2020
+
2021
+ /* Toggle switch */
2022
+ .toggle {
2023
+ width: var(--space-12);
2024
+ height: 26px;
2025
+ background: var(--border);
2026
+ border-radius: var(--radius-xl);
2027
+ position: relative;
2028
+ cursor: pointer;
2029
+ transition: background 0.2s;
2030
+ flex-shrink: 0;
2031
+ }
2032
+
2033
+ .toggle.on {
2034
+ background: var(--accent);
2035
+ }
2036
+
2037
+ .toggle::after {
2038
+ content: '';
2039
+ position: absolute;
2040
+ width: 22px;
2041
+ height: 22px;
2042
+ background: white;
2043
+ border-radius: var(--radius-full);
2044
+ top: 2px;
2045
+ left: 2px;
2046
+ transition: transform 0.2s;
2047
+ }
2048
+
2049
+ .toggle.on::after {
2050
+ transform: translateX(22px);
2051
+ }
2052
+
2053
+ .toggle.disabled {
2054
+ opacity: 0.5;
2055
+ cursor: not-allowed;
2056
+ }
2057
+
2058
+ /* Desktop adjustments */
2059
+ @media (min-width: 640px) {
2060
+ .app {
2061
+ border-left: 1px solid var(--border);
2062
+ border-right: 1px solid var(--border);
2063
+ }
2064
+
2065
+ .message {
2066
+ max-width: 70%;
2067
+ }
2068
+
2069
+ .voice-btn {
2070
+ width: 80px;
2071
+ height: 80px;
2072
+ }
2073
+ }
2074
+
2075
+ /* Tablet - tabs at bottom, no gap, constrained to app width */
2076
+ /* Breakpoint: 769px - 1023px (between --breakpoint-md and --breakpoint-lg) */
2077
+ @media (min-width: 769px) and (max-width: 1023px) {
2078
+
2079
+ /* ============================================================================
2080
+ 11. MODE TABS
2081
+ ============================================================================ */
2082
+ .mode-tabs {
2083
+ position: fixed;
2084
+ bottom: 0;
2085
+ left: 50%;
2086
+ transform: translateX(-50%);
2087
+ width: 100%;
2088
+ max-width: 800px;
2089
+ background: var(--bg);
2090
+ border-top: 1px solid var(--border);
2091
+ padding: 0;
2092
+ z-index: var(--z-dropdown);
2093
+ }
2094
+
2095
+ .mode-tab {
2096
+ padding: var(--space-2) var(--space-3);
2097
+ font-size: var(--text-base);
2098
+ }
2099
+
2100
+
2101
+ /* ============================================================================
2102
+ 10. INPUT AREA
2103
+ ============================================================================ */
2104
+ .input-area {
2105
+ position: fixed !important;
2106
+ bottom: 41px;
2107
+ left: 50%;
2108
+ transform: translateX(-50%);
2109
+ width: 100%;
2110
+ max-width: 800px;
2111
+ padding: var(--space-3);
2112
+ background: var(--bg);
2113
+ border-top: none;
2114
+ z-index: calc(var(--z-dropdown) + 1);
2115
+ }
2116
+ }
2117
+
2118
+ /* Wide desktop - expand chat area */
2119
+ /* Breakpoint: 1024px (--breakpoint-lg) */
2120
+ @media (min-width: 1024px) {
2121
+ .app {
2122
+ max-width: 900px;
2123
+ }
2124
+
2125
+ .message {
2126
+ max-width: 75%;
2127
+ }
2128
+
2129
+
2130
+ /* ============================================================================
2131
+ 7. HEADER & LOGO
2132
+ ============================================================================ */
2133
+ .header {
2134
+ padding: var(--space-2) var(--space-5);
2135
+ border-bottom: 1px solid var(--border);
2136
+ }
2137
+
2138
+ /* Show header center mode toggle on desktop */
2139
+ .header-center {
2140
+ display: flex;
2141
+ gap: var(--space-1);
2142
+ align-items: center;
2143
+ }
2144
+
2145
+ /* Hide bottom mode tabs on desktop — header toggle replaces them */
2146
+
2147
+ /* ============================================================================
2148
+ 11. MODE TABS
2149
+ ============================================================================ */
2150
+ .mode-tabs {
2151
+ display: none;
2152
+ }
2153
+
2154
+ /* Desktop split view - 70/30 layout when panel is open */
2155
+ body.panel-open
2156
+ /* ============================================================================
2157
+ 6. LAYOUT STRUCTURE
2158
+ ============================================================================ */
2159
+ .layout-wrapper {
2160
+ display: flex;
2161
+ }
2162
+
2163
+ body.panel-open .app {
2164
+ max-width: 70%;
2165
+ margin: 0;
2166
+ margin-right: 30%;
2167
+ flex-shrink: 0;
2168
+ }
2169
+
2170
+ body.panel-open .side-panel {
2171
+ display: flex;
2172
+ width: 30%;
2173
+ min-width: 320px;
2174
+ }
2175
+
2176
+ /* Override panel positioning when in side panel */
2177
+ .side-panel-content .settings-panel,
2178
+ .side-panel-content .dev-panel,
2179
+ .side-panel-content .satellite-navigator {
2180
+ position: static !important;
2181
+ display: flex !important;
2182
+ flex-direction: column;
2183
+ width: 100% !important;
2184
+ height: 100% !important;
2185
+ max-width: none !important;
2186
+ max-height: none !important;
2187
+ top: auto !important;
2188
+ right: auto !important;
2189
+ left: auto !important;
2190
+ bottom: auto !important;
2191
+ border-radius: 0 !important;
2192
+ box-shadow: none !important;
2193
+ background: transparent !important;
2194
+ border: none !important;
2195
+ padding: 0 !important;
2196
+ }
2197
+
2198
+ /* Hide the panel's own header in split view - use side panel header */
2199
+ .side-panel-content .settings-panel-header,
2200
+ .side-panel-content .dev-panel-header {
2201
+ display: none !important;
2202
+ }
2203
+
2204
+ /* Satellite close button is handled via JS (routes to SplitView on desktop) */
2205
+
2206
+ }
2207
+
2208
+ /* Ultra-wide desktop */
2209
+ /* Breakpoint: 1440px (--breakpoint-xl) */
2210
+ @media (min-width: 1440px) {
2211
+ .app {
2212
+ max-width: 1100px;
2213
+ }
2214
+
2215
+ .message {
2216
+ max-width: 70%;
2217
+ }
2218
+ }
2219
+
2220
+ /* Super-wide desktop (1920px+) */
2221
+ /* Breakpoint: 1920px (--breakpoint-2xl) */
2222
+ @media (min-width: 1920px) {
2223
+ .app {
2224
+ max-width: 1400px;
2225
+ }
2226
+
2227
+ .message {
2228
+ max-width: 65%;
2229
+ }
2230
+ }
2231
+
2232
+ /* Scroll indicators for panels */
2233
+ .settings-panel-content,
2234
+ .dev-panel-content,
2235
+ .satellite-list,
2236
+ .shortcuts-list {
2237
+ /* Fade out at edges to indicate more content */
2238
+ mask-image: linear-gradient(to bottom,
2239
+ transparent 0,
2240
+ black var(--space-2),
2241
+ black calc(100% - var(--space-2)),
2242
+ transparent 100%
2243
+ );
2244
+ -webkit-mask-image: linear-gradient(to bottom,
2245
+ transparent 0,
2246
+ black var(--space-2),
2247
+ black calc(100% - var(--space-2)),
2248
+ transparent 100%
2249
+ );
2250
+ }
2251
+
2252
+ /* ============================================
2253
+ SPLIT VIEW - Desktop panel layout (70/30)
2254
+ ============================================ */
2255
+
2256
+
2257
+ /* ============================================================================
2258
+ 6. LAYOUT STRUCTURE
2259
+ ============================================================================ */
2260
+ .layout-wrapper {
2261
+ display: flex;
2262
+ width: 100%;
2263
+ height: 100%;
2264
+ position: relative;
2265
+ }
2266
+
2267
+ .layout-wrapper .app {
2268
+ flex: 1;
2269
+ transition: max-width 0.3s ease, margin-right 0.3s ease;
2270
+ }
2271
+
2272
+ .side-panel {
2273
+ position: fixed;
2274
+ top: 0;
2275
+ right: 0;
2276
+ width: 0;
2277
+ height: 100%;
2278
+ background: var(--bg);
2279
+ border-left: 1px solid var(--border);
2280
+ display: flex;
2281
+ flex-direction: column;
2282
+ overflow: hidden;
2283
+ transition: width 0.3s ease;
2284
+ z-index: var(--z-dropdown);
2285
+ }
2286
+
2287
+ /* Desktop: 70/30 split when panel is open */
2288
+ @media (min-width: 1024px) {
2289
+ body.panel-open .app {
2290
+ max-width: 70%;
2291
+ margin-right: 30%;
2292
+ }
2293
+
2294
+ body.panel-open .side-panel {
2295
+ width: 30%;
2296
+ display: flex;
2297
+ }
2298
+ }
2299
+
2300
+ /* Hide side panel completely on mobile - use overlay panels instead */
2301
+ @media (max-width: 1023px) {
2302
+ .side-panel {
2303
+ display: none !important;
2304
+ }
2305
+ }
2306
+
2307
+ .side-panel-header {
2308
+ display: flex;
2309
+ justify-content: space-between;
2310
+ align-items: center;
2311
+ padding: var(--space-2) var(--space-5);
2312
+ border-bottom: 1px solid var(--border);
2313
+ background: var(--bg);
2314
+ flex-shrink: 0;
2315
+ }
2316
+
2317
+ .side-panel-header h3,
2318
+ #sidePanelTitle {
2319
+ margin: 0;
2320
+ font-family: 'Space Mono', monospace;
2321
+ font-size: var(--text-sm);
2322
+ color: var(--accent);
2323
+ letter-spacing: 1px;
2324
+ text-transform: uppercase;
2325
+ }
2326
+
2327
+ .side-panel-close {
2328
+ background: none;
2329
+ border: none;
2330
+ color: var(--text-muted);
2331
+ font-size: var(--text-xl);
2332
+ cursor: pointer;
2333
+ min-width: 44px;
2334
+ min-height: 44px;
2335
+ display: flex;
2336
+ align-items: center;
2337
+ justify-content: center;
2338
+ line-height: 1;
2339
+ border-radius: var(--radius-md);
2340
+ transition: color 0.2s;
2341
+ }
2342
+
2343
+ .side-panel-close:hover {
2344
+ color: var(--text-primary, #fff);
2345
+ }
2346
+
2347
+ .side-panel-content {
2348
+ flex: 1;
2349
+ overflow-y: auto;
2350
+ padding: var(--space-4, 16px);
2351
+ }
2352
+
2353
+ /* Override panel styles when inside side-panel */
2354
+ .side-panel-content .settings-panel,
2355
+ .side-panel-content .dev-panel,
2356
+ .side-panel-content .satellite-navigator {
2357
+ position: static !important;
2358
+ width: 100% !important;
2359
+ height: auto !important;
2360
+ max-height: none !important;
2361
+ background: transparent !important;
2362
+ border: none !important;
2363
+ box-shadow: none !important;
2364
+ border-radius: 0 !important;
2365
+ }
2366
+
2367
+ .side-panel-content .settings-panel-header,
2368
+ .side-panel-content .dev-panel-header {
2369
+ display: none !important; /* Use side-panel header instead */
2370
+ }
2371
+
2372
+ /* Button active state */
2373
+ .header-btn.active {
2374
+ background: var(--accent);
2375
+ color: var(--text-inverse);
2376
+ }
2377
+
2378
+ /* ============================================
2379
+ SEND-IN-PROGRESS INDICATOR
2380
+ ============================================ */
2381
+
2382
+ .send-spinner {
2383
+ display: inline-block;
2384
+ width: 16px;
2385
+ height: 16px;
2386
+ border: 2px solid rgba(255, 255, 255, 0.3);
2387
+ border-top-color: #fff;
2388
+ border-radius: 50%;
2389
+ animation: spin 0.8s linear infinite;
2390
+ }
2391
+
2392
+ @keyframes spin {
2393
+ to { transform: rotate(360deg); }
2394
+ }
2395
+
2396
+ #sendBtn.sending {
2397
+ /* No longer dim — button stays interactive for stop/queue */
2398
+ }
2399
+
2400
+ /* Stop icon state on send button */
2401
+ #sendBtn.send-stop {
2402
+ background: var(--error, #ef4444);
2403
+ box-shadow: 0 0 20px rgba(239, 68, 68, 0.3);
2404
+ }
2405
+
2406
+ #sendBtn.send-stop:hover {
2407
+ background: var(--error-hover, #dc2626);
2408
+ box-shadow: 0 0 30px rgba(239, 68, 68, 0.4);
2409
+ }
2410
+
2411
+ #sendBtn.send-stop svg {
2412
+ width: 14px;
2413
+ height: 14px;
2414
+ }
2415
+
2416
+ #sendBtn:disabled {
2417
+ pointer-events: none;
2418
+ }
2419
+
2420
+ #textInput:disabled {
2421
+ opacity: 0.5;
2422
+ cursor: not-allowed;
2423
+ }
2424
+
2425
+ /* ============================================
2426
+ SCROLL TO BOTTOM BUTTON
2427
+ ============================================ */
2428
+
2429
+ .scroll-to-bottom-btn {
2430
+ position: absolute;
2431
+ bottom: 80px;
2432
+ left: 50%;
2433
+ transform: translateX(-50%);
2434
+ background: var(--accent, #00f0ff);
2435
+ color: var(--text-inverse, #000);
2436
+ border: none;
2437
+ border-radius: 20px;
2438
+ padding: 8px 16px;
2439
+ font-size: 13px;
2440
+ font-weight: 500;
2441
+ cursor: pointer;
2442
+ z-index: 100;
2443
+ box-shadow: 0 2px 12px var(--accent-40);
2444
+ animation: fadeInUp 0.2s ease;
2445
+ display: flex;
2446
+ align-items: center;
2447
+ gap: 6px;
2448
+ transition: transform 0.2s, box-shadow 0.2s;
2449
+ }
2450
+
2451
+ .scroll-to-bottom-btn:hover {
2452
+ transform: translateX(-50%) scale(1.05);
2453
+ box-shadow: 0 4px 16px var(--accent-50);
2454
+ }
2455
+
2456
+ .scroll-to-bottom-btn:active {
2457
+ transform: translateX(-50%) scale(0.98);
2458
+ }
2459
+
2460
+ @keyframes fadeInUp {
2461
+ from {
2462
+ opacity: 0;
2463
+ transform: translateX(-50%) translateY(10px);
2464
+ }
2465
+ to {
2466
+ opacity: 1;
2467
+ transform: translateX(-50%) translateY(0);
2468
+ }
2469
+ }
2470
+
2471
+ @keyframes fadeIn {
2472
+ from { opacity: 0; }
2473
+ to { opacity: 1; }
2474
+ }
2475
+
2476
+ /* ============================================
2477
+ AUDIO PLAYER BAR
2478
+ Fixed bottom bar for TTS playback
2479
+ ============================================ */
2480
+ .audio-player-bar {
2481
+ position: fixed;
2482
+ bottom: 0;
2483
+ left: 0;
2484
+ right: 0;
2485
+ height: 40px;
2486
+ background: var(--bg-secondary, #1a1a2e);
2487
+ border-top: 1px solid var(--border-primary, #333);
2488
+ display: flex;
2489
+ align-items: center;
2490
+ gap: 10px;
2491
+ padding: 0 12px;
2492
+ z-index: var(--z-toast, 9000);
2493
+ animation: slideUp 0.2s ease-out;
2494
+ }
2495
+
2496
+ .audio-player-bar audio { display: none; }
2497
+
2498
+ @keyframes slideUp {
2499
+ from { transform: translateY(100%); }
2500
+ to { transform: translateY(0); }
2501
+ }
2502
+
2503
+ .audio-player-play {
2504
+ background: none;
2505
+ border: none;
2506
+ color: var(--text-primary, #eee);
2507
+ cursor: pointer;
2508
+ padding: 4px;
2509
+ display: flex;
2510
+ align-items: center;
2511
+ border-radius: 50%;
2512
+ transition: background 0.15s;
2513
+ }
2514
+ .audio-player-play:hover { background: var(--bg-hover, rgba(255,255,255,0.1)); }
2515
+
2516
+ .audio-player-progress {
2517
+ flex: 1;
2518
+ height: 4px;
2519
+ background: var(--bg-tertiary, #333);
2520
+ border-radius: 2px;
2521
+ cursor: pointer;
2522
+ position: relative;
2523
+ overflow: hidden;
2524
+ }
2525
+
2526
+ .audio-player-bar-fill {
2527
+ height: 100%;
2528
+ background: var(--accent-primary, #6c5ce7);
2529
+ border-radius: 2px;
2530
+ width: 0%;
2531
+ transition: width 0.1s linear;
2532
+ }
2533
+
2534
+ .audio-player-time {
2535
+ font-size: 12px;
2536
+ color: var(--text-secondary, #999);
2537
+ font-variant-numeric: tabular-nums;
2538
+ min-width: 32px;
2539
+ text-align: right;
2540
+ }
2541
+
2542
+ .audio-player-close {
2543
+ background: none;
2544
+ border: none;
2545
+ color: var(--text-secondary, #999);
2546
+ cursor: pointer;
2547
+ font-size: 20px;
2548
+ padding: 4px 6px;
2549
+ line-height: 1;
2550
+ border-radius: 4px;
2551
+ transition: color 0.15s, background 0.15s;
2552
+ }
2553
+ .audio-player-close:hover {
2554
+ color: var(--text-primary, #eee);
2555
+ background: var(--bg-hover, rgba(255,255,255,0.1));
2556
+ }
2557
+
2558
+ /* ============================================================================
2559
+ MISSED MESSAGES NOTIFICATION
2560
+ ============================================================================ */
2561
+ @keyframes missedFadeInOut {
2562
+ 0% { opacity: 0; transform: translateY(-10px); }
2563
+ 20%, 80% { opacity: 1; transform: translateY(0); }
2564
+ 100% { opacity: 0; transform: translateY(-10px); }
2565
+ }
2566
+
2567
+ .missed-message-banner {
2568
+ background: rgba(0, 120, 200, 0.9);
2569
+ color: white;
2570
+ padding: 8px 16px;
2571
+ border-radius: 6px;
2572
+ margin: 10px;
2573
+ text-align: center;
2574
+ font-size: 14px;
2575
+ animation: missedFadeInOut 4s ease-in-out;
2576
+ }
2577
+
2578
+ /* ============================================================================
2579
+ UPDATE BANNER
2580
+ ============================================================================ */
2581
+
2582
+ @keyframes update-banner-slide-down {
2583
+ from {
2584
+ transform: translateY(-100%);
2585
+ opacity: 0;
2586
+ }
2587
+ to {
2588
+ transform: translateY(0);
2589
+ opacity: 1;
2590
+ }
2591
+ }
2592
+
2593
+ .update-banner {
2594
+ display: flex;
2595
+ align-items: center;
2596
+ gap: 12px;
2597
+ padding: 10px 16px;
2598
+ background: var(--bg-secondary, rgba(10, 20, 40, 0.95));
2599
+ border-bottom: 1px solid var(--accent, #4ecdc4);
2600
+ color: var(--text-primary, #eee);
2601
+ font-size: 13px;
2602
+ animation: update-banner-slide-down 0.3s ease-out;
2603
+ position: relative;
2604
+ z-index: 10;
2605
+ flex-shrink: 0;
2606
+ }
2607
+
2608
+ .update-banner-hiding {
2609
+ transform: translateY(-100%);
2610
+ opacity: 0;
2611
+ transition: transform 0.3s ease-in, opacity 0.3s ease-in;
2612
+ }
2613
+
2614
+ .update-banner-text {
2615
+ flex: 1;
2616
+ min-width: 0;
2617
+ }
2618
+
2619
+ .update-banner-bot-btn {
2620
+ background: var(--accent, #4ecdc4);
2621
+ color: var(--bg, #000);
2622
+ border: none;
2623
+ padding: 5px 14px;
2624
+ border-radius: 4px;
2625
+ cursor: pointer;
2626
+ font-size: 12px;
2627
+ font-weight: 600;
2628
+ white-space: nowrap;
2629
+ transition: opacity 0.15s;
2630
+ }
2631
+
2632
+ .update-banner-bot-btn:hover {
2633
+ opacity: 0.85;
2634
+ }
2635
+
2636
+ .update-banner-dismiss {
2637
+ background: none;
2638
+ border: none;
2639
+ color: var(--text-secondary, #999);
2640
+ cursor: pointer;
2641
+ font-size: 16px;
2642
+ padding: 2px 6px;
2643
+ line-height: 1;
2644
+ border-radius: 4px;
2645
+ transition: color 0.15s, background 0.15s;
2646
+ }
2647
+
2648
+ .update-banner-dismiss:hover {
2649
+ color: var(--text-primary, #eee);
2650
+ background: var(--bg-hover, rgba(255,255,255,0.1));
2651
+ }
2652
+
2653
+
2654
+
2655
+
2656
+
2657
+ /* Utility: hide on touch/mobile devices */
2658
+ @media (max-width: 768px) {
2659
+ .desktop-only {
2660
+ display: none !important;
2661
+ }
2662
+ }
2663
+ /* ============================================================================
2664
+ VOICE MODE CARDS (Settings UI)
2665
+ ============================================================================ */
2666
+
2667
+ .voice-mode-cards {
2668
+ display: flex;
2669
+ flex-direction: column;
2670
+ gap: var(--space-3, 12px);
2671
+ margin-top: var(--space-3, 12px);
2672
+ }
2673
+
2674
+ .voice-mode-card {
2675
+ display: flex;
2676
+ align-items: center;
2677
+ gap: var(--space-4, 16px);
2678
+ padding: var(--space-4, 16px);
2679
+ background: var(--bg-secondary, rgba(10, 20, 40, 0.95));
2680
+ border: 2px solid var(--border, rgba(255, 255, 255, 0.1));
2681
+ border-radius: 12px;
2682
+ cursor: pointer;
2683
+ transition: all 0.2s ease;
2684
+ }
2685
+
2686
+ .voice-mode-card:hover {
2687
+ background: var(--bg-hover, rgba(20, 40, 80, 0.6));
2688
+ border-color: var(--accent, #60a5fa);
2689
+ }
2690
+
2691
+ .voice-mode-card.selected {
2692
+ background: var(--bg-hover, rgba(20, 40, 80, 0.6));
2693
+ border-color: var(--accent, #60a5fa);
2694
+ box-shadow: 0 0 0 2px var(--accent-glow, rgba(96, 165, 250, 0.3));
2695
+ }
2696
+
2697
+ .voice-mode-icon {
2698
+ font-size: 32px;
2699
+ line-height: 1;
2700
+ flex-shrink: 0;
2701
+ }
2702
+
2703
+ .voice-mode-content {
2704
+ flex: 1;
2705
+ }
2706
+
2707
+ .voice-mode-title {
2708
+ font-size: var(--text-base, 14px);
2709
+ font-weight: 600;
2710
+ color: var(--text-primary, #eee);
2711
+ margin-bottom: var(--space-1, 4px);
2712
+ }
2713
+
2714
+ .voice-mode-desc {
2715
+ font-size: var(--text-sm, 13px);
2716
+ color: var(--text-muted, rgba(255, 255, 255, 0.6));
2717
+ line-height: 1.4;
2718
+ }
2719
+
2720
+ /* Keyboard focus for accessibility */
2721
+ .voice-mode-card:focus-visible {
2722
+ outline: 2px solid var(--accent, #60a5fa);
2723
+ outline-offset: 2px;
2724
+ }
2725
+
2726
+ /* Conditional settings containers */
2727
+ #pushToTalkSettings,
2728
+ #liveVoiceSettings,
2729
+ #agentVoiceSettings {
2730
+ margin-top: var(--space-4, 16px);
2731
+ }