bob-app-assistant 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +292 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +296 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/database.d.ts +42 -0
  7. package/dist/database.d.ts.map +1 -0
  8. package/dist/database.js +199 -0
  9. package/dist/database.js.map +1 -0
  10. package/dist/postinstall.d.ts +3 -0
  11. package/dist/postinstall.d.ts.map +1 -0
  12. package/dist/postinstall.js +48 -0
  13. package/dist/postinstall.js.map +1 -0
  14. package/dist/routes/aiRoutes.d.ts +3 -0
  15. package/dist/routes/aiRoutes.d.ts.map +1 -0
  16. package/dist/routes/aiRoutes.js +1277 -0
  17. package/dist/routes/aiRoutes.js.map +1 -0
  18. package/dist/server.d.ts +3 -0
  19. package/dist/server.d.ts.map +1 -0
  20. package/dist/server.js +120 -0
  21. package/dist/server.js.map +1 -0
  22. package/dist/services/apiTestingService.d.ts +90 -0
  23. package/dist/services/apiTestingService.d.ts.map +1 -0
  24. package/dist/services/apiTestingService.js +351 -0
  25. package/dist/services/apiTestingService.js.map +1 -0
  26. package/dist/services/backupService.d.ts +63 -0
  27. package/dist/services/backupService.d.ts.map +1 -0
  28. package/dist/services/backupService.js +235 -0
  29. package/dist/services/backupService.js.map +1 -0
  30. package/dist/services/bobShellService.d.ts +91 -0
  31. package/dist/services/bobShellService.d.ts.map +1 -0
  32. package/dist/services/bobShellService.js +534 -0
  33. package/dist/services/bobShellService.js.map +1 -0
  34. package/dist/services/codeQualityService.d.ts +105 -0
  35. package/dist/services/codeQualityService.d.ts.map +1 -0
  36. package/dist/services/codeQualityService.js +432 -0
  37. package/dist/services/codeQualityService.js.map +1 -0
  38. package/dist/services/dependencyService.d.ts +127 -0
  39. package/dist/services/dependencyService.d.ts.map +1 -0
  40. package/dist/services/dependencyService.js +499 -0
  41. package/dist/services/dependencyService.js.map +1 -0
  42. package/dist/services/diffService.d.ts +48 -0
  43. package/dist/services/diffService.d.ts.map +1 -0
  44. package/dist/services/diffService.js +175 -0
  45. package/dist/services/diffService.js.map +1 -0
  46. package/dist/services/documentationService.d.ts +124 -0
  47. package/dist/services/documentationService.d.ts.map +1 -0
  48. package/dist/services/documentationService.js +628 -0
  49. package/dist/services/documentationService.js.map +1 -0
  50. package/dist/services/promptEnhancerService.d.ts +92 -0
  51. package/dist/services/promptEnhancerService.d.ts.map +1 -0
  52. package/dist/services/promptEnhancerService.js +459 -0
  53. package/dist/services/promptEnhancerService.js.map +1 -0
  54. package/dist/services/securityService.d.ts +99 -0
  55. package/dist/services/securityService.d.ts.map +1 -0
  56. package/dist/services/securityService.js +467 -0
  57. package/dist/services/securityService.js.map +1 -0
  58. package/dist/websocket.d.ts +17 -0
  59. package/dist/websocket.d.ts.map +1 -0
  60. package/dist/websocket.js +242 -0
  61. package/dist/websocket.js.map +1 -0
  62. package/package.json +74 -0
  63. package/public/assistant-ui.html +211 -0
  64. package/public/dist/assets/index-BdGvFYpE.css +1 -0
  65. package/public/dist/assets/index-Dfe7xGmm.js +55 -0
  66. package/public/dist/index.html +13 -0
  67. package/public/inject.js +241 -0
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupWebSocketServer = setupWebSocketServer;
4
+ exports.broadcastToSession = broadcastToSession;
5
+ exports.broadcastToAll = broadcastToAll;
6
+ exports.getConnectedClients = getConnectedClients;
7
+ exports.isSessionConnected = isSessionConnected;
8
+ const ws_1 = require("ws");
9
+ const database_1 = require("./database");
10
+ const clients = new Map();
11
+ function setupWebSocketServer(wss) {
12
+ // Heartbeat to detect dead connections
13
+ const heartbeatInterval = setInterval(() => {
14
+ clients.forEach((client, sessionId) => {
15
+ if (!client.isAlive) {
16
+ console.log(`Terminating dead connection for session: ${sessionId}`);
17
+ client.ws.terminate();
18
+ clients.delete(sessionId);
19
+ return;
20
+ }
21
+ client.isAlive = false;
22
+ client.ws.ping();
23
+ });
24
+ }, 30000); // 30 seconds
25
+ wss.on('connection', (ws, req) => {
26
+ console.log('New WebSocket connection established');
27
+ let sessionId = null;
28
+ ws.on('pong', () => {
29
+ const client = sessionId ? clients.get(sessionId) : null;
30
+ if (client) {
31
+ client.isAlive = true;
32
+ }
33
+ });
34
+ ws.on('message', async (data) => {
35
+ try {
36
+ const message = JSON.parse(data.toString());
37
+ console.log('Received message:', message.type);
38
+ // Handle different message types
39
+ switch (message.type) {
40
+ case 'init':
41
+ await handleInit(ws, message);
42
+ sessionId = message.payload.sessionId;
43
+ if (sessionId) {
44
+ clients.set(sessionId, { ws, sessionId, isAlive: true });
45
+ }
46
+ break;
47
+ case 'prompt':
48
+ await handlePrompt(ws, message);
49
+ break;
50
+ case 'approval_response':
51
+ await handleApprovalResponse(ws, message);
52
+ break;
53
+ case 'state_sync':
54
+ await handleStateSync(ws, message);
55
+ break;
56
+ case 'chat_history_request':
57
+ await handleChatHistoryRequest(ws, message);
58
+ break;
59
+ case 'pending_approvals_request':
60
+ await handlePendingApprovalsRequest(ws, message);
61
+ break;
62
+ case 'clear_chat':
63
+ await handleClearChat(ws, message);
64
+ break;
65
+ default:
66
+ sendError(ws, `Unknown message type: ${message.type}`, message.requestId);
67
+ }
68
+ }
69
+ catch (error) {
70
+ console.error('Error handling WebSocket message:', error);
71
+ sendError(ws, error instanceof Error ? error.message : 'Unknown error');
72
+ }
73
+ });
74
+ ws.on('close', () => {
75
+ if (sessionId) {
76
+ console.log(`WebSocket connection closed for session: ${sessionId}`);
77
+ clients.delete(sessionId);
78
+ }
79
+ });
80
+ ws.on('error', (error) => {
81
+ console.error('WebSocket error:', error);
82
+ });
83
+ });
84
+ wss.on('close', () => {
85
+ clearInterval(heartbeatInterval);
86
+ });
87
+ }
88
+ // Message Handlers
89
+ async function handleInit(ws, message) {
90
+ const { sessionId, projectPath, settings } = message.payload;
91
+ // Create or update session
92
+ database_1.sessions.create(sessionId, projectPath, settings);
93
+ // Send initialization response
94
+ sendMessage(ws, {
95
+ type: 'init_response',
96
+ payload: {
97
+ success: true,
98
+ sessionId,
99
+ timestamp: Date.now()
100
+ },
101
+ requestId: message.requestId
102
+ });
103
+ // Send current state
104
+ const chatMessages = database_1.chatHistory.getBySession(sessionId, 50);
105
+ const pendingApprovalsList = database_1.pendingApprovals.getBySession(sessionId);
106
+ sendMessage(ws, {
107
+ type: 'state_sync',
108
+ payload: {
109
+ chatHistory: chatMessages.reverse(),
110
+ pendingApprovals: pendingApprovalsList
111
+ }
112
+ });
113
+ }
114
+ async function handlePrompt(ws, message) {
115
+ const { sessionId, prompt, mode } = message.payload;
116
+ // Store user message in chat history
117
+ database_1.chatHistory.add(sessionId, 'user', prompt);
118
+ // Update session activity
119
+ database_1.sessions.updateActivity(sessionId);
120
+ // Send acknowledgment
121
+ sendMessage(ws, {
122
+ type: 'prompt_received',
123
+ payload: {
124
+ success: true,
125
+ timestamp: Date.now()
126
+ },
127
+ requestId: message.requestId
128
+ });
129
+ // Note: Actual AI processing will be handled by the HTTP API
130
+ // This just manages the WebSocket communication
131
+ }
132
+ async function handleApprovalResponse(ws, message) {
133
+ const { approvalId, approved, sessionId } = message.payload;
134
+ // Update approval status
135
+ const status = approved ? 'approved' : 'rejected';
136
+ database_1.pendingApprovals.resolve(approvalId, status);
137
+ // Send confirmation
138
+ sendMessage(ws, {
139
+ type: 'approval_response_received',
140
+ payload: {
141
+ success: true,
142
+ approvalId,
143
+ status
144
+ },
145
+ requestId: message.requestId
146
+ });
147
+ // Broadcast updated pending approvals to all clients in this session
148
+ broadcastToSession(sessionId, {
149
+ type: 'pending_approvals_updated',
150
+ payload: {
151
+ pendingApprovals: database_1.pendingApprovals.getBySession(sessionId)
152
+ }
153
+ });
154
+ }
155
+ async function handleStateSync(ws, message) {
156
+ const { sessionId } = message.payload;
157
+ const chatMessages = database_1.chatHistory.getBySession(sessionId, 50);
158
+ const pendingApprovalsList = database_1.pendingApprovals.getBySession(sessionId);
159
+ sendMessage(ws, {
160
+ type: 'state_sync',
161
+ payload: {
162
+ chatHistory: chatMessages.reverse(),
163
+ pendingApprovals: pendingApprovalsList
164
+ },
165
+ requestId: message.requestId
166
+ });
167
+ }
168
+ async function handleChatHistoryRequest(ws, message) {
169
+ const { sessionId, limit } = message.payload;
170
+ const chatMessages = database_1.chatHistory.getBySession(sessionId, limit || 50);
171
+ sendMessage(ws, {
172
+ type: 'chat_history_response',
173
+ payload: {
174
+ chatHistory: chatMessages.reverse()
175
+ },
176
+ requestId: message.requestId
177
+ });
178
+ }
179
+ async function handlePendingApprovalsRequest(ws, message) {
180
+ const { sessionId } = message.payload;
181
+ const pendingApprovalsList = database_1.pendingApprovals.getBySession(sessionId);
182
+ sendMessage(ws, {
183
+ type: 'pending_approvals_response',
184
+ payload: {
185
+ pendingApprovals: pendingApprovalsList
186
+ },
187
+ requestId: message.requestId
188
+ });
189
+ }
190
+ async function handleClearChat(ws, message) {
191
+ const { sessionId } = message.payload;
192
+ database_1.chatHistory.clear(sessionId);
193
+ sendMessage(ws, {
194
+ type: 'chat_cleared',
195
+ payload: {
196
+ success: true
197
+ },
198
+ requestId: message.requestId
199
+ });
200
+ // Broadcast to all clients in this session
201
+ broadcastToSession(sessionId, {
202
+ type: 'chat_history_updated',
203
+ payload: {
204
+ chatHistory: []
205
+ }
206
+ });
207
+ }
208
+ // Utility Functions
209
+ function sendMessage(ws, message) {
210
+ if (ws.readyState === ws_1.WebSocket.OPEN) {
211
+ ws.send(JSON.stringify(message));
212
+ }
213
+ }
214
+ function sendError(ws, error, requestId) {
215
+ sendMessage(ws, {
216
+ type: 'error',
217
+ payload: { error },
218
+ requestId
219
+ });
220
+ }
221
+ function broadcastToSession(sessionId, message) {
222
+ clients.forEach((client) => {
223
+ if (client.sessionId === sessionId && client.ws.readyState === ws_1.WebSocket.OPEN) {
224
+ client.ws.send(JSON.stringify(message));
225
+ }
226
+ });
227
+ }
228
+ function broadcastToAll(message) {
229
+ clients.forEach((client) => {
230
+ if (client.ws.readyState === ws_1.WebSocket.OPEN) {
231
+ client.ws.send(JSON.stringify(message));
232
+ }
233
+ });
234
+ }
235
+ function getConnectedClients() {
236
+ return Array.from(clients.keys());
237
+ }
238
+ function isSessionConnected(sessionId) {
239
+ return clients.has(sessionId);
240
+ }
241
+ // Made with Bob
242
+ //# sourceMappingURL=websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.js","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "bob-app-assistant",
3
+ "version": "0.0.1",
4
+ "description": "BobAppAssistant - Independent AI-powered development assistant that can be injected into any web application",
5
+ "main": "dist/server.js",
6
+ "bin": {
7
+ "BobAppAssistant": "./dist/cli.js"
8
+ },
9
+ "scripts": {
10
+ "dev": "tsx watch src/server.ts",
11
+ "build": "npm run build:iframe && tsc",
12
+ "build:iframe": "cd iframe-app && npm run build",
13
+ "start": "node dist/server.js",
14
+ "clean": "rm -rf dist",
15
+ "prepublishOnly": "npm run build",
16
+ "postinstall": "node dist/postinstall.js || echo 'Postinstall script not found'"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "public",
21
+ "iframe-app/dist",
22
+ "README.md",
23
+ "LICENSE",
24
+ ".env.example"
25
+ ],
26
+ "keywords": [
27
+ "ai",
28
+ "assistant",
29
+ "bob",
30
+ "websocket",
31
+ "bob-shell",
32
+ "code-assistant",
33
+ "ai-tools",
34
+ "developer-tools",
35
+ "ai-development",
36
+ "code-generation"
37
+ ],
38
+ "author": "Bob AI",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/yourusername/BobAppAssistant.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/yourusername/BobAppAssistant/issues"
46
+ },
47
+ "homepage": "https://github.com/yourusername/BobAppAssistant#readme",
48
+ "engines": {
49
+ "node": ">=18.0.0"
50
+ },
51
+ "dependencies": {
52
+ "@types/multer": "^2.1.0",
53
+ "axios": "^1.13.6",
54
+ "better-sqlite3": "^9.2.2",
55
+ "cors": "^2.8.5",
56
+ "diff": "^5.2.2",
57
+ "dotenv": "^16.3.1",
58
+ "express": "^4.18.2",
59
+ "multer": "^2.1.1",
60
+ "uuid": "^9.0.1",
61
+ "ws": "^8.14.2"
62
+ },
63
+ "devDependencies": {
64
+ "@types/better-sqlite3": "^7.6.8",
65
+ "@types/cors": "^2.8.17",
66
+ "@types/diff": "^5.2.3",
67
+ "@types/express": "^4.17.21",
68
+ "@types/node": "^20.10.6",
69
+ "@types/uuid": "^9.0.7",
70
+ "@types/ws": "^8.5.10",
71
+ "tsx": "^4.7.0",
72
+ "typescript": "^5.3.3"
73
+ }
74
+ }
@@ -0,0 +1,211 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI Assistant</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
16
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
17
+ sans-serif;
18
+ -webkit-font-smoothing: antialiased;
19
+ -moz-osx-font-smoothing: grayscale;
20
+ background: #f9fafb;
21
+ height: 100vh;
22
+ overflow: hidden;
23
+ }
24
+
25
+ #root {
26
+ width: 100%;
27
+ height: 100%;
28
+ }
29
+
30
+ .loading {
31
+ display: flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ height: 100vh;
35
+ flex-direction: column;
36
+ gap: 16px;
37
+ }
38
+
39
+ .spinner {
40
+ width: 40px;
41
+ height: 40px;
42
+ border: 4px solid #e5e7eb;
43
+ border-top-color: #3b82f6;
44
+ border-radius: 50%;
45
+ animation: spin 1s linear infinite;
46
+ }
47
+
48
+ @keyframes spin {
49
+ to { transform: rotate(360deg); }
50
+ }
51
+
52
+ .error {
53
+ padding: 20px;
54
+ background: #fee;
55
+ border: 1px solid #fcc;
56
+ border-radius: 8px;
57
+ color: #c00;
58
+ margin: 20px;
59
+ }
60
+ </style>
61
+ </head>
62
+ <body>
63
+ <div id="root">
64
+ <div class="loading">
65
+ <div class="spinner"></div>
66
+ <p>Loading AI Assistant...</p>
67
+ </div>
68
+ </div>
69
+
70
+ <script>
71
+ // WebSocket bridge to parent window
72
+ let parentWs = null;
73
+
74
+ // Listen for messages from parent (injection script)
75
+ window.addEventListener('message', (event) => {
76
+ const { source, message } = event.data;
77
+
78
+ if (source === 'sidecar-websocket') {
79
+ // Handle WebSocket messages from sidecar
80
+ handleSidecarMessage(message);
81
+ }
82
+ });
83
+
84
+ // Send message to parent (which forwards to WebSocket)
85
+ function sendToSidecar(message) {
86
+ window.parent.postMessage({
87
+ source: 'ai-assistant-iframe',
88
+ message
89
+ }, '*');
90
+ }
91
+
92
+ // Handle messages from sidecar
93
+ function handleSidecarMessage(message) {
94
+ console.log('Received from sidecar:', message.type);
95
+
96
+ switch (message.type) {
97
+ case 'init_response':
98
+ console.log('Session initialized:', message.payload.sessionId);
99
+ break;
100
+
101
+ case 'state_sync':
102
+ console.log('State synced:', message.payload);
103
+ // Update UI with chat history and pending approvals
104
+ updateUI(message.payload);
105
+ break;
106
+
107
+ case 'prompt_received':
108
+ console.log('Prompt acknowledged');
109
+ break;
110
+
111
+ case 'error':
112
+ console.error('Sidecar error:', message.payload.error);
113
+ showError(message.payload.error);
114
+ break;
115
+ }
116
+ }
117
+
118
+ // Update UI with state
119
+ function updateUI(state) {
120
+ const root = document.getElementById('root');
121
+
122
+ // Simple UI for now - will be replaced with React app
123
+ root.innerHTML = `
124
+ <div style="padding: 20px;">
125
+ <h2 style="margin-bottom: 16px;">AI Assistant</h2>
126
+
127
+ <div style="margin-bottom: 20px;">
128
+ <h3 style="margin-bottom: 8px;">Chat History</h3>
129
+ <div style="background: white; border: 1px solid #e5e7eb; border-radius: 8px; padding: 12px; max-height: 300px; overflow-y: auto;">
130
+ ${state.chatHistory && state.chatHistory.length > 0
131
+ ? state.chatHistory.map(msg => `
132
+ <div style="margin-bottom: 12px; padding: 8px; background: ${msg.role === 'user' ? '#eff6ff' : '#f9fafb'}; border-radius: 4px;">
133
+ <strong>${msg.role}:</strong> ${msg.content.substring(0, 100)}${msg.content.length > 100 ? '...' : ''}
134
+ </div>
135
+ `).join('')
136
+ : '<p style="color: #6b7280;">No messages yet</p>'
137
+ }
138
+ </div>
139
+ </div>
140
+
141
+ <div style="margin-bottom: 20px;">
142
+ <h3 style="margin-bottom: 8px;">Pending Approvals</h3>
143
+ <div style="background: white; border: 1px solid #e5e7eb; border-radius: 8px; padding: 12px;">
144
+ ${state.pendingApprovals && state.pendingApprovals.length > 0
145
+ ? `<p style="color: #dc2626;">${state.pendingApprovals.length} pending approval(s)</p>`
146
+ : '<p style="color: #6b7280;">No pending approvals</p>'
147
+ }
148
+ </div>
149
+ </div>
150
+
151
+ <div>
152
+ <textarea
153
+ id="prompt-input"
154
+ placeholder="Enter your prompt..."
155
+ style="width: 100%; height: 100px; padding: 12px; border: 1px solid #e5e7eb; border-radius: 8px; resize: vertical; font-family: inherit;"
156
+ ></textarea>
157
+ <button
158
+ onclick="sendPrompt()"
159
+ style="margin-top: 8px; padding: 10px 20px; background: #3b82f6; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 500;"
160
+ >
161
+ Send Prompt
162
+ </button>
163
+ </div>
164
+
165
+ <div style="margin-top: 20px; padding: 12px; background: #fef3c7; border: 1px solid #fbbf24; border-radius: 8px;">
166
+ <p style="font-size: 14px; color: #92400e;">
167
+ <strong>Note:</strong> This is a simplified UI. The full React app will be integrated here.
168
+ </p>
169
+ </div>
170
+ </div>
171
+ `;
172
+ }
173
+
174
+ // Send prompt to sidecar
175
+ window.sendPrompt = function() {
176
+ const input = document.getElementById('prompt-input');
177
+ const prompt = input.value.trim();
178
+
179
+ if (!prompt) {
180
+ alert('Please enter a prompt');
181
+ return;
182
+ }
183
+
184
+ sendToSidecar({
185
+ type: 'prompt',
186
+ payload: {
187
+ sessionId: localStorage.getItem('ai-assistant-session-id'),
188
+ prompt,
189
+ mode: 'code'
190
+ }
191
+ });
192
+
193
+ input.value = '';
194
+ };
195
+
196
+ // Show error
197
+ function showError(error) {
198
+ const root = document.getElementById('root');
199
+ root.innerHTML = `
200
+ <div class="error">
201
+ <h3>Error</h3>
202
+ <p>${error}</p>
203
+ </div>
204
+ `;
205
+ }
206
+
207
+ // Initialize
208
+ console.log('AI Assistant iframe initialized');
209
+ </script>
210
+ </body>
211
+ </html>
@@ -0,0 +1 @@
1
+ *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.-right-2{right:-.5rem}.-top-2{top:-.5rem}.z-50{z-index:50}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-4{margin-left:1rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-12{height:3rem}.h-16{height:4rem}.h-3{height:.75rem}.h-32{height:8rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-64{max-height:16rem}.max-h-96{max-height:24rem}.max-h-\[80vh\]{max-height:80vh}.max-h-\[90vh\]{max-height:90vh}.w-11{width:2.75rem}.w-12{width:3rem}.w-16{width:4rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-6xl{max-width:72rem}.max-w-none{max-width:none}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.translate-x-1{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-6{--tw-translate-x: 1.5rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.list-disc{list-style-type:disc}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(229 231 235 / var(--tw-divide-opacity, 1))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-amber-200{--tw-border-opacity: 1;border-color:rgb(253 230 138 / var(--tw-border-opacity, 1))}.border-blue-200{--tw-border-opacity: 1;border-color:rgb(191 219 254 / var(--tw-border-opacity, 1))}.border-blue-600{--tw-border-opacity: 1;border-color:rgb(37 99 235 / var(--tw-border-opacity, 1))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.border-gray-800{--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity, 1))}.border-orange-200{--tw-border-opacity: 1;border-color:rgb(254 215 170 / var(--tw-border-opacity, 1))}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-yellow-200{--tw-border-opacity: 1;border-color:rgb(254 240 138 / var(--tw-border-opacity, 1))}.bg-amber-50{--tw-bg-opacity: 1;background-color:rgb(255 251 235 / var(--tw-bg-opacity, 1))}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-blue-900{--tw-bg-opacity: 1;background-color:rgb(30 58 138 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-300{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.bg-green-900{--tw-bg-opacity: 1;background-color:rgb(20 83 45 / var(--tw-bg-opacity, 1))}.bg-indigo-100{--tw-bg-opacity: 1;background-color:rgb(224 231 255 / var(--tw-bg-opacity, 1))}.bg-indigo-600{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity, 1))}.bg-orange-50{--tw-bg-opacity: 1;background-color:rgb(255 247 237 / var(--tw-bg-opacity, 1))}.bg-purple-100{--tw-bg-opacity: 1;background-color:rgb(243 232 255 / var(--tw-bg-opacity, 1))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.bg-red-900{--tw-bg-opacity: 1;background-color:rgb(127 29 29 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-50{--tw-bg-opacity: 1;background-color:rgb(254 252 232 / var(--tw-bg-opacity, 1))}.bg-opacity-20{--tw-bg-opacity: .2}.bg-opacity-30{--tw-bg-opacity: .3}.bg-opacity-50{--tw-bg-opacity: .5}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-indigo-600{--tw-gradient-from: #4f46e5 var(--tw-gradient-from-position);--tw-gradient-to: rgb(79 70 229 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-purple-600{--tw-gradient-to: #9333ea var(--tw-gradient-to-position)}.object-cover{-o-object-fit:cover;object-fit:cover}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-5xl{font-size:3rem;line-height:1}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.text-amber-800{--tw-text-opacity: 1;color:rgb(146 64 14 / var(--tw-text-opacity, 1))}.text-blue-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-blue-800{--tw-text-opacity: 1;color:rgb(30 64 175 / var(--tw-text-opacity, 1))}.text-gray-100{--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-300{--tw-text-opacity: 1;color:rgb(134 239 172 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-indigo-100{--tw-text-opacity: 1;color:rgb(224 231 255 / var(--tw-text-opacity, 1))}.text-indigo-700{--tw-text-opacity: 1;color:rgb(67 56 202 / var(--tw-text-opacity, 1))}.text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity, 1))}.text-orange-700{--tw-text-opacity: 1;color:rgb(194 65 12 / var(--tw-text-opacity, 1))}.text-purple-700{--tw-text-opacity: 1;color:rgb(126 34 206 / var(--tw-text-opacity, 1))}.text-red-300{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.text-yellow-600{--tw-text-opacity: 1;color:rgb(202 138 4 / var(--tw-text-opacity, 1))}.text-yellow-700{--tw-text-opacity: 1;color:rgb(161 98 7 / var(--tw-text-opacity, 1))}.text-yellow-800{--tw-text-opacity: 1;color:rgb(133 77 14 / var(--tw-text-opacity, 1))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}.hover\:border-gray-300:hover{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.hover\:border-indigo-500:hover{--tw-border-opacity: 1;border-color:rgb(99 102 241 / var(--tw-border-opacity, 1))}.hover\:bg-blue-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\:bg-green-700:hover{--tw-bg-opacity: 1;background-color:rgb(21 128 61 / var(--tw-bg-opacity, 1))}.hover\:bg-indigo-200:hover{--tw-bg-opacity: 1;background-color:rgb(199 210 254 / var(--tw-bg-opacity, 1))}.hover\:bg-indigo-700:hover{--tw-bg-opacity: 1;background-color:rgb(67 56 202 / var(--tw-bg-opacity, 1))}.hover\:bg-purple-200:hover{--tw-bg-opacity: 1;background-color:rgb(233 213 255 / var(--tw-bg-opacity, 1))}.hover\:bg-red-700:hover{--tw-bg-opacity: 1;background-color:rgb(185 28 28 / var(--tw-bg-opacity, 1))}.hover\:bg-white\/10:hover{background-color:#ffffff1a}.hover\:from-indigo-700:hover{--tw-gradient-from: #4338ca var(--tw-gradient-from-position);--tw-gradient-to: rgb(67 56 202 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.hover\:to-purple-700:hover{--tw-gradient-to: #7e22ce var(--tw-gradient-to-position)}.hover\:text-gray-600:hover{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.hover\:text-gray-800:hover{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.focus\:border-transparent:focus{border-color:transparent}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.focus\:ring-indigo-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity, 1))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-100{opacity:1}@media (min-width: 768px){.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}