@mooncompany/uplink-chat 0.5.2 → 0.32.3

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