astrogators-shared-ui 0.5.0 → 0.6.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.
- package/README.md +137 -203
- package/dist/index.css +2 -0
- package/dist/index.js +744 -805
- package/dist/{components → src/components}/display/Badge.d.ts +0 -1
- package/dist/{components → src/components}/display/Card.d.ts +0 -1
- package/dist/{components → src/components}/display/Modal.d.ts +0 -1
- package/dist/{components → src/components}/feedback/Loader.d.ts +0 -1
- package/dist/{components → src/components}/forms/AllyCodeDropdown.d.ts +0 -1
- package/dist/{components → src/components}/forms/Button.d.ts +0 -1
- package/dist/{components → src/components}/forms/Input.d.ts +0 -1
- package/dist/{components → src/components}/forms/Select.d.ts +0 -1
- package/dist/{components → src/components}/layout/Container.d.ts +0 -1
- package/dist/{components → src/components}/layout/Footer.d.ts +0 -1
- package/dist/{components → src/components}/layout/TopBar.d.ts +0 -1
- package/dist/{contexts → src/contexts}/AuthContext.d.ts +0 -1
- package/dist/{index.d.ts → src/index.d.ts} +0 -1
- package/package.json +18 -13
- package/dist/style.css +0 -1
- /package/dist/{components → src/components}/display/index.d.ts +0 -0
- /package/dist/{components → src/components}/feedback/index.d.ts +0 -0
- /package/dist/{components → src/components}/forms/index.d.ts +0 -0
- /package/dist/{components → src/components}/layout/index.d.ts +0 -0
- /package/dist/{services → src/services}/allyCodeStorage.d.ts +0 -0
- /package/dist/{services → src/services}/api.d.ts +0 -0
- /package/dist/{services → src/services}/auth.d.ts +0 -0
- /package/dist/{types → src/types}/api.d.ts +0 -0
- /package/dist/{types → src/types}/index.d.ts +0 -0
- /package/dist/{types → src/types}/mod.d.ts +0 -0
- /package/dist/{types → src/types}/user.d.ts +0 -0
- /package/dist/{utils → src/utils}/formatAllyCode.d.ts +0 -0
package/README.md
CHANGED
|
@@ -1,172 +1,114 @@
|
|
|
1
|
-
#
|
|
1
|
+
# astrogators-shared-ui
|
|
2
2
|
|
|
3
|
-
Shared
|
|
3
|
+
Shared React components, auth, and API client for the Astrogator's Table
|
|
4
|
+
frontends (`astrogators-hub`, `mod-ledger-ui`, `nightwatcher-ui`). Published
|
|
5
|
+
to the public npm registry as `astrogators-shared-ui` (unscoped).
|
|
6
|
+
|
|
7
|
+
> The git repo lives under `psytor/astrogators-shared-ui` on GitHub, but the
|
|
8
|
+
> package itself is published to **npmjs.org**, not GitHub Packages. No
|
|
9
|
+
> `.npmrc` scope config is needed to install.
|
|
10
|
+
|
|
11
|
+
This is a library — there is no app shell here. See `PUBLISHING.md` for the
|
|
12
|
+
release flow and `CLAUDE.md` for architecture notes.
|
|
4
13
|
|
|
5
14
|
## Installation
|
|
6
15
|
|
|
7
16
|
```bash
|
|
8
|
-
npm install
|
|
17
|
+
npm install astrogators-shared-ui
|
|
9
18
|
```
|
|
10
19
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
### 1. Initialize API Client
|
|
20
|
+
Peer dependencies: `react` and `react-dom` (18 or 19).
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
```tsx
|
|
18
|
-
import { initializeApiClient } from '@psytor/astrogators-shared-ui';
|
|
22
|
+
## Setup
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
baseURL: 'http://localhost:8000', // Your backend API URL
|
|
22
|
-
onUnauthorized: () => {
|
|
23
|
-
// Handle unauthorized (e.g., redirect to login)
|
|
24
|
-
window.location.href = '/login';
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
```
|
|
24
|
+
### 1. Wrap the app in `AuthProvider`
|
|
28
25
|
|
|
29
|
-
|
|
26
|
+
`AuthProvider` initializes the API client with the given `apiBaseUrl` and
|
|
27
|
+
manages auth, feature flags, and ally codes for the whole app. Pass the
|
|
28
|
+
**prefixed** backend URL — workspace backends mount their routes under
|
|
29
|
+
`/<service-name>` (see the workspace `CLAUDE.md`).
|
|
30
30
|
|
|
31
31
|
```tsx
|
|
32
|
-
import { AuthProvider } from '
|
|
32
|
+
import { AuthProvider } from 'astrogators-shared-ui';
|
|
33
|
+
import 'astrogators-shared-ui/styles';
|
|
33
34
|
|
|
34
35
|
function App() {
|
|
35
36
|
return (
|
|
36
|
-
<AuthProvider>
|
|
37
|
-
{/*
|
|
37
|
+
<AuthProvider apiBaseUrl={import.meta.env.VITE_API_BASE_URL}>
|
|
38
|
+
{/* your app */}
|
|
38
39
|
</AuthProvider>
|
|
39
40
|
);
|
|
40
41
|
}
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
```tsx
|
|
46
|
-
import '@psytor/astrogators-shared-ui/styles';
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Components
|
|
44
|
+
`VITE_API_BASE_URL` looks like `http://localhost:8000/astrogators-table` in
|
|
45
|
+
dev.
|
|
50
46
|
|
|
51
|
-
###
|
|
47
|
+
### 2. (Optional) Reconfigure the API client
|
|
52
48
|
|
|
53
|
-
|
|
49
|
+
`AuthProvider` already calls `initializeApiClient`. Call it yourself only if
|
|
50
|
+
you need a custom `onUnauthorized` handler (e.g. router-driven redirects):
|
|
54
51
|
|
|
55
52
|
```tsx
|
|
56
|
-
import {
|
|
53
|
+
import { initializeApiClient } from 'astrogators-shared-ui';
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
initializeApiClient({
|
|
56
|
+
baseURL: import.meta.env.VITE_API_BASE_URL,
|
|
57
|
+
onUnauthorized: () => navigate('/login'),
|
|
58
|
+
});
|
|
62
59
|
```
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
```tsx
|
|
67
|
-
import { Container } from '@psytor/astrogators-shared-ui';
|
|
61
|
+
## Components
|
|
68
62
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
```
|
|
63
|
+
All components are styled via CSS Modules and the global design tokens in
|
|
64
|
+
`./styles`. Override the design system by redefining CSS variables on `:root`
|
|
65
|
+
(see "Theming").
|
|
73
66
|
|
|
74
|
-
|
|
67
|
+
### Layout
|
|
75
68
|
|
|
76
69
|
```tsx
|
|
77
|
-
import { Footer } from '
|
|
70
|
+
import { TopBar, Container, Footer } from 'astrogators-shared-ui';
|
|
78
71
|
|
|
72
|
+
<TopBar logo={<Logo />} rightContent={<UserMenu />} />
|
|
73
|
+
<Container maxWidth="lg" padding>{children}</Container>
|
|
79
74
|
<Footer />
|
|
80
75
|
```
|
|
81
76
|
|
|
82
|
-
###
|
|
83
|
-
|
|
84
|
-
#### Button
|
|
77
|
+
### Forms
|
|
85
78
|
|
|
86
79
|
```tsx
|
|
87
|
-
import { Button } from '
|
|
80
|
+
import { Button, Input, Select, AllyCodeDropdown } from 'astrogators-shared-ui';
|
|
88
81
|
|
|
89
|
-
<Button variant="primary" size="md"
|
|
90
|
-
|
|
91
|
-
|
|
82
|
+
<Button variant="primary" size="md" loading={submitting}>Save</Button>
|
|
83
|
+
<Input label="Email" type="email" required error={errors.email} />
|
|
84
|
+
<Select label="Profile" options={profiles} placeholder="Choose…" />
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
86
|
+
// Wired into useAuth — manages the user's ally codes (DB-backed when
|
|
87
|
+
// authenticated, localStorage when anonymous):
|
|
88
|
+
<AllyCodeDropdown />
|
|
96
89
|
```
|
|
97
90
|
|
|
98
|
-
|
|
91
|
+
`Button` variants: `primary | secondary | outline | ghost | danger`.
|
|
99
92
|
|
|
100
|
-
|
|
101
|
-
import { Input } from '@psytor/astrogators-shared-ui';
|
|
102
|
-
|
|
103
|
-
<Input
|
|
104
|
-
label="Email"
|
|
105
|
-
type="email"
|
|
106
|
-
placeholder="Enter your email"
|
|
107
|
-
required
|
|
108
|
-
error={errors.email}
|
|
109
|
-
/>
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
#### Select
|
|
93
|
+
### Display
|
|
113
94
|
|
|
114
95
|
```tsx
|
|
115
|
-
import {
|
|
116
|
-
|
|
117
|
-
<Select
|
|
118
|
-
label="Choose Profile"
|
|
119
|
-
options={[
|
|
120
|
-
{ value: 'standard', label: 'Standard' },
|
|
121
|
-
{ value: 'speed', label: 'Speed Focus' },
|
|
122
|
-
]}
|
|
123
|
-
placeholder="Select a profile"
|
|
124
|
-
/>
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Display Components
|
|
128
|
-
|
|
129
|
-
#### Card
|
|
130
|
-
|
|
131
|
-
```tsx
|
|
132
|
-
import { Card } from '@psytor/astrogators-shared-ui';
|
|
96
|
+
import { Card, Badge, Modal } from 'astrogators-shared-ui';
|
|
133
97
|
|
|
134
98
|
<Card variant="elevated" chamfered chamferSize="md" padding="lg" hoverable>
|
|
135
|
-
|
|
136
|
-
<p>Analyze your mods</p>
|
|
99
|
+
…
|
|
137
100
|
</Card>
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
#### Badge
|
|
141
|
-
|
|
142
|
-
```tsx
|
|
143
|
-
import { Badge } from '@psytor/astrogators-shared-ui';
|
|
144
101
|
|
|
145
102
|
<Badge variant="success">Active</Badge>
|
|
146
103
|
<Badge variant="warning" size="sm">Beta</Badge>
|
|
147
|
-
```
|
|
148
104
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
```tsx
|
|
152
|
-
import { Modal } from '@psytor/astrogators-shared-ui';
|
|
153
|
-
|
|
154
|
-
<Modal
|
|
155
|
-
isOpen={isOpen}
|
|
156
|
-
onClose={() => setIsOpen(false)}
|
|
157
|
-
title="Login"
|
|
158
|
-
size="md"
|
|
159
|
-
>
|
|
160
|
-
{/* Modal content */}
|
|
161
|
-
</Modal>
|
|
105
|
+
<Modal isOpen={open} onClose={close} title="Login" size="md">…</Modal>
|
|
162
106
|
```
|
|
163
107
|
|
|
164
|
-
### Feedback
|
|
165
|
-
|
|
166
|
-
#### Loader
|
|
108
|
+
### Feedback
|
|
167
109
|
|
|
168
110
|
```tsx
|
|
169
|
-
import { Loader } from '
|
|
111
|
+
import { Loader } from 'astrogators-shared-ui';
|
|
170
112
|
|
|
171
113
|
<Loader size="md" variant="spinner" />
|
|
172
114
|
<Loader size="lg" variant="dots" />
|
|
@@ -174,82 +116,96 @@ import { Loader } from '@psytor/astrogators-shared-ui';
|
|
|
174
116
|
|
|
175
117
|
## Authentication
|
|
176
118
|
|
|
177
|
-
|
|
119
|
+
`useAuth` returns the full auth + ally-code surface. The hook must be called
|
|
120
|
+
inside `AuthProvider`.
|
|
178
121
|
|
|
179
122
|
```tsx
|
|
180
|
-
import { useAuth } from '
|
|
123
|
+
import { useAuth } from 'astrogators-shared-ui';
|
|
181
124
|
|
|
182
|
-
|
|
183
|
-
|
|
125
|
+
const {
|
|
126
|
+
// session
|
|
127
|
+
user, isAuthenticated, isLoading,
|
|
128
|
+
login, register, logout, refreshUser,
|
|
129
|
+
forgotPassword, resetPassword, resendVerification,
|
|
184
130
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
await login({
|
|
188
|
-
email: 'user@example.com',
|
|
189
|
-
password: 'password',
|
|
190
|
-
});
|
|
191
|
-
} catch (error) {
|
|
192
|
-
console.error('Login failed:', error);
|
|
193
|
-
}
|
|
194
|
-
};
|
|
131
|
+
// backend feature flags (e.g. auth_enabled)
|
|
132
|
+
authEnabled, isLoadingFeatures,
|
|
195
133
|
|
|
196
|
-
|
|
134
|
+
// ally codes — DB-backed when logged in, localStorage when anonymous
|
|
135
|
+
allyCodes, selectedAllyCode, isLoadingAllyCodes,
|
|
136
|
+
fetchAllyCodes, addAllyCode, removeAllyCode,
|
|
137
|
+
selectAllyCode, updateAllyCodeLastUsed,
|
|
197
138
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
<p>Welcome, {user?.username}!</p>
|
|
203
|
-
<button onClick={logout}>Logout</button>
|
|
204
|
-
</div>
|
|
205
|
-
) : (
|
|
206
|
-
<button onClick={handleLogin}>Login</button>
|
|
207
|
-
)}
|
|
208
|
-
</div>
|
|
209
|
-
);
|
|
210
|
-
}
|
|
139
|
+
// localStorage → DB migration prompt for users who sign up after
|
|
140
|
+
// adding ally codes anonymously
|
|
141
|
+
migrationPrompt, dismissMigrationPrompt, migrateLocalStorageCodes,
|
|
142
|
+
} = useAuth();
|
|
211
143
|
```
|
|
212
144
|
|
|
213
|
-
|
|
145
|
+
Tokens are stored in `localStorage`. Registration does **not** auto-login —
|
|
146
|
+
it requires email verification.
|
|
214
147
|
|
|
215
|
-
|
|
148
|
+
## API client
|
|
216
149
|
|
|
217
150
|
```tsx
|
|
218
|
-
import { apiClient } from '
|
|
151
|
+
import { apiClient } from 'astrogators-shared-ui';
|
|
219
152
|
|
|
220
|
-
|
|
221
|
-
const data = await apiClient.get('/api/v1/game-data/characters');
|
|
222
|
-
|
|
223
|
-
// POST request
|
|
153
|
+
const characters = await apiClient.get('/api/v1/game-data/characters');
|
|
224
154
|
const result = await apiClient.post('/api/v1/mod-ledger/evaluate/123456789', {
|
|
225
155
|
profile_name: 'standard',
|
|
226
156
|
});
|
|
227
157
|
```
|
|
228
158
|
|
|
229
|
-
The
|
|
230
|
-
-
|
|
231
|
-
-
|
|
232
|
-
|
|
233
|
-
-
|
|
159
|
+
The client:
|
|
160
|
+
- injects the access token into `Authorization`
|
|
161
|
+
- on `401`, transparently refreshes via `/api/v1/auth/refresh-token` and
|
|
162
|
+
retries the original request once
|
|
163
|
+
- calls `onUnauthorized` if refresh fails
|
|
164
|
+
|
|
165
|
+
Endpoints are written **without** the service prefix — the prefix lives in
|
|
166
|
+
the configured `baseURL`.
|
|
234
167
|
|
|
235
|
-
##
|
|
168
|
+
## Ally code utilities
|
|
236
169
|
|
|
237
|
-
|
|
170
|
+
For UI that needs to format/validate the 9-digit SWGOH player IDs outside the
|
|
171
|
+
context of `useAuth`:
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import {
|
|
175
|
+
formatAllyCode, // "123456789" → "123-456-789"
|
|
176
|
+
unformatAllyCode, // "123-456-789" → "123456789"
|
|
177
|
+
getAllyCodesFromStorage,
|
|
178
|
+
saveAllyCodeToStorage,
|
|
179
|
+
removeAllyCodeFromStorage,
|
|
180
|
+
getSelectedAllyCode,
|
|
181
|
+
setSelectedAllyCode,
|
|
182
|
+
clearAllyCodes,
|
|
183
|
+
} from 'astrogators-shared-ui';
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Prefer `useAuth` when you can — it keeps DB and localStorage in sync.
|
|
187
|
+
|
|
188
|
+
## TypeScript
|
|
189
|
+
|
|
190
|
+
All public types are re-exported from the package root, including:
|
|
238
191
|
|
|
239
192
|
```tsx
|
|
240
193
|
import type {
|
|
241
|
-
User,
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
ModEvaluation,
|
|
246
|
-
|
|
247
|
-
} from '@psytor/astrogators-shared-ui';
|
|
194
|
+
User, LoginRequest, LoginResponse,
|
|
195
|
+
RegisterRequest, ForgotPasswordRequest, ResetPasswordRequest,
|
|
196
|
+
AllyCode, AllyCodeCreate, AllyCodeListResponse, StoredAllyCode,
|
|
197
|
+
ApiResponse, ApiError, PaginatedResponse,
|
|
198
|
+
ParsedMod, ModStat, ModEvaluation, EvaluationRequest, EvaluationResponse,
|
|
199
|
+
} from 'astrogators-shared-ui';
|
|
248
200
|
```
|
|
249
201
|
|
|
250
|
-
|
|
202
|
+
Consumers should set `"moduleResolution": "bundler"` (or `"node16"`) in
|
|
203
|
+
`tsconfig.json` so the bundled `.d.ts` files resolve.
|
|
251
204
|
|
|
252
|
-
|
|
205
|
+
## Theming
|
|
206
|
+
|
|
207
|
+
Design tokens are CSS variables on `:root`. Override anything you need in
|
|
208
|
+
your own stylesheet, loaded after the library styles:
|
|
253
209
|
|
|
254
210
|
```css
|
|
255
211
|
:root {
|
|
@@ -261,57 +217,35 @@ Customize the design system by overriding CSS variables:
|
|
|
261
217
|
}
|
|
262
218
|
```
|
|
263
219
|
|
|
264
|
-
|
|
220
|
+
### Chamfered boxes
|
|
265
221
|
|
|
266
|
-
|
|
222
|
+
Sci-fi cut-corner effect, available as utility classes or via `Card`:
|
|
267
223
|
|
|
268
224
|
```tsx
|
|
269
|
-
<div className="chamfered-box">
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
<div className="chamfered-box-lg">
|
|
274
|
-
{/* Large chamfered corners */}
|
|
275
|
-
</div>
|
|
225
|
+
<div className="chamfered-box">…</div>
|
|
226
|
+
<div className="chamfered-box-sm">…</div>
|
|
227
|
+
<div className="chamfered-box-lg">…</div>
|
|
276
228
|
|
|
277
|
-
<
|
|
278
|
-
{/* Small chamfered corners */}
|
|
279
|
-
</div>
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
Or use the Card component:
|
|
283
|
-
|
|
284
|
-
```tsx
|
|
285
|
-
<Card chamfered chamferSize="lg">
|
|
286
|
-
{/* Content */}
|
|
287
|
-
</Card>
|
|
229
|
+
<Card chamfered chamferSize="lg">…</Card>
|
|
288
230
|
```
|
|
289
231
|
|
|
290
232
|
## Development
|
|
291
233
|
|
|
292
234
|
```bash
|
|
293
|
-
# Install dependencies
|
|
294
235
|
npm install
|
|
295
|
-
|
|
296
|
-
#
|
|
297
|
-
npm run build
|
|
298
|
-
|
|
299
|
-
# Type check
|
|
300
|
-
npm run type-check
|
|
236
|
+
npm run build # tsc && vite build → dist/
|
|
237
|
+
npm run type-check # tsc --noEmit
|
|
301
238
|
```
|
|
302
239
|
|
|
303
|
-
|
|
240
|
+
There is no `dev` server worth running (this is a library, not an app).
|
|
241
|
+
Iterate by rebuilding and reinstalling in a consumer.
|
|
304
242
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
npm
|
|
311
|
-
|
|
312
|
-
# Publish to GitHub Packages
|
|
313
|
-
npm publish
|
|
314
|
-
```
|
|
243
|
+
See `PUBLISHING.md` for the release procedure. Two non-negotiable rules:
|
|
244
|
+
- **Always `npm run build` before `npm publish`** — `dist/` is gitignored
|
|
245
|
+
but is the only thing shipped, so skipping the build re-publishes stale
|
|
246
|
+
code.
|
|
247
|
+
- **`npm login` and `npm publish` must be run by the user**, not by Claude
|
|
248
|
+
— publishing needs interactive npm auth.
|
|
315
249
|
|
|
316
250
|
## License
|
|
317
251
|
|
package/dist/index.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
:root{--color-primary:#1a73e8;--color-primary-hover:#1557b0;--color-primary-active:#0d3c78;--color-secondary:#34a853;--color-secondary-hover:#2d8e47;--color-secondary-active:#1f6631;--color-background:#0a0e27;--color-background-elevated:#131729;--color-background-hover:#1a1f3a;--color-surface:#1e2139;--color-border:#2a2f4a;--color-text-primary:#fff;--color-text-secondary:#a0a5c0;--color-text-disabled:#5a5f7a;--color-success:#34a853;--color-warning:#fbbc04;--color-error:#ea4335;--color-info:#4285f4;--tier-grey:#c0eeee;--tier-green:#47c43c;--tier-blue:#1d99ff;--tier-purple:#8b5cf6;--tier-gold:#fc3;--tier-gold-glow:#fc39;--tier-purple-glow:#8b5cf680;--tier-blue-glow:#1d99ff80;--sixdot-gradient-start:#60a5fa;--sixdot-gradient-mid:#a855f7;--sixdot-gradient-end:#f59e0b;--sixdot-border-opacity:.3;--font-primary:-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-heading:"Orbitron", var(--font-primary);--font-mono:"Fira Code", "Courier New", monospace;--font-size-xs:.75rem;--font-size-sm:.875rem;--font-size-base:1rem;--font-size-lg:1.125rem;--font-size-xl:1.25rem;--font-size-2xl:1.5rem;--font-size-3xl:1.875rem;--font-size-4xl:2.25rem;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--line-height-tight:1.25;--line-height-normal:1.5;--line-height-relaxed:1.75;--spacing-xs:.25rem;--spacing-sm:.5rem;--spacing-md:1rem;--spacing-lg:1.5rem;--spacing-xl:2rem;--spacing-2xl:3rem;--spacing-3xl:4rem;--radius-sm:.25rem;--radius-md:.5rem;--radius-lg:.75rem;--radius-xl:1rem;--radius-full:9999px;--shadow-sm:0 1px 2px 0 #0000004d;--shadow-md:0 4px 6px -1px #0006, 0 2px 4px -1px #0000004d;--shadow-lg:0 10px 15px -3px #00000080, 0 4px 6px -2px #0006;--shadow-xl:0 20px 25px -5px #0009, 0 10px 10px -5px #00000080;--z-base:0;--z-dropdown:100;--z-sticky:200;--z-modal:300;--z-toast:400;--z-tooltip:500;--chamfer-size:8px;--chamfer-size-lg:12px;--chamfer-size-sm:4px;--transition-fast:.15s ease-in-out;--transition-base:.2s ease-in-out;--transition-slow:.3s ease-in-out;--topbar-height:64px;--topbar-background:var(--color-background-elevated);--topbar-border:var(--color-border);--container-max-width:1280px;--container-padding:var(--spacing-md);--button-height:40px;--button-padding-x:var(--spacing-lg);--button-padding-y:var(--spacing-sm);--input-height:40px;--input-padding-x:var(--spacing-md);--input-padding-y:var(--spacing-sm);--input-border:var(--color-border);--input-background:var(--color-surface);--card-padding:var(--spacing-lg);--card-background:var(--color-surface);--card-border:var(--color-border)}*,:before,:after{box-sizing:border-box}*{margin:0;padding:0}html,body{height:100%}html{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{line-height:var(--line-height-normal);font-family:var(--font-primary);font-size:var(--font-size-base);color:var(--color-text-primary);background-color:var(--color-background)}img,picture,video,canvas,svg{max-width:100%;display:block}input,button,textarea,select{font:inherit;color:inherit}button{cursor:pointer;background:0 0;border:none}p,h1,h2,h3,h4,h5,h6{overflow-wrap:break-word}ul,ol{list-style:none}a{color:inherit;text-decoration:none}#root{isolation:isolate;min-height:100%}.chamfered-box{clip-path:polygon(var(--chamfer-size) 0, calc(100% - var(--chamfer-size)) 0, 100% var(--chamfer-size), 100% calc(100% - var(--chamfer-size)), calc(100% - var(--chamfer-size)) 100%, var(--chamfer-size) 100%, 0 calc(100% - var(--chamfer-size)), 0 var(--chamfer-size));position:relative}.chamfered-box-lg{clip-path:polygon(var(--chamfer-size-lg) 0, calc(100% - var(--chamfer-size-lg)) 0, 100% var(--chamfer-size-lg), 100% calc(100% - var(--chamfer-size-lg)), calc(100% - var(--chamfer-size-lg)) 100%, var(--chamfer-size-lg) 100%, 0 calc(100% - var(--chamfer-size-lg)), 0 var(--chamfer-size-lg));position:relative}.chamfered-box-sm{clip-path:polygon(var(--chamfer-size-sm) 0, calc(100% - var(--chamfer-size-sm)) 0, 100% var(--chamfer-size-sm), 100% calc(100% - var(--chamfer-size-sm)), calc(100% - var(--chamfer-size-sm)) 100%, var(--chamfer-size-sm) 100%, 0 calc(100% - var(--chamfer-size-sm)), 0 var(--chamfer-size-sm));position:relative}.chamfered-box-asymmetric{clip-path:polygon(12px 0%,calc(100% - 12px) 0%,100% 12px,100% calc(100% - 24px),calc(100% - 24px) 100%,12px 100%,0% calc(100% - 12px),0% 12px);position:relative}.chamfered-border{background:var(--color-surface);position:relative}.chamfered-border:before{content:"";background:linear-gradient(135deg, var(--color-primary), var(--color-secondary));-webkit-mask-composite:xor;clip-path:polygon(var(--chamfer-size) 0, calc(100% - var(--chamfer-size)) 0, 100% var(--chamfer-size), 100% calc(100% - var(--chamfer-size)), calc(100% - var(--chamfer-size)) 100%, var(--chamfer-size) 100%, 0 calc(100% - var(--chamfer-size)), 0 var(--chamfer-size));padding:2px;position:absolute;inset:0;-webkit-mask-image:linear-gradient(#fff 0 0),linear-gradient(#fff 0 0);-webkit-mask-position:0 0,0 0;-webkit-mask-size:auto,auto;-webkit-mask-repeat:repeat,repeat;-webkit-mask-clip:content-box,border-box;-webkit-mask-origin:content-box,border-box;-webkit-mask-composite:xor;mask-composite:exclude;-webkit-mask-source-type:auto,auto;mask-mode:match-source,match-source}.chamfered-diagonal-border{z-index:5;pointer-events:none;background-color:currentColor;transition:background-color .3s;position:absolute}.chamfered-diagonal-tl{transform-origin:0;width:16px;height:2px;top:-1px;left:11px;transform:rotate(135deg)}.chamfered-diagonal-tr{transform-origin:100%;width:18px;height:2px;top:10px;right:0;transform:rotate(45deg)}.chamfered-diagonal-bl{transform-origin:0;width:20px;height:2px;bottom:-2px;left:12px;transform:rotate(-135deg)}.chamfered-diagonal-br{transform-origin:100%;width:18px;height:2px;bottom:11px;right:-1px;transform:rotate(-45deg)}.chamfered-diagonal-br-large{transform-origin:100%;width:35px;height:2px;bottom:23px;right:-1px;transform:rotate(-45deg)}.gradient-primary{background:linear-gradient(135deg, var(--color-primary), var(--color-primary-active))}.gradient-secondary{background:linear-gradient(135deg, var(--color-secondary), var(--color-secondary-active))}.gradient-surface{background:linear-gradient(135deg, var(--color-surface), var(--color-background-elevated))}.glow-primary{box-shadow:0 0 20px #1a73e880}.glow-secondary{box-shadow:0 0 20px #34a85380}.glow-error{box-shadow:0 0 20px #ea433580}.glow-tier-gold{box-shadow:0 0 20px var(--tier-gold-glow)}.glow-tier-purple{box-shadow:0 0 20px var(--tier-purple-glow)}.glow-tier-blue{box-shadow:0 0 20px var(--tier-blue-glow)}.sixdot-border{position:relative}.sixdot-border:before{content:"";background:linear-gradient(45deg, var(--sixdot-gradient-start), var(--sixdot-gradient-mid), var(--sixdot-gradient-end));border-radius:inherit;opacity:var(--sixdot-border-opacity);z-index:-1;position:absolute;inset:-2px}.sixdot-glow{background:linear-gradient(135deg,#4338ca33,#7c3aed33)}.hover-scale{transition:transform var(--transition-base)}.hover-scale:hover{transform:scale(1.02)}.hover-brighten{transition:filter var(--transition-base)}.hover-brighten:hover{filter:brightness(1.1)}.hover-lift{transition:transform var(--transition-base)}.hover-lift:hover{transform:translateY(-2px)}.hover-glow{transition:box-shadow var(--transition-base)}.hover-glow:hover{box-shadow:0 0 20px #1a73e866}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.animate-fade-in{animation:fadeIn var(--transition-base)}.animate-slide-up{animation:slideUp var(--transition-base)}.animate-slide-down{animation:slideDown var(--transition-base)}.animate-spin{animation:1s linear infinite spin}.flex{display:flex}.flex-col{flex-direction:column;display:flex}.flex-center{justify-content:center;align-items:center;display:flex}.flex-between{justify-content:space-between;align-items:center;display:flex}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.grid{display:grid}.grid-cols-2{grid-template-columns:repeat(2,1fr)}.grid-cols-3{grid-template-columns:repeat(3,1fr)}.grid-cols-4{grid-template-columns:repeat(4,1fr)}.gap-sm{gap:var(--spacing-sm)}.gap-md{gap:var(--spacing-md)}.gap-lg{gap:var(--spacing-lg)}.gap-xl{gap:var(--spacing-xl)}.m-0{margin:0}.mt-sm{margin-top:var(--spacing-sm)}.mt-md{margin-top:var(--spacing-md)}.mt-lg{margin-top:var(--spacing-lg)}.mt-xl{margin-top:var(--spacing-xl)}.mb-sm{margin-bottom:var(--spacing-sm)}.mb-md{margin-bottom:var(--spacing-md)}.mb-lg{margin-bottom:var(--spacing-lg)}.mb-xl{margin-bottom:var(--spacing-xl)}.p-sm{padding:var(--spacing-sm)}.p-md{padding:var(--spacing-md)}.p-lg{padding:var(--spacing-lg)}.p-xl{padding:var(--spacing-xl)}.text-xs{font-size:var(--font-size-xs)}.text-sm{font-size:var(--font-size-sm)}.text-base{font-size:var(--font-size-base)}.text-lg{font-size:var(--font-size-lg)}.text-xl{font-size:var(--font-size-xl)}.text-2xl{font-size:var(--font-size-2xl)}.text-3xl{font-size:var(--font-size-3xl)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-normal{font-weight:var(--font-weight-normal)}.font-medium{font-weight:var(--font-weight-medium)}.font-semibold{font-weight:var(--font-weight-semibold)}.font-bold{font-weight:var(--font-weight-bold)}.text-primary{color:var(--color-text-primary)}.text-secondary{color:var(--color-text-secondary)}.text-disabled{color:var(--color-text-disabled)}.bg-primary{background-color:var(--color-primary)}.bg-secondary{background-color:var(--color-secondary)}.bg-surface{background-color:var(--color-surface)}.bg-elevated{background-color:var(--color-background-elevated)}.hidden{display:none}.block{display:block}.inline-block{display:inline-block}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.w-full{width:100%}.w-auto{width:auto}.h-full{height:100%}.h-auto{height:auto}.overflow-hidden{overflow:hidden}.overflow-auto{overflow:auto}.overflow-scroll{overflow:scroll}.cursor-pointer{cursor:pointer}.cursor-not-allowed{cursor:not-allowed}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--color-background)}::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:var(--radius-full)}::-webkit-scrollbar-thumb:hover{background:var(--color-text-disabled)}:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.bg-overlay{position:relative}.bg-overlay:before{content:"";background:var(--overlay-gradient,linear-gradient(135deg, #0f172ab3, #0f172a80));z-index:0;position:absolute;inset:0}.bg-overlay>*{z-index:1;position:relative}.backdrop-image{background-position:50%;background-repeat:no-repeat;background-size:cover;position:absolute;inset:0}._container_u3o0e_1{width:100%;margin-left:auto;margin-right:auto}._padding_u3o0e_7{padding-left:var(--container-padding);padding-right:var(--container-padding)}._maxWidth-sm_u3o0e_12{max-width:640px}._maxWidth-md_u3o0e_16{max-width:768px}._maxWidth-lg_u3o0e_20{max-width:1024px}._maxWidth-xl_u3o0e_24{max-width:var(--container-max-width)}._maxWidth-full_u3o0e_28{max-width:100%}@media (width<=768px){._padding_u3o0e_7{padding-left:var(--spacing-md);padding-right:var(--spacing-md)}}._topBar_10g3c_1{height:var(--topbar-height);background-color:var(--topbar-background);border-bottom:1px solid var(--topbar-border);z-index:var(--z-sticky);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);position:sticky;top:0;left:0;right:0}._content_10g3c_13{height:100%;max-width:var(--container-max-width);padding:0 var(--container-padding);justify-content:space-between;align-items:center;margin:0 auto;display:flex}._left_10g3c_23{align-items:center;gap:var(--spacing-xl);flex:1;display:flex}._right_10g3c_30{align-items:center;gap:var(--spacing-md);display:flex}._logo_10g3c_36{font-size:var(--font-size-xl);font-weight:var(--font-weight-bold);color:var(--color-primary);font-family:var(--font-heading);align-items:center;display:flex}@media (width<=768px){._content_10g3c_13{padding:0 var(--spacing-md)}._left_10g3c_23{gap:var(--spacing-md)}._right_10g3c_30{gap:var(--spacing-sm)}}._footer_zjuj8_1{background-color:var(--color-background-elevated);border-top:1px solid var(--color-border);margin-top:auto}._content_zjuj8_7{max-width:var(--container-max-width);padding:var(--spacing-xl) var(--container-padding);margin:0 auto}._defaultContent_zjuj8_13{align-items:center;gap:var(--spacing-sm);text-align:center;flex-direction:column;display:flex}._copyright_zjuj8_21{font-size:var(--font-size-sm);color:var(--color-text-secondary)}._disclaimer_zjuj8_26{font-size:var(--font-size-xs);color:var(--color-text-disabled)}@media (width<=768px){._content_zjuj8_7{padding:var(--spacing-lg) var(--spacing-md)}}._button_159ms_1{justify-content:center;align-items:center;gap:var(--spacing-sm);font-family:var(--font-primary);font-weight:var(--font-weight-medium);border-radius:var(--radius-md);cursor:pointer;transition:all var(--transition-base);text-align:center;-webkit-user-select:none;user-select:none;border:2px solid #0000;display:inline-flex}._button_159ms_1:disabled{opacity:.5;cursor:not-allowed}._sm_159ms_22{height:32px;padding:0 var(--spacing-md);font-size:var(--font-size-sm)}._md_159ms_28{height:var(--button-height);padding:0 var(--button-padding-x);font-size:var(--font-size-base)}._lg_159ms_34{height:48px;padding:0 var(--spacing-xl);font-size:var(--font-size-lg)}._primary_159ms_41{background-color:var(--color-primary);color:var(--color-text-primary)}._primary_159ms_41:hover:not(:disabled){background-color:var(--color-primary-hover);box-shadow:var(--shadow-md)}._primary_159ms_41:active:not(:disabled){background-color:var(--color-primary-active)}._secondary_159ms_55{background-color:var(--color-secondary);color:var(--color-text-primary)}._secondary_159ms_55:hover:not(:disabled){background-color:var(--color-secondary-hover);box-shadow:var(--shadow-md)}._secondary_159ms_55:active:not(:disabled){background-color:var(--color-secondary-active)}._outline_159ms_69{border-color:var(--color-border);color:var(--color-text-primary);background-color:#0000}._outline_159ms_69:hover:not(:disabled){background-color:var(--color-background-hover);border-color:var(--color-primary)}._ghost_159ms_80{color:var(--color-text-primary);background-color:#0000}._ghost_159ms_80:hover:not(:disabled){background-color:var(--color-background-hover)}._danger_159ms_89{background-color:var(--color-error);color:var(--color-text-primary)}._danger_159ms_89:hover:not(:disabled){box-shadow:var(--shadow-md);background-color:#c33a2e}._fullWidth_159ms_100{width:100%}._loading_159ms_105{pointer-events:none;position:relative}._loadingSpinner_159ms_110{align-items:center;gap:var(--spacing-sm);display:flex}._spinner_159ms_116{border:2px solid #0000;border-top-color:currentColor;border-radius:50%;width:16px;height:16px;animation:.6s linear infinite _spin_159ms_116}@keyframes _spin_159ms_116{to{transform:rotate(360deg)}}._loadingText_159ms_131{opacity:.7}._inputWrapper_15t4g_1{gap:var(--spacing-xs);flex-direction:column;display:flex}._fullWidth_15t4g_7{width:100%}._label_15t4g_11{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary);align-items:center;gap:var(--spacing-xs);display:flex}._required_15t4g_20{color:var(--color-error)}._input_15t4g_1{height:var(--input-height);padding:var(--input-padding-y) var(--input-padding-x);font-size:var(--font-size-base);font-family:var(--font-primary);color:var(--color-text-primary);background-color:var(--input-background);border:1px solid var(--input-border);border-radius:var(--radius-md);transition:all var(--transition-base);outline:none}._input_15t4g_1:hover{border-color:var(--color-text-disabled)}._input_15t4g_1:focus{border-color:var(--color-primary);box-shadow:0 0 0 3px #1a73e81a}._input_15t4g_1:disabled{opacity:.5;cursor:not-allowed}._input_15t4g_1::placeholder{color:var(--color-text-disabled)}._input_15t4g_1._error_15t4g_55{border-color:var(--color-error)}._input_15t4g_1._error_15t4g_55:focus{box-shadow:0 0 0 3px #ea43351a}._errorText_15t4g_63{font-size:var(--font-size-sm);color:var(--color-error)}._helperText_15t4g_68{font-size:var(--font-size-sm);color:var(--color-text-secondary)}._selectWrapper_ir4bq_1{gap:var(--spacing-xs);flex-direction:column;display:flex}._fullWidth_ir4bq_7{width:100%}._label_ir4bq_11{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary);align-items:center;gap:var(--spacing-xs);display:flex}._required_ir4bq_20{color:var(--color-error)}._selectContainer_ir4bq_24{width:100%;display:inline-block;position:relative}._select_ir4bq_1{width:100%;height:var(--input-height);padding:var(--input-padding-y) var(--input-padding-x);padding-right:calc(var(--input-padding-x) + 24px);font-size:var(--font-size-base);font-family:var(--font-primary);color:var(--color-text-primary);background-color:var(--input-background);border:1px solid var(--input-border);border-radius:var(--radius-md);transition:all var(--transition-base);cursor:pointer;appearance:none;outline:none}._select_ir4bq_1:hover{border-color:var(--color-text-disabled)}._select_ir4bq_1:focus{border-color:var(--color-primary);box-shadow:0 0 0 3px #1a73e81a}._select_ir4bq_1:disabled{opacity:.5;cursor:not-allowed}._select_ir4bq_1._error_ir4bq_63{border-color:var(--color-error)}._select_ir4bq_1._error_ir4bq_63:focus{box-shadow:0 0 0 3px #ea43351a}._arrow_ir4bq_71{right:var(--input-padding-x);pointer-events:none;font-size:var(--font-size-xs);color:var(--color-text-secondary);position:absolute;top:50%;transform:translateY(-50%)}._errorText_ir4bq_81{font-size:var(--font-size-sm);color:var(--color-error)}._helperText_ir4bq_86{font-size:var(--font-size-sm);color:var(--color-text-secondary)}._select_ir4bq_1 option{background-color:var(--color-surface);color:var(--color-text-primary)}._allyCodeDropdown_1m0tw_1{align-items:center;gap:.5rem;display:flex;position:relative}._select_1m0tw_8{min-width:200px}._manageButton_1m0tw_12{white-space:nowrap}._managePanel_1m0tw_16{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);z-index:1000;min-width:300px;margin-top:.5rem;padding:1rem;position:absolute;top:100%;right:0;box-shadow:0 4px 6px #0000001a}._managePanel_1m0tw_16 h4{color:var(--color-text-primary);margin:0 0 1rem;font-size:.875rem;font-weight:600}._addSection_1m0tw_37{border-bottom:1px solid var(--color-border);margin-bottom:1rem;padding-bottom:1rem}._addInputGroup_1m0tw_43{gap:.5rem;margin-bottom:.5rem;display:flex}._addInputGroup_1m0tw_43 input{letter-spacing:.05em;flex:1;font-family:monospace}._error_1m0tw_55{color:var(--color-error);margin-top:.25rem;font-size:.75rem}._success_1m0tw_61{color:var(--color-success);margin-top:.25rem;font-size:.75rem}._codesList_1m0tw_67{margin-bottom:1rem}._codeItem_1m0tw_71{border-bottom:1px solid var(--color-border);justify-content:space-between;align-items:center;padding:.5rem 0;display:flex}._codeItem_1m0tw_71:last-of-type{border-bottom:none}._codeItem_1m0tw_71 span{color:var(--color-text-secondary);font-size:.875rem}._doneButton_1m0tw_88{width:100%}@media (width<=768px){._select_1m0tw_8{min-width:150px}._managePanel_1m0tw_16{left:0;right:auto}}._card_b4471_1{background-color:var(--card-background);border-radius:var(--radius-lg);transition:transform var(--transition-base), box-shadow var(--transition-base)}._card_b4471_1._chamfered_b4471_8{border-radius:0}._default_b4471_13{background-color:var(--card-background)}._elevated_b4471_17{background-color:var(--card-background);box-shadow:var(--shadow-md)}._outline_b4471_22{border:1px solid var(--card-border);background-color:#0000}._padding-none_b4471_28{padding:0}._padding-sm_b4471_32{padding:var(--spacing-sm)}._padding-md_b4471_36{padding:var(--card-padding)}._padding-lg_b4471_40{padding:var(--spacing-xl)}._hoverable_b4471_45{cursor:pointer;will-change:transform;backface-visibility:hidden;transform:translateZ(0)}._hoverable_b4471_45:hover{box-shadow:var(--shadow-lg);transform:translateY(-4px)translateZ(0)}._hoverable_b4471_45:active{box-shadow:var(--shadow-md);transform:translateY(-1px)translateZ(0)}._card_b4471_1[role=button]:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}._badge_rk7t5_1{font-weight:var(--font-weight-medium);border-radius:var(--radius-full);white-space:nowrap;justify-content:center;align-items:center;display:inline-flex}._sm_rk7t5_11{font-size:var(--font-size-xs);padding:2px 8px}._md_rk7t5_16{font-size:var(--font-size-sm);padding:4px 12px}._lg_rk7t5_21{font-size:var(--font-size-base);padding:6px 16px}._default_rk7t5_27{background-color:var(--color-surface);color:var(--color-text-secondary)}._primary_rk7t5_32{background-color:var(--color-primary);color:var(--color-text-primary)}._secondary_rk7t5_37{background-color:var(--color-secondary);color:var(--color-text-primary)}._success_rk7t5_42{background-color:var(--color-success);color:var(--color-text-primary)}._warning_rk7t5_47{background-color:var(--color-warning);color:var(--color-background)}._error_rk7t5_52{background-color:var(--color-error);color:var(--color-text-primary)}._info_rk7t5_57{background-color:var(--color-info);color:var(--color-text-primary)}._overlay_1b3yt_1{z-index:var(--z-modal);padding:var(--spacing-md);animation:_fadeIn_1b3yt_1 var(--transition-base);background-color:#000000bf;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}._modal_1b3yt_13{background-color:var(--color-surface);border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);max-height:90vh;animation:_slideUp_1b3yt_1 var(--transition-base);flex-direction:column;display:flex;overflow:hidden}._sm_1b3yt_25{width:100%;max-width:400px}._md_1b3yt_30{width:100%;max-width:600px}._lg_1b3yt_35{width:100%;max-width:800px}._xl_1b3yt_40{width:100%;max-width:1200px}._header_1b3yt_45{padding:var(--spacing-lg);border-bottom:1px solid var(--color-border);justify-content:space-between;align-items:center;display:flex}._title_1b3yt_53{font-size:var(--font-size-xl);font-weight:var(--font-weight-semibold);color:var(--color-text-primary);margin:0}._closeButton_1b3yt_60{border-radius:var(--radius-md);width:32px;height:32px;color:var(--color-text-secondary);transition:all var(--transition-fast);font-size:var(--font-size-xl);justify-content:center;align-items:center;line-height:1;display:flex}._closeButton_1b3yt_60:hover{background-color:var(--color-background-hover);color:var(--color-text-primary)}._content_1b3yt_78{padding:var(--spacing-lg);flex:1;overflow-y:auto}@keyframes _fadeIn_1b3yt_1{0%{opacity:0}to{opacity:1}}@keyframes _slideUp_1b3yt_1{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media (width<=768px){._modal_1b3yt_13{max-height:95vh}._header_1b3yt_45,._content_1b3yt_78{padding:var(--spacing-md)}}._spinner_1uhnh_2{border:3px solid #ffffff1a;border-top-color:var(--color-primary);border-radius:50%;animation:.8s linear infinite _spin_1uhnh_2}._spinner_1uhnh_2._sm_1uhnh_9{border-width:2px;width:16px;height:16px}._spinner_1uhnh_2._md_1uhnh_15{border-width:3px;width:32px;height:32px}._spinner_1uhnh_2._lg_1uhnh_21{border-width:4px;width:48px;height:48px}@keyframes _spin_1uhnh_2{to{transform:rotate(360deg)}}._dots_1uhnh_34{gap:8px;display:inline-flex}._dots_1uhnh_34._sm_1uhnh_9{gap:4px}._dots_1uhnh_34._lg_1uhnh_21{gap:12px}._dot_1uhnh_34{background-color:var(--color-primary);border-radius:50%;animation:1.4s ease-in-out infinite both _bounce_1uhnh_1;display:inline-block}._dots_1uhnh_34._sm_1uhnh_9 ._dot_1uhnh_34{width:6px;height:6px}._dots_1uhnh_34._md_1uhnh_15 ._dot_1uhnh_34{width:10px;height:10px}._dots_1uhnh_34._lg_1uhnh_21 ._dot_1uhnh_34{width:14px;height:14px}._dot_1uhnh_34:first-child{animation-delay:-.32s}._dot_1uhnh_34:nth-child(2){animation-delay:-.16s}@keyframes _bounce_1uhnh_1{0%,80%,to{opacity:.5;transform:scale(0)}40%{opacity:1;transform:scale(1)}}._visuallyHidden_1uhnh_91{clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}
|
|
2
|
+
/*$vite$:1*/
|