@nextclaw/ui 0.2.4 → 0.2.5

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 (78) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/assets/index-BV3Gyu8h.js +225 -0
  3. package/dist/assets/index-iSLahgqA.css +1 -0
  4. package/dist/index.html +2 -2
  5. package/dist/logos/aihubmix.png +0 -0
  6. package/dist/logos/anthropic.svg +1 -0
  7. package/dist/logos/dashscope.png +0 -0
  8. package/dist/logos/deepseek.png +0 -0
  9. package/dist/logos/dingtalk.svg +1 -0
  10. package/dist/logos/discord.svg +1 -0
  11. package/dist/logos/email.svg +1 -0
  12. package/dist/logos/feishu.svg +12 -0
  13. package/dist/logos/gemini.svg +1 -0
  14. package/dist/logos/groq.svg +1 -0
  15. package/dist/logos/minimax.svg +1 -0
  16. package/dist/logos/mochat.svg +6 -0
  17. package/dist/logos/moonshot.png +0 -0
  18. package/dist/logos/openai.svg +1 -0
  19. package/dist/logos/openrouter.svg +1 -0
  20. package/dist/logos/qq.svg +1 -0
  21. package/dist/logos/slack.svg +1 -0
  22. package/dist/logos/telegram.svg +1 -0
  23. package/dist/logos/vllm.svg +1 -0
  24. package/dist/logos/whatsapp.svg +1 -0
  25. package/dist/logos/zhipu.svg +15 -0
  26. package/package.json +1 -1
  27. package/public/logos/aihubmix.png +0 -0
  28. package/public/logos/anthropic.svg +1 -0
  29. package/public/logos/dashscope.png +0 -0
  30. package/public/logos/deepseek.png +0 -0
  31. package/public/logos/dingtalk.svg +1 -0
  32. package/public/logos/discord.svg +1 -0
  33. package/public/logos/email.svg +1 -0
  34. package/public/logos/feishu.svg +12 -0
  35. package/public/logos/gemini.svg +1 -0
  36. package/public/logos/groq.svg +1 -0
  37. package/public/logos/minimax.svg +1 -0
  38. package/public/logos/mochat.svg +6 -0
  39. package/public/logos/moonshot.png +0 -0
  40. package/public/logos/openai.svg +1 -0
  41. package/public/logos/openrouter.svg +1 -0
  42. package/public/logos/qq.svg +1 -0
  43. package/public/logos/slack.svg +1 -0
  44. package/public/logos/telegram.svg +1 -0
  45. package/public/logos/vllm.svg +1 -0
  46. package/public/logos/whatsapp.svg +1 -0
  47. package/public/logos/zhipu.svg +15 -0
  48. package/src/App.tsx +0 -3
  49. package/src/api/config.ts +0 -19
  50. package/src/api/types.ts +0 -8
  51. package/src/components/common/LogoBadge.tsx +35 -0
  52. package/src/components/common/StatusBadge.tsx +4 -4
  53. package/src/components/config/ChannelForm.tsx +16 -18
  54. package/src/components/config/ChannelsList.tsx +87 -37
  55. package/src/components/config/ModelConfig.tsx +25 -25
  56. package/src/components/config/ProviderForm.tsx +9 -11
  57. package/src/components/config/ProvidersList.tsx +90 -38
  58. package/src/components/layout/Header.tsx +7 -7
  59. package/src/components/layout/Sidebar.tsx +10 -23
  60. package/src/components/ui/HighlightCard.tsx +29 -29
  61. package/src/components/ui/button.tsx +13 -8
  62. package/src/components/ui/card.tsx +8 -7
  63. package/src/components/ui/dialog.tsx +8 -8
  64. package/src/components/ui/input.tsx +1 -1
  65. package/src/components/ui/label.tsx +1 -1
  66. package/src/components/ui/switch.tsx +3 -3
  67. package/src/components/ui/tabs-custom.tsx +6 -6
  68. package/src/components/ui/tabs.tsx +7 -6
  69. package/src/hooks/useConfig.ts +2 -29
  70. package/src/index.css +103 -56
  71. package/src/lib/i18n.ts +3 -6
  72. package/src/lib/logos.ts +42 -0
  73. package/src/stores/ui.store.ts +1 -1
  74. package/src/styles/design-system.css +248 -0
  75. package/tailwind.config.js +118 -10
  76. package/dist/assets/index-C4OKhpdC.css +0 -1
  77. package/dist/assets/index-C8nOCIVG.js +0 -240
  78. package/src/components/config/UiConfig.tsx +0 -189
@@ -1,5 +1,5 @@
1
1
  import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
2
- import { fetchConfig, fetchConfigMeta, updateModel, updateProvider, updateChannel, updateUiConfig, reloadConfig } from '@/api/config';
2
+ import { fetchConfig, fetchConfigMeta, updateModel, updateProvider, updateChannel } from '@/api/config';
3
3
  import { toast } from 'sonner';
4
4
  import { t } from '@/lib/i18n';
5
5
 
@@ -59,37 +59,10 @@ export function useUpdateChannel() {
59
59
  updateChannel(channel, data as Parameters<typeof updateChannel>[1]),
60
60
  onSuccess: () => {
61
61
  queryClient.invalidateQueries({ queryKey: ['config'] });
62
- toast.success(t('configSaved'));
63
- },
64
- onError: (error: Error) => {
65
- toast.error(t('configSaveFailed') + ': ' + error.message);
66
- }
67
- });
68
- }
69
-
70
- export function useUpdateUiConfig() {
71
- const queryClient = useQueryClient();
72
-
73
- return useMutation({
74
- mutationFn: updateUiConfig,
75
- onSuccess: () => {
76
- queryClient.invalidateQueries({ queryKey: ['config'] });
77
- toast.success(t('configSaved'));
62
+ toast.success(t('configSavedApplied'));
78
63
  },
79
64
  onError: (error: Error) => {
80
65
  toast.error(t('configSaveFailed') + ': ' + error.message);
81
66
  }
82
67
  });
83
68
  }
84
-
85
- export function useReloadConfig() {
86
- return useMutation({
87
- mutationFn: reloadConfig,
88
- onSuccess: () => {
89
- toast.success(t('configReloaded'));
90
- },
91
- onError: (error: Error) => {
92
- toast.error(t('configReloadFailed') + ': ' + error.message);
93
- }
94
- });
95
- }
package/src/index.css CHANGED
@@ -1,55 +1,67 @@
1
+ /* Import Design System - must be first */
2
+ @import './styles/design-system.css';
3
+
1
4
  @tailwind base;
2
5
  @tailwind components;
3
6
  @tailwind utilities;
4
7
 
5
8
  @layer base {
6
9
  :root {
7
- /* HappyCapy-inspired cleaner "Milk/Cream" palette */
8
- --background: 40 20% 98%;
9
- /* Very light cream #F9F9F7 */
10
- --foreground: 30 15% 10%;
11
- /* Very dark brown/gray */
10
+ /* ========================================
11
+ CORE VARIABLES (Mapped to Design System)
12
+ ======================================== */
13
+ --background: 210 20% 98%;
14
+ --foreground: 221 39% 11%;
15
+
12
16
  --card: 0 0% 100%;
13
- /* Pure white cards */
14
- --card-foreground: 30 15% 10%;
17
+ --card-foreground: 221 39% 11%;
18
+
15
19
  --popover: 0 0% 100%;
16
- --popover-foreground: 30 15% 10%;
17
- --primary: 30 15% 10%;
18
- /* Dark active states */
20
+ --popover-foreground: 221 39% 11%;
21
+
22
+ /* Primary: Brand Blue */
23
+ --primary: 217 80% 55%;
19
24
  --primary-foreground: 0 0% 100%;
20
- --secondary: 40 10% 94%;
21
- /* Neutral light gray */
22
- --secondary-foreground: 30 15% 10%;
23
- --muted: 40 10% 94%;
24
- --muted-foreground: 30 8% 45%;
25
- --accent: 40 10% 92%;
26
- /* Light pill background */
27
- --accent-foreground: 30 15% 10%;
25
+
26
+ /* Secondary: Light Gray */
27
+ --secondary: 220 14% 96%;
28
+ --secondary-foreground: 215 28% 17%;
29
+
30
+ /* Muted */
31
+ --muted: 220 14% 96%;
32
+ --muted-foreground: 220 9% 46%;
33
+
34
+ /* Accent */
35
+ --accent: 217 100% 97%;
36
+ --accent-foreground: 217 70% 40%;
37
+
38
+ /* Destructive */
28
39
  --destructive: 0 84% 60%;
29
40
  --destructive-foreground: 0 0% 98%;
30
- --border: 40 10% 92%;
31
- --input: 40 10% 92%;
32
- --ring: 30 15% 10%;
33
- --radius: 1.25rem;
34
- /* Large rounded corners (20px) */
35
-
36
- /* HappyCapy Neutrals */
37
- --milk-50: 40 20% 98%;
38
- --milk-100: 40 15% 96%;
39
- --milk-200: 40 12% 92%;
40
- --milk-300: 40 10% 88%;
41
- --milk-400: 40 8% 70%;
42
- --milk-500: 30 8% 45%;
43
- --milk-600: 30 10% 35%;
44
- --milk-700: 30 12% 25%;
45
- --milk-800: 30 15% 15%;
46
- --milk-900: 30 20% 8%;
41
+
42
+ /* UI Elements */
43
+ --border: 220 13% 91%;
44
+ --input: 220 13% 91%;
45
+ --ring: 217 80% 55%;
46
+ --radius: 0.75rem;
47
+
48
+ /* Legacy compatibility */
49
+ --milk-50: 210 20% 98%;
50
+ --milk-100: 220 14% 96%;
51
+ --milk-200: 220 13% 91%;
52
+ --milk-300: 216 12% 84%;
53
+ --milk-400: 218 11% 65%;
54
+ --milk-500: 220 9% 46%;
55
+ --milk-600: 215 14% 34%;
56
+ --milk-700: 217 19% 27%;
57
+ --milk-800: 215 28% 17%;
58
+ --milk-900: 221 39% 11%;
47
59
  }
48
60
  }
49
61
 
50
62
  @layer base {
51
63
  * {
52
- @apply border-border outline-none transition-colors duration-200;
64
+ @apply border-border;
53
65
  }
54
66
 
55
67
  html {
@@ -60,20 +72,21 @@
60
72
 
61
73
  body {
62
74
  @apply bg-background text-foreground;
63
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
75
+ font-family: var(--font-sans);
64
76
  overflow: hidden;
65
77
  }
66
78
 
67
- /* Smooth scrolling for the whole page if needed */
79
+ /* Smooth scrolling */
68
80
  * {
69
81
  scrollbar-width: thin;
70
- scrollbar-color: hsl(var(--warm-gray-300)) transparent;
82
+ scrollbar-color: hsl(var(--gray-300)) transparent;
71
83
  }
72
84
  }
73
85
 
74
86
  @layer utilities {
75
-
76
- /* Custom scrollbar */
87
+ /* ========================================
88
+ SCROLLBAR
89
+ ======================================== */
77
90
  .custom-scrollbar::-webkit-scrollbar {
78
91
  width: 6px;
79
92
  height: 6px;
@@ -84,24 +97,42 @@
84
97
  }
85
98
 
86
99
  .custom-scrollbar::-webkit-scrollbar-thumb {
87
- background: hsl(var(--warm-gray-300));
100
+ background: hsl(var(--gray-300));
88
101
  border-radius: 6px;
89
102
  }
90
103
 
91
104
  .custom-scrollbar::-webkit-scrollbar-thumb:hover {
92
- background: hsl(var(--warm-gray-400));
105
+ background: hsl(var(--gray-400));
93
106
  }
94
107
 
95
- /* Glassmorphism */
108
+ /* ========================================
109
+ GLASSMORPHISM
110
+ ======================================== */
96
111
  .glass {
97
- @apply bg-white/70 backdrop-blur-md border border-white/20;
112
+ background: rgba(255, 255, 255, 0.8);
113
+ backdrop-filter: blur(12px);
114
+ -webkit-backdrop-filter: blur(12px);
115
+ border: 1px solid rgba(255, 255, 255, 0.2);
98
116
  }
99
117
 
100
118
  .glass-dark {
101
- @apply bg-black/10 backdrop-blur-md border border-white/10;
119
+ background: rgba(0, 0, 0, 0.1);
120
+ backdrop-filter: blur(12px);
121
+ -webkit-backdrop-filter: blur(12px);
122
+ border: 1px solid rgba(255, 255, 255, 0.1);
123
+ }
124
+
125
+ /* ========================================
126
+ SHADOWS
127
+ ======================================== */
128
+ .shadow-card {
129
+ box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.05), 0 1px 2px -1px rgb(0 0 0 / 0.05);
130
+ }
131
+
132
+ .shadow-card-hover {
133
+ box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.08), 0 4px 6px -4px rgb(0 0 0 / 0.05);
102
134
  }
103
135
 
104
- /* Premium Shadows */
105
136
  .shadow-premium {
106
137
  box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.08), 0 4px 10px -4px rgba(0, 0, 0, 0.04);
107
138
  }
@@ -109,15 +140,36 @@
109
140
  .shadow-premium-hover {
110
141
  box-shadow: 0 20px 40px -15px rgba(0, 0, 0, 0.12), 0 8px 15px -6px rgba(0, 0, 0, 0.06);
111
142
  }
143
+
144
+ /* ========================================
145
+ GRADIENTS
146
+ ======================================== */
147
+ .bg-gradient-hero {
148
+ background: linear-gradient(180deg, hsl(var(--background)) 0%, hsl(var(--gray-50)) 100%);
149
+ }
150
+
151
+ .bg-gradient-subtle {
152
+ background: linear-gradient(180deg, hsl(var(--gray-50)) 0%, hsl(var(--background)) 100%);
153
+ }
154
+
155
+ /* ========================================
156
+ FOCUS STATES
157
+ ======================================== */
158
+ .focus-ring {
159
+ @apply focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2;
160
+ --tw-ring-color: hsl(var(--ring));
161
+ --tw-ring-offset-color: hsl(var(--ring-offset));
162
+ }
112
163
  }
113
164
 
114
- /* Animation keyframes */
165
+ /* ========================================
166
+ ANIMATIONS
167
+ ======================================== */
115
168
  @keyframes fadeIn {
116
169
  from {
117
170
  opacity: 0;
118
171
  transform: translateY(12px);
119
172
  }
120
-
121
173
  to {
122
174
  opacity: 1;
123
175
  transform: translateY(0);
@@ -129,7 +181,6 @@
129
181
  opacity: 0;
130
182
  transform: translateX(-12px);
131
183
  }
132
-
133
184
  to {
134
185
  opacity: 1;
135
186
  transform: translateX(0);
@@ -141,7 +192,6 @@
141
192
  opacity: 0;
142
193
  transform: scale(0.97);
143
194
  }
144
-
145
195
  to {
146
196
  opacity: 1;
147
197
  transform: scale(1);
@@ -149,12 +199,9 @@
149
199
  }
150
200
 
151
201
  @keyframes pulse-soft {
152
-
153
- 0%,
154
- 100% {
202
+ 0%, 100% {
155
203
  opacity: 1;
156
204
  }
157
-
158
205
  50% {
159
206
  opacity: 0.8;
160
207
  }
@@ -174,4 +221,4 @@
174
221
 
175
222
  .animate-pulse-soft {
176
223
  animation: pulse-soft 3s ease-in-out infinite;
177
- }
224
+ }
package/src/lib/i18n.ts CHANGED
@@ -4,7 +4,6 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
4
4
  model: { zh: '模型', en: 'Model' },
5
5
  providers: { zh: '提供商', en: 'Providers' },
6
6
  channels: { zh: '渠道', en: 'Channels' },
7
- uiConfig: { zh: '界面', en: 'UI' },
8
7
 
9
8
  // Common
10
9
  enabled: { zh: '启用', en: 'Enabled' },
@@ -41,6 +40,7 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
41
40
  appToken: { zh: 'App Token', en: 'App Token' },
42
41
  appId: { zh: 'App ID', en: 'App ID' },
43
42
  appSecret: { zh: 'App Secret', en: 'App Secret' },
43
+ markdownSupport: { zh: 'Markdown 支持', en: 'Markdown Support' },
44
44
  clientId: { zh: 'Client ID', en: 'Client ID' },
45
45
  clientSecret: { zh: 'Client Secret', en: 'Client Secret' },
46
46
  encryptKey: { zh: '加密密钥', en: 'Encrypt Key' },
@@ -93,11 +93,7 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
93
93
  replyDelayMs: { zh: '回复延迟(ms)', en: 'Reply Delay (ms)' },
94
94
  secret: { zh: '密钥', en: 'Secret' },
95
95
 
96
- // UI Config
97
- host: { zh: '主机', en: 'Host' },
98
- port: { zh: '端口', en: 'Port' },
99
- open: { zh: '自动打开', en: 'Open Automatically' },
100
- reloadConfig: { zh: '重载配置', en: 'Reload Config' },
96
+ // UI
101
97
  saveVerifyConnect: { zh: '保存并验证 / 连接', en: 'Save & Verify / Connect' },
102
98
 
103
99
  // Status
@@ -108,6 +104,7 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
108
104
 
109
105
  // Messages
110
106
  configSaved: { zh: '配置已保存', en: 'Configuration saved' },
107
+ configSavedApplied: { zh: '配置已保存并已应用', en: 'Configuration saved and applied' },
111
108
  configSaveFailed: { zh: '保存配置失败', en: 'Failed to save configuration' },
112
109
  configReloaded: { zh: '配置已重载', en: 'Configuration reloaded' },
113
110
  configReloadFailed: { zh: '重载配置失败', en: 'Failed to reload configuration' },
@@ -0,0 +1,42 @@
1
+ type LogoMap = Record<string, string>;
2
+
3
+ const PROVIDER_LOGOS: LogoMap = {
4
+ openrouter: "openrouter.svg",
5
+ aihubmix: "aihubmix.png",
6
+ anthropic: "anthropic.svg",
7
+ openai: "openai.svg",
8
+ gemini: "gemini.svg",
9
+ deepseek: "deepseek.png",
10
+ zhipu: "zhipu.svg",
11
+ dashscope: "dashscope.png",
12
+ moonshot: "moonshot.png",
13
+ minimax: "minimax.svg",
14
+ vllm: "vllm.svg",
15
+ groq: "groq.svg"
16
+ };
17
+
18
+ const CHANNEL_LOGOS: LogoMap = {
19
+ telegram: "telegram.svg",
20
+ slack: "slack.svg",
21
+ discord: "discord.svg",
22
+ whatsapp: "whatsapp.svg",
23
+ qq: "qq.svg",
24
+ feishu: "feishu.svg",
25
+ dingtalk: "dingtalk.svg",
26
+ mochat: "mochat.svg",
27
+ email: "email.svg"
28
+ };
29
+
30
+ function resolveLogo(map: LogoMap, name: string): string | null {
31
+ const key = name.toLowerCase();
32
+ const file = map[key];
33
+ return file ? `/logos/${file}` : null;
34
+ }
35
+
36
+ export function getProviderLogo(name: string): string | null {
37
+ return resolveLogo(PROVIDER_LOGOS, name);
38
+ }
39
+
40
+ export function getChannelLogo(name: string): string | null {
41
+ return resolveLogo(CHANNEL_LOGOS, name);
42
+ }
@@ -4,7 +4,7 @@ type ConnectionStatus = 'connected' | 'disconnected' | 'connecting';
4
4
 
5
5
  interface UiState {
6
6
  // Active configuration tab
7
- activeTab: 'model' | 'providers' | 'channels' | 'ui';
7
+ activeTab: 'model' | 'providers' | 'channels';
8
8
  setActiveTab: (tab: UiState['activeTab']) => void;
9
9
 
10
10
  // Connection status
@@ -0,0 +1,248 @@
1
+ /**
2
+ * NextClaw Design System
3
+ * Inspired by modern SaaS landing pages (e.g., 推理时代)
4
+ * Clean, professional, with blue accent
5
+ */
6
+
7
+ @layer base {
8
+ :root {
9
+ /* ========================================
10
+ BRAND COLORS
11
+ ======================================== */
12
+ --brand-50: 217 100% 97%;
13
+ --brand-100: 217 100% 94%;
14
+ --brand-200: 217 95% 87%;
15
+ --brand-300: 217 90% 77%;
16
+ --brand-400: 217 85% 65%;
17
+ --brand-500: 217 80% 55%;
18
+ --brand-600: 217 75% 48%;
19
+ --brand-700: 217 70% 40%;
20
+ --brand-800: 217 65% 33%;
21
+ --brand-900: 217 60% 25%;
22
+
23
+ /* ========================================
24
+ NEUTRAL COLORS (Gray Scale)
25
+ ======================================== */
26
+ --gray-50: 210 20% 98%;
27
+ --gray-100: 220 14% 96%;
28
+ --gray-200: 220 13% 91%;
29
+ --gray-300: 216 12% 84%;
30
+ --gray-400: 218 11% 65%;
31
+ --gray-500: 220 9% 46%;
32
+ --gray-600: 215 14% 34%;
33
+ --gray-700: 217 19% 27%;
34
+ --gray-800: 215 28% 17%;
35
+ --gray-900: 221 39% 11%;
36
+
37
+ /* ========================================
38
+ SEMANTIC COLORS
39
+ ======================================== */
40
+ /* Background */
41
+ --background: 210 20% 98%;
42
+ --background-secondary: 220 14% 96%;
43
+ --background-tertiary: 220 13% 91%;
44
+
45
+ /* Foreground (Text) */
46
+ --foreground: 221 39% 11%;
47
+ --foreground-secondary: 215 28% 17%;
48
+ --foreground-tertiary: 220 9% 46%;
49
+ --foreground-muted: 218 11% 65%;
50
+
51
+ /* Primary (Brand Blue) */
52
+ --primary: 217 80% 55%;
53
+ --primary-hover: 217 75% 48%;
54
+ --primary-active: 217 70% 40%;
55
+ --primary-foreground: 0 0% 100%;
56
+
57
+ /* Secondary */
58
+ --secondary: 220 14% 96%;
59
+ --secondary-hover: 220 13% 91%;
60
+ --secondary-foreground: 215 28% 17%;
61
+
62
+ /* Accent */
63
+ --accent: 217 100% 97%;
64
+ --accent-foreground: 217 70% 40%;
65
+
66
+ /* Muted */
67
+ --muted: 220 14% 96%;
68
+ --muted-foreground: 220 9% 46%;
69
+
70
+ /* Destructive */
71
+ --destructive: 0 84% 60%;
72
+ --destructive-foreground: 0 0% 98%;
73
+
74
+ /* Success */
75
+ --success: 142 76% 36%;
76
+ --success-foreground: 0 0% 100%;
77
+
78
+ /* Warning */
79
+ --warning: 38 92% 50%;
80
+ --warning-foreground: 0 0% 100%;
81
+
82
+ /* ========================================
83
+ UI ELEMENTS
84
+ ======================================== */
85
+ /* Card */
86
+ --card: 0 0% 100%;
87
+ --card-foreground: 221 39% 11%;
88
+ --card-border: 220 13% 91%;
89
+
90
+ /* Popover */
91
+ --popover: 0 0% 100%;
92
+ --popover-foreground: 221 39% 11%;
93
+
94
+ /* Border */
95
+ --border: 220 13% 91%;
96
+ --border-hover: 216 12% 84%;
97
+ --border-active: 217 80% 55%;
98
+
99
+ /* Input */
100
+ --input: 0 0% 100%;
101
+ --input-border: 220 13% 91%;
102
+ --input-focus: 217 80% 55%;
103
+
104
+ /* Ring (Focus) */
105
+ --ring: 217 80% 55%;
106
+ --ring-offset: 0 0% 100%;
107
+
108
+ /* ========================================
109
+ SPACING SYSTEM
110
+ ======================================== */
111
+ --space-0: 0px;
112
+ --space-1: 4px;
113
+ --space-2: 8px;
114
+ --space-3: 12px;
115
+ --space-4: 16px;
116
+ --space-5: 20px;
117
+ --space-6: 24px;
118
+ --space-8: 32px;
119
+ --space-10: 40px;
120
+ --space-12: 48px;
121
+ --space-16: 64px;
122
+ --space-20: 80px;
123
+ --space-24: 96px;
124
+
125
+ /* ========================================
126
+ BORDER RADIUS
127
+ ======================================== */
128
+ --radius-sm: 6px;
129
+ --radius-md: 8px;
130
+ --radius-lg: 12px;
131
+ --radius-xl: 16px;
132
+ --radius-2xl: 20px;
133
+ --radius-3xl: 24px;
134
+ --radius-full: 9999px;
135
+
136
+ /* ========================================
137
+ SHADOWS
138
+ ======================================== */
139
+ --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
140
+ --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
141
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
142
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
143
+ --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
144
+ --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
145
+
146
+ /* Card Shadow */
147
+ --shadow-card: 0 1px 3px 0 rgb(0 0 0 / 0.05), 0 1px 2px -1px rgb(0 0 0 / 0.05);
148
+ --shadow-card-hover: 0 10px 15px -3px rgb(0 0 0 / 0.08), 0 4px 6px -4px rgb(0 0 0 / 0.05);
149
+
150
+ /* ========================================
151
+ TYPOGRAPHY
152
+ ======================================== */
153
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
154
+ --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
155
+
156
+ /* Font Sizes */
157
+ --text-xs: 12px;
158
+ --text-sm: 14px;
159
+ --text-base: 16px;
160
+ --text-lg: 18px;
161
+ --text-xl: 20px;
162
+ --text-2xl: 24px;
163
+ --text-3xl: 30px;
164
+ --text-4xl: 36px;
165
+ --text-5xl: 48px;
166
+
167
+ /* Font Weights */
168
+ --font-normal: 400;
169
+ --font-medium: 500;
170
+ --font-semibold: 600;
171
+ --font-bold: 700;
172
+
173
+ /* Line Heights */
174
+ --leading-tight: 1.25;
175
+ --leading-snug: 1.375;
176
+ --leading-normal: 1.5;
177
+ --leading-relaxed: 1.625;
178
+
179
+ /* Letter Spacing */
180
+ --tracking-tight: -0.025em;
181
+ --tracking-normal: 0;
182
+ --tracking-wide: 0.025em;
183
+
184
+ /* ========================================
185
+ TRANSITIONS
186
+ ======================================== */
187
+ --transition-fast: 150ms ease;
188
+ --transition-base: 200ms ease;
189
+ --transition-slow: 300ms ease;
190
+
191
+ /* ========================================
192
+ Z-INDEX SCALE
193
+ ======================================== */
194
+ --z-dropdown: 100;
195
+ --z-sticky: 200;
196
+ --z-fixed: 300;
197
+ --z-modal-backdrop: 400;
198
+ --z-modal: 500;
199
+ --z-popover: 600;
200
+ --z-tooltip: 700;
201
+ }
202
+ }
203
+
204
+ @layer utilities {
205
+ /* ========================================
206
+ UTILITY CLASSES
207
+ ======================================== */
208
+
209
+ /* Brand Colors */
210
+ .bg-brand-50 { background-color: hsl(var(--brand-50)); }
211
+ .bg-brand-100 { background-color: hsl(var(--brand-100)); }
212
+ .bg-brand-500 { background-color: hsl(var(--brand-500)); }
213
+ .bg-brand-600 { background-color: hsl(var(--brand-600)); }
214
+ .text-brand-500 { color: hsl(var(--brand-500)); }
215
+ .text-brand-600 { color: hsl(var(--brand-600)); }
216
+ .border-brand-500 { border-color: hsl(var(--brand-500)); }
217
+
218
+ /* Gray Colors */
219
+ .bg-gray-50 { background-color: hsl(var(--gray-50)); }
220
+ .bg-gray-100 { background-color: hsl(var(--gray-100)); }
221
+ .bg-gray-900 { background-color: hsl(var(--gray-900)); }
222
+ .text-gray-500 { color: hsl(var(--gray-500)); }
223
+ .text-gray-600 { color: hsl(var(--gray-600)); }
224
+ .text-gray-900 { color: hsl(var(--gray-900)); }
225
+
226
+ /* Shadows */
227
+ .shadow-card { box-shadow: var(--shadow-card); }
228
+ .shadow-card-hover { box-shadow: var(--shadow-card-hover); }
229
+
230
+ /* Gradient Backgrounds */
231
+ .bg-gradient-hero {
232
+ background: linear-gradient(180deg, hsl(var(--background)) 0%, hsl(var(--gray-50)) 100%);
233
+ }
234
+
235
+ /* Glass Effect */
236
+ .glass {
237
+ background: rgba(255, 255, 255, 0.8);
238
+ backdrop-filter: blur(12px);
239
+ -webkit-backdrop-filter: blur(12px);
240
+ }
241
+
242
+ /* Focus Ring */
243
+ .focus-ring {
244
+ @apply focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2;
245
+ --tw-ring-color: hsl(var(--ring));
246
+ --tw-ring-offset-color: hsl(var(--ring-offset));
247
+ }
248
+ }