clawport-ui 0.1.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.
Files changed (132) hide show
  1. package/.env.example +35 -0
  2. package/BRANDING.md +131 -0
  3. package/CLAUDE.md +252 -0
  4. package/README.md +262 -0
  5. package/SETUP.md +337 -0
  6. package/app/agents/[id]/page.tsx +727 -0
  7. package/app/api/agents/route.ts +12 -0
  8. package/app/api/chat/[id]/route.ts +139 -0
  9. package/app/api/cron-runs/route.ts +13 -0
  10. package/app/api/crons/route.ts +12 -0
  11. package/app/api/kanban/chat/[id]/route.ts +119 -0
  12. package/app/api/kanban/chat-history/[ticketId]/route.ts +36 -0
  13. package/app/api/memory/route.ts +12 -0
  14. package/app/api/transcribe/route.ts +37 -0
  15. package/app/api/tts/route.ts +42 -0
  16. package/app/chat/[id]/page.tsx +10 -0
  17. package/app/chat/page.tsx +200 -0
  18. package/app/crons/page.tsx +870 -0
  19. package/app/docs/page.tsx +399 -0
  20. package/app/favicon.ico +0 -0
  21. package/app/globals.css +692 -0
  22. package/app/kanban/page.tsx +327 -0
  23. package/app/layout.tsx +45 -0
  24. package/app/memory/page.tsx +685 -0
  25. package/app/page.tsx +817 -0
  26. package/app/providers.tsx +37 -0
  27. package/app/settings/page.tsx +901 -0
  28. package/app/settings-provider.tsx +209 -0
  29. package/components/AgentAvatar.tsx +54 -0
  30. package/components/AgentNode.tsx +122 -0
  31. package/components/Breadcrumbs.tsx +126 -0
  32. package/components/DynamicFavicon.tsx +62 -0
  33. package/components/ErrorState.tsx +97 -0
  34. package/components/FeedView.tsx +494 -0
  35. package/components/GlobalSearch.tsx +571 -0
  36. package/components/GridView.tsx +532 -0
  37. package/components/ManorMap.tsx +157 -0
  38. package/components/MobileSidebar.tsx +251 -0
  39. package/components/NavLinks.tsx +271 -0
  40. package/components/OnboardingWizard.tsx +1067 -0
  41. package/components/Sidebar.tsx +115 -0
  42. package/components/ThemeToggle.tsx +108 -0
  43. package/components/chat/AgentList.tsx +537 -0
  44. package/components/chat/ConversationView.tsx +1047 -0
  45. package/components/chat/FileAttachment.tsx +140 -0
  46. package/components/chat/MediaPreview.tsx +111 -0
  47. package/components/chat/VoiceMessage.tsx +139 -0
  48. package/components/crons/PipelineGraph.tsx +327 -0
  49. package/components/crons/WeeklySchedule.tsx +630 -0
  50. package/components/docs/AgentsSection.tsx +209 -0
  51. package/components/docs/ApiReferenceSection.tsx +256 -0
  52. package/components/docs/ArchitectureSection.tsx +221 -0
  53. package/components/docs/ComponentsSection.tsx +253 -0
  54. package/components/docs/CronSystemSection.tsx +235 -0
  55. package/components/docs/DocSection.tsx +346 -0
  56. package/components/docs/GettingStartedSection.tsx +169 -0
  57. package/components/docs/ThemingSection.tsx +257 -0
  58. package/components/docs/TroubleshootingSection.tsx +200 -0
  59. package/components/kanban/AgentPicker.tsx +321 -0
  60. package/components/kanban/CreateTicketModal.tsx +333 -0
  61. package/components/kanban/KanbanBoard.tsx +70 -0
  62. package/components/kanban/KanbanColumn.tsx +166 -0
  63. package/components/kanban/TicketCard.tsx +245 -0
  64. package/components/kanban/TicketDetailPanel.tsx +850 -0
  65. package/components/ui/badge.tsx +48 -0
  66. package/components/ui/button.tsx +64 -0
  67. package/components/ui/card.tsx +92 -0
  68. package/components/ui/dialog.tsx +158 -0
  69. package/components/ui/scroll-area.tsx +58 -0
  70. package/components/ui/separator.tsx +28 -0
  71. package/components/ui/skeleton.tsx +27 -0
  72. package/components/ui/tabs.tsx +91 -0
  73. package/components/ui/tooltip.tsx +57 -0
  74. package/components.json +23 -0
  75. package/docs/API.md +648 -0
  76. package/docs/COMPONENTS.md +1059 -0
  77. package/docs/THEMING.md +795 -0
  78. package/lib/agents-registry.ts +35 -0
  79. package/lib/agents.json +282 -0
  80. package/lib/agents.test.ts +367 -0
  81. package/lib/agents.ts +32 -0
  82. package/lib/anthropic.test.ts +422 -0
  83. package/lib/anthropic.ts +220 -0
  84. package/lib/api-error.ts +16 -0
  85. package/lib/audio-recorder.test.ts +72 -0
  86. package/lib/audio-recorder.ts +169 -0
  87. package/lib/conversations.test.ts +331 -0
  88. package/lib/conversations.ts +117 -0
  89. package/lib/cron-pipelines.test.ts +69 -0
  90. package/lib/cron-pipelines.ts +58 -0
  91. package/lib/cron-runs.test.ts +118 -0
  92. package/lib/cron-runs.ts +67 -0
  93. package/lib/cron-utils.test.ts +222 -0
  94. package/lib/cron-utils.ts +160 -0
  95. package/lib/crons.test.ts +502 -0
  96. package/lib/crons.ts +114 -0
  97. package/lib/env.test.ts +44 -0
  98. package/lib/env.ts +14 -0
  99. package/lib/kanban/automation.test.ts +245 -0
  100. package/lib/kanban/automation.ts +143 -0
  101. package/lib/kanban/chat-store.test.ts +149 -0
  102. package/lib/kanban/chat-store.ts +81 -0
  103. package/lib/kanban/store.test.ts +238 -0
  104. package/lib/kanban/store.ts +98 -0
  105. package/lib/kanban/types.ts +50 -0
  106. package/lib/kanban/useAgentWork.ts +78 -0
  107. package/lib/memory.ts +45 -0
  108. package/lib/multimodal.test.ts +219 -0
  109. package/lib/multimodal.ts +68 -0
  110. package/lib/pipeline.integration.test.ts +343 -0
  111. package/lib/sanitize.ts +194 -0
  112. package/lib/settings.test.ts +137 -0
  113. package/lib/settings.ts +94 -0
  114. package/lib/styles.ts +24 -0
  115. package/lib/themes.ts +9 -0
  116. package/lib/transcribe.test.ts +141 -0
  117. package/lib/transcribe.ts +111 -0
  118. package/lib/types.ts +66 -0
  119. package/lib/utils.ts +6 -0
  120. package/lib/validation.test.ts +132 -0
  121. package/lib/validation.ts +80 -0
  122. package/next.config.ts +7 -0
  123. package/package.json +56 -0
  124. package/postcss.config.mjs +7 -0
  125. package/public/file.svg +1 -0
  126. package/public/globe.svg +1 -0
  127. package/public/next.svg +1 -0
  128. package/public/vercel.svg +1 -0
  129. package/public/window.svg +1 -0
  130. package/scripts/setup.mjs +215 -0
  131. package/tsconfig.json +34 -0
  132. package/vitest.config.ts +17 -0
@@ -0,0 +1,692 @@
1
+ @import "tailwindcss";
2
+ @import "@xyflow/react/dist/style.css";
3
+
4
+ /* ============================================
5
+ Tailwind v4 Theme Tokens
6
+ ============================================ */
7
+
8
+ @theme {
9
+ --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", system-ui, sans-serif;
10
+ --font-mono: "SF Mono", Monaco, Menlo, "Courier New", monospace;
11
+
12
+ /* Typography Scale — Apple HIG */
13
+ --text-caption2: 11px;
14
+ --text-caption1: 12px;
15
+ --text-footnote: 13px;
16
+ --text-subheadline: 15px;
17
+ --text-body: 17px;
18
+ --text-title3: 20px;
19
+ --text-title2: 22px;
20
+ --text-title1: 28px;
21
+ --text-large-title: 34px;
22
+
23
+ /* Leading */
24
+ --leading-tight: 1.15;
25
+ --leading-snug: 1.3;
26
+ --leading-normal: 1.47;
27
+ --leading-relaxed: 1.65;
28
+
29
+ /* Tracking */
30
+ --tracking-tight: -0.41px;
31
+ --tracking-normal: -0.24px;
32
+ --tracking-wide: 0.07em;
33
+
34
+ /* Font Weights */
35
+ --weight-regular: 400;
36
+ --weight-medium: 500;
37
+ --weight-semibold: 600;
38
+ --weight-bold: 700;
39
+
40
+ /* Spacing Scale — 4px grid */
41
+ --space-1: 4px;
42
+ --space-2: 8px;
43
+ --space-3: 12px;
44
+ --space-4: 16px;
45
+ --space-5: 20px;
46
+ --space-6: 24px;
47
+ --space-8: 32px;
48
+ --space-10: 40px;
49
+ --space-12: 48px;
50
+ --space-16: 64px;
51
+
52
+ /* Tailwind animation tokens */
53
+ --animate-fade-in: fadeIn 0.2s ease-out;
54
+ --animate-slide-in: slideIn 0.2s ease-out;
55
+ --animate-pulse-red: pulse-red 1.5s ease-in-out infinite;
56
+ --animate-blink: blink-cursor 1s step-end infinite;
57
+ --animate-float-hint: float-hint 2s ease-in-out infinite;
58
+ }
59
+
60
+ /* ============================================
61
+ Apple Material Design System — Themes
62
+ ============================================ */
63
+
64
+ /* DEFAULT: Dark (Apple Dark Mode) */
65
+ :root, [data-theme="dark"] {
66
+ --bg: #000000;
67
+ --bg-secondary: rgba(28,28,30,1);
68
+ --bg-tertiary: rgba(44,44,46,1);
69
+ --material-regular: rgba(28,28,30,0.92);
70
+ --material-thick: rgba(22,22,24,0.96);
71
+ --material-thin: rgba(255,255,255,0.06);
72
+ --material-ultra-thin: rgba(255,255,255,0.04);
73
+ --fill-primary: rgba(120,120,128,0.36);
74
+ --fill-secondary: rgba(120,120,128,0.32);
75
+ --fill-tertiary: rgba(118,118,128,0.24);
76
+ --fill-quaternary: rgba(118,118,128,0.18);
77
+ --separator: rgba(84,84,88,0.60);
78
+ --separator-opaque: #38383A;
79
+ --text-primary: #FFFFFF;
80
+ --text-secondary: rgba(235,235,245,0.60);
81
+ --text-tertiary: rgba(235,235,245,0.30);
82
+ --text-quaternary: rgba(235,235,245,0.18);
83
+ --accent: #F5C518;
84
+ --accent-fill: rgba(245,197,24,0.15);
85
+ --accent-contrast: #000;
86
+ --system-blue: #0A84FF;
87
+ --system-green: #30D158;
88
+ --system-red: #FF453A;
89
+ --system-orange: #FF9F0A;
90
+ --system-purple: #BF5AF2;
91
+ --inset-shine: inset 0 1px 0 rgba(255,255,255,0.08);
92
+ --shadow-subtle: 0 1px 2px rgba(0,0,0,0.20);
93
+ --shadow-ambient: 0 0 0 0.5px rgba(0,0,0,0.20);
94
+ --shadow-key: 0 4px 16px rgba(0,0,0,0.40);
95
+ --shadow-card: 0 0 0 0.5px rgba(0,0,0,0.20), 0 4px 16px rgba(0,0,0,0.40), inset 0 1px 0 rgba(255,255,255,0.08);
96
+ --shadow-overlay: 0 0 0 0.5px rgba(0,0,0,0.30), 0 16px 48px rgba(0,0,0,0.60), inset 0 1px 0 rgba(255,255,255,0.06);
97
+ --code-bg: rgba(255,255,255,0.06);
98
+ --code-border: rgba(255,255,255,0.10);
99
+ --code-text: #e5e5ea;
100
+ --sidebar-bg: rgba(28,28,30,0.92);
101
+ --sidebar-backdrop: blur(40px) saturate(180%);
102
+ --radius-sm: 6px;
103
+ --radius-md: 12px;
104
+ --radius-lg: 16px;
105
+ --radius-xl: 20px;
106
+ --radius-2xl: 24px;
107
+ --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
108
+ --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
109
+ --ease-snappy: cubic-bezier(0.2, 0, 0, 1);
110
+ }
111
+
112
+ /* GLASS: Frosted glass dark variant */
113
+ [data-theme="glass"] {
114
+ --bg: #0d0d18;
115
+ --bg-secondary: rgba(20,20,32,1);
116
+ --bg-tertiary: rgba(30,30,48,1);
117
+ --material-regular: rgba(255,255,255,0.07);
118
+ --material-thick: rgba(255,255,255,0.10);
119
+ --material-thin: rgba(255,255,255,0.06);
120
+ --material-ultra-thin: rgba(255,255,255,0.04);
121
+ --fill-primary: rgba(255,255,255,0.16);
122
+ --fill-secondary: rgba(255,255,255,0.12);
123
+ --fill-tertiary: rgba(255,255,255,0.08);
124
+ --fill-quaternary: rgba(255,255,255,0.05);
125
+ --separator: rgba(255,255,255,0.14);
126
+ --separator-opaque: rgba(255,255,255,0.18);
127
+ --text-primary: #ffffff;
128
+ --text-secondary: rgba(255,255,255,0.60);
129
+ --text-tertiary: rgba(255,255,255,0.30);
130
+ --text-quaternary: rgba(255,255,255,0.18);
131
+ --accent: #F5C518;
132
+ --accent-fill: rgba(245,197,24,0.18);
133
+ --accent-contrast: #000;
134
+ --system-blue: #3B9EFF;
135
+ --system-green: #34D058;
136
+ --system-red: #FF5C57;
137
+ --system-orange: #FFB340;
138
+ --system-purple: #CC6FF0;
139
+ --inset-shine: inset 0 1px 0 rgba(255,255,255,0.15);
140
+ --shadow-subtle: 0 1px 3px rgba(0,0,0,0.25);
141
+ --shadow-ambient: 0 0 0 0.5px rgba(255,255,255,0.06);
142
+ --shadow-key: 0 8px 32px rgba(0,0,0,0.45);
143
+ --shadow-card: 0 0 0 0.5px rgba(255,255,255,0.06), 0 8px 32px rgba(0,0,0,0.40), inset 0 1px 0 rgba(255,255,255,0.15);
144
+ --shadow-overlay: 0 0 0 0.5px rgba(255,255,255,0.08), 0 16px 56px rgba(0,0,0,0.55), inset 0 1px 0 rgba(255,255,255,0.12);
145
+ --code-bg: rgba(255,255,255,0.07);
146
+ --code-border: rgba(255,255,255,0.12);
147
+ --code-text: #e5e5ea;
148
+ --sidebar-bg: rgba(255,255,255,0.05);
149
+ --sidebar-backdrop: blur(40px) saturate(180%);
150
+ --radius-sm: 6px;
151
+ --radius-md: 12px;
152
+ --radius-lg: 16px;
153
+ --radius-xl: 20px;
154
+ --radius-2xl: 24px;
155
+ --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
156
+ --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
157
+ --ease-snappy: cubic-bezier(0.2, 0, 0, 1);
158
+ }
159
+
160
+ /* COLOR: Vibrant purple-indigo variant */
161
+ [data-theme="color"] {
162
+ --bg: #0a0814;
163
+ --bg-secondary: #16112a;
164
+ --bg-tertiary: #1e1838;
165
+ --material-regular: #16112a;
166
+ --material-thick: #1e1838;
167
+ --material-thin: rgba(139,92,246,0.12);
168
+ --material-ultra-thin: rgba(139,92,246,0.06);
169
+ --fill-primary: rgba(139,92,246,0.30);
170
+ --fill-secondary: rgba(139,92,246,0.20);
171
+ --fill-tertiary: rgba(139,92,246,0.14);
172
+ --fill-quaternary: rgba(139,92,246,0.08);
173
+ --separator: rgba(139,92,246,0.25);
174
+ --separator-opaque: rgba(139,92,246,0.35);
175
+ --text-primary: #ffffff;
176
+ --text-secondary: rgba(220,210,255,0.70);
177
+ --text-tertiary: rgba(220,210,255,0.35);
178
+ --text-quaternary: rgba(220,210,255,0.20);
179
+ --accent: #F5C518;
180
+ --accent-fill: rgba(245,197,24,0.20);
181
+ --accent-contrast: #000;
182
+ --system-blue: #60A5FA;
183
+ --system-green: #34D399;
184
+ --system-red: #F87171;
185
+ --system-orange: #FB923C;
186
+ --system-purple: #C084FC;
187
+ --inset-shine: inset 0 1px 0 rgba(139,92,246,0.15);
188
+ --shadow-subtle: 0 1px 3px rgba(88,28,135,0.20);
189
+ --shadow-ambient: 0 0 0 0.5px rgba(88,28,135,0.30);
190
+ --shadow-key: 0 8px 32px rgba(88,28,135,0.40);
191
+ --shadow-card: 0 0 0 0.5px rgba(88,28,135,0.25), 0 4px 24px rgba(88,28,135,0.30), inset 0 1px 0 rgba(139,92,246,0.15);
192
+ --shadow-overlay: 0 0 0 0.5px rgba(88,28,135,0.30), 0 16px 48px rgba(88,28,135,0.45), inset 0 1px 0 rgba(139,92,246,0.12);
193
+ --code-bg: rgba(139,92,246,0.10);
194
+ --code-border: rgba(139,92,246,0.20);
195
+ --code-text: #ddd6fe;
196
+ --sidebar-bg: #0f0b20;
197
+ --sidebar-backdrop: blur(40px) saturate(200%);
198
+ --radius-sm: 6px;
199
+ --radius-md: 12px;
200
+ --radius-lg: 16px;
201
+ --radius-xl: 20px;
202
+ --radius-2xl: 24px;
203
+ --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
204
+ --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
205
+ --ease-snappy: cubic-bezier(0.2, 0, 0, 1);
206
+ }
207
+
208
+ /* LIGHT: Apple Light Mode */
209
+ [data-theme="light"] {
210
+ --bg: #f2f2f7;
211
+ --bg-secondary: #ffffff;
212
+ --bg-tertiary: #e5e5ea;
213
+ --material-regular: #ffffff;
214
+ --material-thick: rgba(255,255,255,0.97);
215
+ --material-thin: rgba(0,0,0,0.03);
216
+ --material-ultra-thin: rgba(0,0,0,0.02);
217
+ --fill-primary: rgba(120,120,128,0.20);
218
+ --fill-secondary: rgba(120,120,128,0.16);
219
+ --fill-tertiary: rgba(118,118,128,0.12);
220
+ --fill-quaternary: rgba(118,118,128,0.08);
221
+ --separator: rgba(60,60,67,0.29);
222
+ --separator-opaque: #C6C6C8;
223
+ --text-primary: #000000;
224
+ --text-secondary: rgba(60,60,67,0.60);
225
+ --text-tertiary: rgba(60,60,67,0.44);
226
+ --text-quaternary: rgba(60,60,67,0.30);
227
+ --accent: #B8860B;
228
+ --accent-fill: rgba(184,134,11,0.12);
229
+ --accent-contrast: #fff;
230
+ --system-blue: #007AFF;
231
+ --system-green: #28CD41;
232
+ --system-red: #FF3B30;
233
+ --system-orange: #FF9500;
234
+ --system-purple: #AF52DE;
235
+ --inset-shine: inset 0 1px 0 rgba(255,255,255,0.70);
236
+ --shadow-subtle: 0 1px 2px rgba(0,0,0,0.06);
237
+ --shadow-ambient: 0 0 0 0.5px rgba(0,0,0,0.08);
238
+ --shadow-key: 0 2px 8px rgba(0,0,0,0.12), 0 8px 24px rgba(0,0,0,0.08);
239
+ --shadow-card: 0 0 0 0.5px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.70);
240
+ --shadow-overlay: 0 0 0 0.5px rgba(0,0,0,0.08), 0 4px 16px rgba(0,0,0,0.10), 0 16px 48px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,0.80);
241
+ --code-bg: rgba(0,0,0,0.04);
242
+ --code-border: rgba(0,0,0,0.08);
243
+ --code-text: #1c1c1e;
244
+ --sidebar-bg: #ffffff;
245
+ --sidebar-backdrop: blur(20px) saturate(150%);
246
+ --radius-sm: 6px;
247
+ --radius-md: 12px;
248
+ --radius-lg: 16px;
249
+ --radius-xl: 20px;
250
+ --radius-2xl: 24px;
251
+ --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
252
+ --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
253
+ --ease-snappy: cubic-bezier(0.2, 0, 0, 1);
254
+ }
255
+
256
+ /* SYSTEM: Media query fallback before JS hydration */
257
+ @media (prefers-color-scheme: light) {
258
+ [data-theme="system"] {
259
+ --bg: #f2f2f7;
260
+ --bg-secondary: #ffffff;
261
+ --bg-tertiary: #e5e5ea;
262
+ --material-regular: #ffffff;
263
+ --material-thick: rgba(255,255,255,0.97);
264
+ --material-thin: rgba(0,0,0,0.03);
265
+ --material-ultra-thin: rgba(0,0,0,0.02);
266
+ --fill-primary: rgba(120,120,128,0.20);
267
+ --fill-secondary: rgba(120,120,128,0.16);
268
+ --fill-tertiary: rgba(118,118,128,0.12);
269
+ --fill-quaternary: rgba(118,118,128,0.08);
270
+ --separator: rgba(60,60,67,0.29);
271
+ --separator-opaque: #C6C6C8;
272
+ --text-primary: #000000;
273
+ --text-secondary: rgba(60,60,67,0.60);
274
+ --text-tertiary: rgba(60,60,67,0.44);
275
+ --text-quaternary: rgba(60,60,67,0.30);
276
+ --accent: #B8860B;
277
+ --accent-fill: rgba(184,134,11,0.12);
278
+ --accent-contrast: #fff;
279
+ --system-blue: #007AFF;
280
+ --system-green: #28CD41;
281
+ --system-red: #FF3B30;
282
+ --system-orange: #FF9500;
283
+ --system-purple: #AF52DE;
284
+ --inset-shine: inset 0 1px 0 rgba(255,255,255,0.70);
285
+ --shadow-subtle: 0 1px 2px rgba(0,0,0,0.06);
286
+ --shadow-ambient: 0 0 0 0.5px rgba(0,0,0,0.08);
287
+ --shadow-key: 0 2px 8px rgba(0,0,0,0.12), 0 8px 24px rgba(0,0,0,0.08);
288
+ --shadow-card: 0 0 0 0.5px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.70);
289
+ --shadow-overlay: 0 0 0 0.5px rgba(0,0,0,0.08), 0 4px 16px rgba(0,0,0,0.10), 0 16px 48px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,0.80);
290
+ --code-bg: rgba(0,0,0,0.04);
291
+ --code-border: rgba(0,0,0,0.08);
292
+ --code-text: #1c1c1e;
293
+ --sidebar-bg: #ffffff;
294
+ --sidebar-backdrop: blur(20px) saturate(150%);
295
+ --radius-sm: 6px;
296
+ --radius-md: 12px;
297
+ --radius-lg: 16px;
298
+ --radius-xl: 20px;
299
+ --radius-2xl: 24px;
300
+ --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
301
+ --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
302
+ --ease-snappy: cubic-bezier(0.2, 0, 0, 1);
303
+ }
304
+ }
305
+
306
+ /* ============================================
307
+ Base Styles
308
+ ============================================ */
309
+
310
+ html {
311
+ scroll-behavior: smooth;
312
+ -webkit-tap-highlight-color: transparent;
313
+ }
314
+
315
+ body {
316
+ background: var(--bg);
317
+ color: var(--text-primary);
318
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", system-ui, sans-serif;
319
+ font-size: var(--text-body);
320
+ line-height: var(--leading-normal);
321
+ -webkit-font-smoothing: antialiased;
322
+ -moz-osx-font-smoothing: grayscale;
323
+ text-rendering: optimizeLegibility;
324
+ }
325
+
326
+ /* Apple-style scrollbar */
327
+ ::-webkit-scrollbar { width: 8px; height: 8px; }
328
+ ::-webkit-scrollbar-track { background: transparent; }
329
+ ::-webkit-scrollbar-thumb { background: rgba(120,120,128,0.36); border-radius: 4px; border: 2px solid transparent; background-clip: content-box; }
330
+ ::-webkit-scrollbar-thumb:hover { background: rgba(120,120,128,0.60); border: 2px solid transparent; background-clip: content-box; }
331
+
332
+ /* Selection */
333
+ ::selection { background: var(--accent-fill); color: var(--text-primary); }
334
+
335
+ /* ============================================
336
+ Keyframe Animations
337
+ ============================================ */
338
+
339
+ @keyframes scaleIn {
340
+ from { transform: scale(0.94); opacity: 0; }
341
+ to { transform: scale(1); opacity: 1; }
342
+ }
343
+
344
+ @keyframes slideUp {
345
+ from { transform: translateY(8px); opacity: 0; }
346
+ to { transform: translateY(0); opacity: 1; }
347
+ }
348
+
349
+ @keyframes slideInRight {
350
+ from { transform: translateX(16px); opacity: 0; }
351
+ to { transform: translateX(0); opacity: 1; }
352
+ }
353
+
354
+ @keyframes fadeIn {
355
+ from { opacity: 0; transform: translateY(4px); }
356
+ to { opacity: 1; transform: translateY(0); }
357
+ }
358
+
359
+ @keyframes slideIn {
360
+ from { opacity: 0; transform: translateX(12px); }
361
+ to { opacity: 1; transform: translateX(0); }
362
+ }
363
+
364
+ @keyframes pulse-red {
365
+ 0%, 100% { opacity: 1; }
366
+ 50% { opacity: 0.4; }
367
+ }
368
+
369
+ @keyframes blink-cursor {
370
+ 0%, 100% { opacity: 1; }
371
+ 50% { opacity: 0; }
372
+ }
373
+
374
+ @keyframes float-hint {
375
+ 0%, 100% { transform: translate(0, 0); }
376
+ 50% { transform: translate(-3px, -3px); }
377
+ }
378
+
379
+ @keyframes shimmer {
380
+ 0% { background-position: -200% 0; }
381
+ 100% { background-position: 200% 0; }
382
+ }
383
+
384
+ @keyframes slideDown {
385
+ from { opacity: 0; transform: translateY(-8px); }
386
+ to { opacity: 1; transform: translateY(0); }
387
+ }
388
+
389
+ @keyframes scaleUp {
390
+ from { opacity: 0; transform: scale(0.95); }
391
+ to { opacity: 1; transform: scale(1); }
392
+ }
393
+
394
+ @keyframes fadeOut {
395
+ from { opacity: 1; }
396
+ to { opacity: 0; }
397
+ }
398
+
399
+ @keyframes spin {
400
+ from { transform: rotate(0deg); }
401
+ to { transform: rotate(360deg); }
402
+ }
403
+
404
+ /* Typing dots */
405
+ @keyframes bounce-dot {
406
+ 0%, 60%, 100% { transform: translateY(0); }
407
+ 30% { transform: translateY(-4px); }
408
+ }
409
+
410
+ /* ============================================
411
+ Utility Animation Classes
412
+ ============================================ */
413
+
414
+ .animate-scale-in { animation: scaleIn 200ms var(--ease-spring); }
415
+ .animate-slide-up { animation: slideUp 250ms var(--ease-spring); }
416
+ .animate-slide-in-right { animation: slideInRight 300ms var(--ease-smooth); }
417
+ .animate-fade-in { animation: fadeIn 200ms var(--ease-smooth); }
418
+ .animate-slide-in { animation: slideIn 200ms var(--ease-smooth); }
419
+ .animate-error-pulse { animation: pulse-red 1.5s ease-in-out infinite; }
420
+ .animate-blink { animation: blink-cursor 1s step-end infinite; }
421
+ .animate-float-hint { animation: float-hint 2s ease-in-out infinite; }
422
+
423
+ /* New animation utilities */
424
+ .animate-shimmer { animation: shimmer 1.5s ease-in-out infinite; background-size: 200% 100%; }
425
+ .animate-slide-down { animation: slideDown 250ms var(--ease-smooth) forwards; }
426
+ .animate-slide-up-enter { animation: slideUp 250ms var(--ease-smooth) forwards; }
427
+ .animate-scale-up { animation: scaleUp 200ms var(--ease-spring) forwards; }
428
+ .animate-fade-out { animation: fadeOut 150ms ease forwards; }
429
+
430
+ /* Panel slide-in from right edge */
431
+ @keyframes panelSlideIn {
432
+ from { transform: translateX(100%); opacity: 0.8; }
433
+ to { transform: translateX(0); opacity: 1; }
434
+ }
435
+
436
+ .panel-slide-in {
437
+ animation: panelSlideIn 350ms cubic-bezier(0.32, 0.72, 0, 1) both;
438
+ }
439
+
440
+ /* Backdrop fade (reuses fadeIn timing) */
441
+ .backdrop-fade {
442
+ animation: fadeIn 200ms ease-out both;
443
+ }
444
+
445
+ /* ============================================
446
+ Interactive State Classes
447
+ ============================================ */
448
+
449
+ /* Hover lift — card elevation on hover */
450
+ .hover-lift {
451
+ transition: transform 200ms var(--ease-spring), box-shadow 200ms var(--ease-smooth);
452
+ }
453
+ .hover-lift:hover {
454
+ transform: translateY(-2px);
455
+ box-shadow: var(--shadow-key);
456
+ }
457
+ .hover-lift:active {
458
+ transform: translateY(0) scale(0.98);
459
+ transition-duration: 100ms;
460
+ }
461
+
462
+ /* Hover background — subtle fill on hover */
463
+ .hover-bg {
464
+ transition: background-color 150ms var(--ease-smooth);
465
+ }
466
+ .hover-bg:hover {
467
+ background-color: var(--fill-secondary);
468
+ }
469
+ .hover-bg:active {
470
+ background-color: var(--fill-tertiary);
471
+ }
472
+
473
+ /* Button scale — tactile press */
474
+ .btn-scale {
475
+ transition: transform 150ms var(--ease-spring), box-shadow 150ms var(--ease-smooth);
476
+ }
477
+ .btn-scale:hover {
478
+ transform: scale(0.98);
479
+ }
480
+ .btn-scale:active {
481
+ transform: scale(0.96);
482
+ }
483
+
484
+ /* Primary CTA — gold glow */
485
+ .btn-primary {
486
+ background: var(--accent);
487
+ color: var(--accent-contrast);
488
+ font-weight: var(--weight-semibold);
489
+ border: none;
490
+ cursor: pointer;
491
+ transition: all 150ms var(--ease-spring);
492
+ }
493
+ .btn-primary:hover {
494
+ transform: scale(0.98);
495
+ box-shadow: 0 0 24px var(--accent-fill);
496
+ }
497
+ .btn-primary:active {
498
+ transform: scale(0.96);
499
+ }
500
+ .btn-primary:disabled {
501
+ opacity: 0.4;
502
+ cursor: not-allowed;
503
+ transform: none;
504
+ box-shadow: none;
505
+ }
506
+
507
+ /* Ghost button */
508
+ .btn-ghost {
509
+ background: transparent;
510
+ color: var(--text-secondary);
511
+ border: none;
512
+ cursor: pointer;
513
+ transition: all 150ms var(--ease-smooth);
514
+ }
515
+ .btn-ghost:hover {
516
+ background: var(--fill-secondary);
517
+ color: var(--text-primary);
518
+ }
519
+
520
+ /* Focus ring — keyboard only */
521
+ .focus-ring:focus-visible {
522
+ outline: 2px solid var(--system-blue);
523
+ outline-offset: 2px;
524
+ }
525
+
526
+ /* Nav item */
527
+ .nav-item {
528
+ transition: background-color 150ms var(--ease-smooth), color 150ms var(--ease-smooth);
529
+ border-radius: var(--radius-sm);
530
+ }
531
+ .nav-item:hover {
532
+ background: var(--fill-secondary);
533
+ }
534
+ .nav-item.active {
535
+ background: var(--fill-secondary);
536
+ color: var(--accent);
537
+ }
538
+
539
+ /* ============================================
540
+ React Flow Overrides (theme-aware)
541
+ ============================================ */
542
+
543
+ .react-flow__background { background: var(--bg) !important; }
544
+ .react-flow__minimap { background: var(--material-regular) !important; border: 1px solid var(--separator); border-radius: var(--radius-md); overflow: hidden; }
545
+ .react-flow__controls { background: var(--material-regular) !important; border: 1px solid var(--separator) !important; border-radius: var(--radius-md) !important; overflow: hidden; box-shadow: var(--shadow-card); }
546
+ .react-flow__controls-button { background: var(--material-regular) !important; border-color: var(--separator) !important; color: var(--text-secondary) !important; }
547
+ .react-flow__controls-button:hover { background: var(--material-thin) !important; color: var(--text-primary) !important; }
548
+ .react-flow__edge-path { opacity: 0.85; stroke-width: 1.5; }
549
+ .react-flow__node { transition: transform 200ms var(--ease-spring); }
550
+
551
+ /* ============================================
552
+ Apple Card
553
+ ============================================ */
554
+
555
+ .apple-card {
556
+ background: var(--material-thin);
557
+ border-radius: var(--radius-lg);
558
+ box-shadow: var(--shadow-card);
559
+ border: 1px solid var(--separator);
560
+ backdrop-filter: blur(20px) saturate(180%);
561
+ -webkit-backdrop-filter: blur(20px) saturate(180%);
562
+ }
563
+
564
+ /* ============================================
565
+ Apple Input
566
+ ============================================ */
567
+
568
+ .apple-input {
569
+ background: var(--fill-tertiary);
570
+ border: none;
571
+ border-radius: var(--radius-md);
572
+ padding: 11px 16px;
573
+ font-size: 15px;
574
+ color: var(--text-primary);
575
+ outline: none;
576
+ transition: box-shadow 200ms var(--ease-smooth);
577
+ }
578
+
579
+ .apple-input::placeholder { color: var(--text-tertiary); }
580
+ .apple-input:focus { box-shadow: 0 0 0 4px rgba(10,132,255,0.30); }
581
+
582
+ /* ============================================
583
+ Apple Buttons
584
+ ============================================ */
585
+
586
+ .apple-btn-primary {
587
+ background: var(--accent);
588
+ color: var(--accent-contrast);
589
+ border: none;
590
+ border-radius: var(--radius-md);
591
+ padding: 14px 20px;
592
+ font-size: 15px;
593
+ font-weight: 600;
594
+ cursor: pointer;
595
+ transition: all 150ms var(--ease-spring);
596
+ }
597
+
598
+ .apple-btn-primary:hover { filter: brightness(1.05); transform: scale(0.98); }
599
+ .apple-btn-primary:active { transform: scale(0.96); }
600
+
601
+ .apple-btn-secondary {
602
+ background: var(--accent-fill);
603
+ color: var(--accent);
604
+ border: none;
605
+ border-radius: var(--radius-md);
606
+ padding: 14px 20px;
607
+ font-size: 15px;
608
+ font-weight: 600;
609
+ cursor: pointer;
610
+ transition: all 150ms var(--ease-spring);
611
+ }
612
+
613
+ .apple-btn-secondary:hover { filter: brightness(1.08); }
614
+
615
+ /* ============================================
616
+ Glass Theme Specifics
617
+ ============================================ */
618
+
619
+ [data-theme="glass"] body {
620
+ background: radial-gradient(ellipse at 30% 20%, #1a1040 0%, #0d0d18 40%, #050510 100%);
621
+ min-height: 100vh;
622
+ }
623
+
624
+ .glass-orbs { display: none; }
625
+ [data-theme="glass"] .glass-orbs { display: block; }
626
+
627
+ /* ============================================
628
+ Color Theme Specifics
629
+ ============================================ */
630
+
631
+ [data-theme="color"] body {
632
+ background: linear-gradient(135deg, #0a0814 0%, #0f0b20 50%, #0a0814 100%);
633
+ }
634
+
635
+ [data-theme="color"] .react-flow__node > div {
636
+ background: linear-gradient(#16112a, #16112a) padding-box,
637
+ linear-gradient(135deg, rgba(139,92,246,0.5), rgba(245,197,24,0.3)) border-box !important;
638
+ border: 1px solid transparent !important;
639
+ }
640
+
641
+ [data-theme="color"] .react-flow__edge-path { stroke: rgba(139,92,246,0.45) !important; }
642
+
643
+ /* ============================================
644
+ Light Theme Overrides
645
+ ============================================ */
646
+
647
+ [data-theme="light"] .react-flow__edge-path { stroke: rgba(60,60,67,0.25) !important; }
648
+
649
+ [data-theme="light"] .apple-card {
650
+ background: #ffffff !important;
651
+ border: 1px solid rgba(60,60,67,0.12) !important;
652
+ box-shadow: var(--shadow-card) !important;
653
+ }
654
+
655
+ [data-theme="light"] .apple-input:focus { box-shadow: 0 0 0 4px rgba(0,122,255,0.25); }
656
+
657
+ [data-theme="light"] .msg-user { background: var(--system-blue) !important; color: #ffffff !important; }
658
+ [data-theme="light"] .msg-assistant {
659
+ background: #ffffff !important;
660
+ color: #000000 !important;
661
+ border: 1px solid rgba(60,60,67,0.12) !important;
662
+ box-shadow: 0 1px 4px rgba(0,0,0,0.06) !important;
663
+ }
664
+
665
+ /* User bubble code blocks need inverted colors in light theme */
666
+ .msg-user pre { background: rgba(0,0,0,0.15) !important; color: var(--accent-contrast) !important; }
667
+ .msg-user code { background: rgba(0,0,0,0.12) !important; color: var(--accent-contrast) !important; }
668
+
669
+ /* ============================================
670
+ Chat — Typing Dots
671
+ ============================================ */
672
+
673
+ .typing-dot {
674
+ width: 6px;
675
+ height: 6px;
676
+ border-radius: 50%;
677
+ background: var(--text-tertiary);
678
+ animation: bounce-dot 1.2s ease-in-out infinite;
679
+ }
680
+
681
+ /* ============================================
682
+ Reduced Motion
683
+ ============================================ */
684
+
685
+ @media (prefers-reduced-motion: reduce) {
686
+ *, *::before, *::after {
687
+ animation-duration: 0.01ms !important;
688
+ animation-iteration-count: 1 !important;
689
+ transition-duration: 0.01ms !important;
690
+ scroll-behavior: auto !important;
691
+ }
692
+ }