diff --git a/frontends/react-native-ios/.gitignore b/frontends/auth/react-native/.gitignore similarity index 100% rename from frontends/react-native-ios/.gitignore rename to frontends/auth/react-native/.gitignore diff --git a/frontends/react-native-ios/App.tsx b/frontends/auth/react-native/App.tsx similarity index 100% rename from frontends/react-native-ios/App.tsx rename to frontends/auth/react-native/App.tsx diff --git a/frontends/react-native-ios/app.json b/frontends/auth/react-native/app.json similarity index 100% rename from frontends/react-native-ios/app.json rename to frontends/auth/react-native/app.json diff --git a/frontends/react-native-ios/app/(app)/(tabs)/_layout.tsx b/frontends/auth/react-native/app/(app)/(tabs)/_layout.tsx similarity index 100% rename from frontends/react-native-ios/app/(app)/(tabs)/_layout.tsx rename to frontends/auth/react-native/app/(app)/(tabs)/_layout.tsx diff --git a/frontends/react-native-ios/app/(app)/(tabs)/home.tsx b/frontends/auth/react-native/app/(app)/(tabs)/home.tsx similarity index 100% rename from frontends/react-native-ios/app/(app)/(tabs)/home.tsx rename to frontends/auth/react-native/app/(app)/(tabs)/home.tsx diff --git a/frontends/react-native-ios/app/(app)/(tabs)/settings.tsx b/frontends/auth/react-native/app/(app)/(tabs)/settings.tsx similarity index 100% rename from frontends/react-native-ios/app/(app)/(tabs)/settings.tsx rename to frontends/auth/react-native/app/(app)/(tabs)/settings.tsx diff --git a/frontends/react-native-ios/app/(app)/_layout.tsx b/frontends/auth/react-native/app/(app)/_layout.tsx similarity index 100% rename from frontends/react-native-ios/app/(app)/_layout.tsx rename to frontends/auth/react-native/app/(app)/_layout.tsx diff --git a/frontends/react-native-ios/app/(app)/profile.tsx b/frontends/auth/react-native/app/(app)/profile.tsx similarity index 100% rename from frontends/react-native-ios/app/(app)/profile.tsx rename to frontends/auth/react-native/app/(app)/profile.tsx diff --git a/frontends/react-native-ios/app/(auth)/_layout.tsx b/frontends/auth/react-native/app/(auth)/_layout.tsx similarity index 100% rename from frontends/react-native-ios/app/(auth)/_layout.tsx rename to frontends/auth/react-native/app/(auth)/_layout.tsx diff --git a/frontends/react-native-ios/app/(auth)/login.tsx b/frontends/auth/react-native/app/(auth)/login.tsx similarity index 100% rename from frontends/react-native-ios/app/(auth)/login.tsx rename to frontends/auth/react-native/app/(auth)/login.tsx diff --git a/frontends/react-native-ios/app/(auth)/register.tsx b/frontends/auth/react-native/app/(auth)/register.tsx similarity index 100% rename from frontends/react-native-ios/app/(auth)/register.tsx rename to frontends/auth/react-native/app/(auth)/register.tsx diff --git a/frontends/react-native-ios/app/_layout.tsx b/frontends/auth/react-native/app/_layout.tsx similarity index 100% rename from frontends/react-native-ios/app/_layout.tsx rename to frontends/auth/react-native/app/_layout.tsx diff --git a/frontends/react-native-ios/app/index.tsx b/frontends/auth/react-native/app/index.tsx similarity index 100% rename from frontends/react-native-ios/app/index.tsx rename to frontends/auth/react-native/app/index.tsx diff --git a/frontends/react-native-ios/assets/adaptive-icon.png b/frontends/auth/react-native/assets/adaptive-icon.png similarity index 100% rename from frontends/react-native-ios/assets/adaptive-icon.png rename to frontends/auth/react-native/assets/adaptive-icon.png diff --git a/frontends/react-native-ios/assets/favicon.png b/frontends/auth/react-native/assets/favicon.png similarity index 100% rename from frontends/react-native-ios/assets/favicon.png rename to frontends/auth/react-native/assets/favicon.png diff --git a/frontends/react-native-ios/assets/icon.png b/frontends/auth/react-native/assets/icon.png similarity index 100% rename from frontends/react-native-ios/assets/icon.png rename to frontends/auth/react-native/assets/icon.png diff --git a/frontends/react-native-ios/assets/splash-icon.png b/frontends/auth/react-native/assets/splash-icon.png similarity index 100% rename from frontends/react-native-ios/assets/splash-icon.png rename to frontends/auth/react-native/assets/splash-icon.png diff --git a/frontends/react-native-ios/babel.config.js b/frontends/auth/react-native/babel.config.js similarity index 100% rename from frontends/react-native-ios/babel.config.js rename to frontends/auth/react-native/babel.config.js diff --git a/frontends/react-native-ios/biome.json b/frontends/auth/react-native/biome.json similarity index 100% rename from frontends/react-native-ios/biome.json rename to frontends/auth/react-native/biome.json diff --git a/frontends/react-native-ios/eas.json b/frontends/auth/react-native/eas.json similarity index 100% rename from frontends/react-native-ios/eas.json rename to frontends/auth/react-native/eas.json diff --git a/frontends/react-native-ios/index.ts b/frontends/auth/react-native/index.ts similarity index 100% rename from frontends/react-native-ios/index.ts rename to frontends/auth/react-native/index.ts diff --git a/frontends/react-native-ios/justfile b/frontends/auth/react-native/justfile similarity index 100% rename from frontends/react-native-ios/justfile rename to frontends/auth/react-native/justfile diff --git a/frontends/react-native-ios/package.json b/frontends/auth/react-native/package.json similarity index 100% rename from frontends/react-native-ios/package.json rename to frontends/auth/react-native/package.json diff --git a/frontends/react-native-ios/pnpm-lock.yaml b/frontends/auth/react-native/pnpm-lock.yaml similarity index 100% rename from frontends/react-native-ios/pnpm-lock.yaml rename to frontends/auth/react-native/pnpm-lock.yaml diff --git a/frontends/react-native-ios/src/api/hooks/index.ts b/frontends/auth/react-native/src/api/hooks/index.ts similarity index 100% rename from frontends/react-native-ios/src/api/hooks/index.ts rename to frontends/auth/react-native/src/api/hooks/index.ts diff --git a/frontends/react-native-ios/src/api/hooks/useAuth.ts b/frontends/auth/react-native/src/api/hooks/useAuth.ts similarity index 100% rename from frontends/react-native-ios/src/api/hooks/useAuth.ts rename to frontends/auth/react-native/src/api/hooks/useAuth.ts diff --git a/frontends/react-native-ios/src/api/hooks/useUsers.ts b/frontends/auth/react-native/src/api/hooks/useUsers.ts similarity index 100% rename from frontends/react-native-ios/src/api/hooks/useUsers.ts rename to frontends/auth/react-native/src/api/hooks/useUsers.ts diff --git a/frontends/react-native-ios/src/api/types/auth.types.ts b/frontends/auth/react-native/src/api/types/auth.types.ts similarity index 100% rename from frontends/react-native-ios/src/api/types/auth.types.ts rename to frontends/auth/react-native/src/api/types/auth.types.ts diff --git a/frontends/react-native-ios/src/api/types/index.ts b/frontends/auth/react-native/src/api/types/index.ts similarity index 100% rename from frontends/react-native-ios/src/api/types/index.ts rename to frontends/auth/react-native/src/api/types/index.ts diff --git a/frontends/react-native-ios/src/api/types/user.types.ts b/frontends/auth/react-native/src/api/types/user.types.ts similarity index 100% rename from frontends/react-native-ios/src/api/types/user.types.ts rename to frontends/auth/react-native/src/api/types/user.types.ts diff --git a/frontends/react-native-ios/src/core/api/api.config.ts b/frontends/auth/react-native/src/core/api/api.config.ts similarity index 100% rename from frontends/react-native-ios/src/core/api/api.config.ts rename to frontends/auth/react-native/src/core/api/api.config.ts diff --git a/frontends/react-native-ios/src/core/api/errors.ts b/frontends/auth/react-native/src/core/api/errors.ts similarity index 100% rename from frontends/react-native-ios/src/core/api/errors.ts rename to frontends/auth/react-native/src/core/api/errors.ts diff --git a/frontends/react-native-ios/src/core/api/index.ts b/frontends/auth/react-native/src/core/api/index.ts similarity index 100% rename from frontends/react-native-ios/src/core/api/index.ts rename to frontends/auth/react-native/src/core/api/index.ts diff --git a/frontends/react-native-ios/src/core/api/query.config.ts b/frontends/auth/react-native/src/core/api/query.config.ts similarity index 100% rename from frontends/react-native-ios/src/core/api/query.config.ts rename to frontends/auth/react-native/src/core/api/query.config.ts diff --git a/frontends/react-native-ios/src/core/config.ts b/frontends/auth/react-native/src/core/config.ts similarity index 100% rename from frontends/react-native-ios/src/core/config.ts rename to frontends/auth/react-native/src/core/config.ts diff --git a/frontends/react-native-ios/src/core/lib/auth.store.ts b/frontends/auth/react-native/src/core/lib/auth.store.ts similarity index 100% rename from frontends/react-native-ios/src/core/lib/auth.store.ts rename to frontends/auth/react-native/src/core/lib/auth.store.ts diff --git a/frontends/react-native-ios/src/core/lib/index.ts b/frontends/auth/react-native/src/core/lib/index.ts similarity index 100% rename from frontends/react-native-ios/src/core/lib/index.ts rename to frontends/auth/react-native/src/core/lib/index.ts diff --git a/frontends/react-native-ios/src/core/lib/ui.store.ts b/frontends/auth/react-native/src/core/lib/ui.store.ts similarity index 100% rename from frontends/react-native-ios/src/core/lib/ui.store.ts rename to frontends/auth/react-native/src/core/lib/ui.store.ts diff --git a/frontends/react-native-ios/src/core/storage/app.storage.ts b/frontends/auth/react-native/src/core/storage/app.storage.ts similarity index 100% rename from frontends/react-native-ios/src/core/storage/app.storage.ts rename to frontends/auth/react-native/src/core/storage/app.storage.ts diff --git a/frontends/react-native-ios/src/core/storage/index.ts b/frontends/auth/react-native/src/core/storage/index.ts similarity index 100% rename from frontends/react-native-ios/src/core/storage/index.ts rename to frontends/auth/react-native/src/core/storage/index.ts diff --git a/frontends/react-native-ios/src/core/storage/secure.storage.ts b/frontends/auth/react-native/src/core/storage/secure.storage.ts similarity index 100% rename from frontends/react-native-ios/src/core/storage/secure.storage.ts rename to frontends/auth/react-native/src/core/storage/secure.storage.ts diff --git a/frontends/react-native-ios/src/shared/components/index.ts b/frontends/auth/react-native/src/shared/components/index.ts similarity index 100% rename from frontends/react-native-ios/src/shared/components/index.ts rename to frontends/auth/react-native/src/shared/components/index.ts diff --git a/frontends/react-native-ios/src/shared/components/ui/Button.tsx b/frontends/auth/react-native/src/shared/components/ui/Button.tsx similarity index 100% rename from frontends/react-native-ios/src/shared/components/ui/Button.tsx rename to frontends/auth/react-native/src/shared/components/ui/Button.tsx diff --git a/frontends/react-native-ios/src/shared/components/ui/Card.tsx b/frontends/auth/react-native/src/shared/components/ui/Card.tsx similarity index 100% rename from frontends/react-native-ios/src/shared/components/ui/Card.tsx rename to frontends/auth/react-native/src/shared/components/ui/Card.tsx diff --git a/frontends/react-native-ios/src/shared/components/ui/DottedBackground.tsx b/frontends/auth/react-native/src/shared/components/ui/DottedBackground.tsx similarity index 100% rename from frontends/react-native-ios/src/shared/components/ui/DottedBackground.tsx rename to frontends/auth/react-native/src/shared/components/ui/DottedBackground.tsx diff --git a/frontends/react-native-ios/src/shared/components/ui/Input.tsx b/frontends/auth/react-native/src/shared/components/ui/Input.tsx similarity index 100% rename from frontends/react-native-ios/src/shared/components/ui/Input.tsx rename to frontends/auth/react-native/src/shared/components/ui/Input.tsx diff --git a/frontends/react-native-ios/src/shared/components/ui/PasswordInput.tsx b/frontends/auth/react-native/src/shared/components/ui/PasswordInput.tsx similarity index 100% rename from frontends/react-native-ios/src/shared/components/ui/PasswordInput.tsx rename to frontends/auth/react-native/src/shared/components/ui/PasswordInput.tsx diff --git a/frontends/react-native-ios/src/shared/components/ui/index.ts b/frontends/auth/react-native/src/shared/components/ui/index.ts similarity index 100% rename from frontends/react-native-ios/src/shared/components/ui/index.ts rename to frontends/auth/react-native/src/shared/components/ui/index.ts diff --git a/frontends/react-native-ios/src/shared/hooks/index.ts b/frontends/auth/react-native/src/shared/hooks/index.ts similarity index 100% rename from frontends/react-native-ios/src/shared/hooks/index.ts rename to frontends/auth/react-native/src/shared/hooks/index.ts diff --git a/frontends/react-native-ios/src/shared/hooks/useAppState.ts b/frontends/auth/react-native/src/shared/hooks/useAppState.ts similarity index 100% rename from frontends/react-native-ios/src/shared/hooks/useAppState.ts rename to frontends/auth/react-native/src/shared/hooks/useAppState.ts diff --git a/frontends/react-native-ios/src/shared/hooks/useBiometricAuth.ts b/frontends/auth/react-native/src/shared/hooks/useBiometricAuth.ts similarity index 100% rename from frontends/react-native-ios/src/shared/hooks/useBiometricAuth.ts rename to frontends/auth/react-native/src/shared/hooks/useBiometricAuth.ts diff --git a/frontends/react-native-ios/src/shared/hooks/useBiometrics.ts b/frontends/auth/react-native/src/shared/hooks/useBiometrics.ts similarity index 100% rename from frontends/react-native-ios/src/shared/hooks/useBiometrics.ts rename to frontends/auth/react-native/src/shared/hooks/useBiometrics.ts diff --git a/frontends/react-native-ios/src/shared/hooks/useOnlineManager.ts b/frontends/auth/react-native/src/shared/hooks/useOnlineManager.ts similarity index 100% rename from frontends/react-native-ios/src/shared/hooks/useOnlineManager.ts rename to frontends/auth/react-native/src/shared/hooks/useOnlineManager.ts diff --git a/frontends/react-native-ios/src/shared/hooks/useSession.ts b/frontends/auth/react-native/src/shared/hooks/useSession.ts similarity index 100% rename from frontends/react-native-ios/src/shared/hooks/useSession.ts rename to frontends/auth/react-native/src/shared/hooks/useSession.ts diff --git a/frontends/react-native-ios/src/shared/index.ts b/frontends/auth/react-native/src/shared/index.ts similarity index 100% rename from frontends/react-native-ios/src/shared/index.ts rename to frontends/auth/react-native/src/shared/index.ts diff --git a/frontends/react-native-ios/src/shared/utils/haptics.ts b/frontends/auth/react-native/src/shared/utils/haptics.ts similarity index 100% rename from frontends/react-native-ios/src/shared/utils/haptics.ts rename to frontends/auth/react-native/src/shared/utils/haptics.ts diff --git a/frontends/react-native-ios/src/shared/utils/index.ts b/frontends/auth/react-native/src/shared/utils/index.ts similarity index 100% rename from frontends/react-native-ios/src/shared/utils/index.ts rename to frontends/auth/react-native/src/shared/utils/index.ts diff --git a/frontends/react-native-ios/src/theme/tamagui.config.ts b/frontends/auth/react-native/src/theme/tamagui.config.ts similarity index 100% rename from frontends/react-native-ios/src/theme/tamagui.config.ts rename to frontends/auth/react-native/src/theme/tamagui.config.ts diff --git a/frontends/react-native-ios/src/theme/themes.ts b/frontends/auth/react-native/src/theme/themes.ts similarity index 100% rename from frontends/react-native-ios/src/theme/themes.ts rename to frontends/auth/react-native/src/theme/themes.ts diff --git a/frontends/react-native-ios/src/theme/tokens.ts b/frontends/auth/react-native/src/theme/tokens.ts similarity index 100% rename from frontends/react-native-ios/src/theme/tokens.ts rename to frontends/auth/react-native/src/theme/tokens.ts diff --git a/frontends/react-native-ios/tamagui.config.ts b/frontends/auth/react-native/tamagui.config.ts similarity index 100% rename from frontends/react-native-ios/tamagui.config.ts rename to frontends/auth/react-native/tamagui.config.ts diff --git a/frontends/react-native-ios/tsconfig.json b/frontends/auth/react-native/tsconfig.json similarity index 100% rename from frontends/react-native-ios/tsconfig.json rename to frontends/auth/react-native/tsconfig.json diff --git a/frontends/react-scss/.dockerignore b/frontends/auth/react-scss/.dockerignore similarity index 100% rename from frontends/react-scss/.dockerignore rename to frontends/auth/react-scss/.dockerignore diff --git a/frontends/react-scss/.gitignore b/frontends/auth/react-scss/.gitignore similarity index 100% rename from frontends/react-scss/.gitignore rename to frontends/auth/react-scss/.gitignore diff --git a/frontends/react-scss/.stylelintignore b/frontends/auth/react-scss/.stylelintignore similarity index 100% rename from frontends/react-scss/.stylelintignore rename to frontends/auth/react-scss/.stylelintignore diff --git a/frontends/react-scss/biome.json b/frontends/auth/react-scss/biome.json similarity index 100% rename from frontends/react-scss/biome.json rename to frontends/auth/react-scss/biome.json diff --git a/frontends/react-scss/index.html b/frontends/auth/react-scss/index.html similarity index 100% rename from frontends/react-scss/index.html rename to frontends/auth/react-scss/index.html diff --git a/frontends/react-scss/package.json b/frontends/auth/react-scss/package.json similarity index 100% rename from frontends/react-scss/package.json rename to frontends/auth/react-scss/package.json diff --git a/frontends/react-scss/pnpm-lock.yaml b/frontends/auth/react-scss/pnpm-lock.yaml similarity index 100% rename from frontends/react-scss/pnpm-lock.yaml rename to frontends/auth/react-scss/pnpm-lock.yaml diff --git a/frontends/react-scss/public/assets/android-chrome-192x192.png b/frontends/auth/react-scss/public/assets/android-chrome-192x192.png similarity index 100% rename from frontends/react-scss/public/assets/android-chrome-192x192.png rename to frontends/auth/react-scss/public/assets/android-chrome-192x192.png diff --git a/frontends/react-scss/public/assets/android-chrome-512x512.png b/frontends/auth/react-scss/public/assets/android-chrome-512x512.png similarity index 100% rename from frontends/react-scss/public/assets/android-chrome-512x512.png rename to frontends/auth/react-scss/public/assets/android-chrome-512x512.png diff --git a/frontends/react-scss/public/assets/apple-touch-icon.png b/frontends/auth/react-scss/public/assets/apple-touch-icon.png similarity index 100% rename from frontends/react-scss/public/assets/apple-touch-icon.png rename to frontends/auth/react-scss/public/assets/apple-touch-icon.png diff --git a/frontends/react-scss/public/assets/favicon-16x16.png b/frontends/auth/react-scss/public/assets/favicon-16x16.png similarity index 100% rename from frontends/react-scss/public/assets/favicon-16x16.png rename to frontends/auth/react-scss/public/assets/favicon-16x16.png diff --git a/frontends/react-scss/public/assets/favicon-32x32.png b/frontends/auth/react-scss/public/assets/favicon-32x32.png similarity index 100% rename from frontends/react-scss/public/assets/favicon-32x32.png rename to frontends/auth/react-scss/public/assets/favicon-32x32.png diff --git a/frontends/react-scss/public/assets/favicon.ico b/frontends/auth/react-scss/public/assets/favicon.ico similarity index 100% rename from frontends/react-scss/public/assets/favicon.ico rename to frontends/auth/react-scss/public/assets/favicon.ico diff --git a/frontends/react-scss/public/assets/site.webmanifest b/frontends/auth/react-scss/public/assets/site.webmanifest similarity index 100% rename from frontends/react-scss/public/assets/site.webmanifest rename to frontends/auth/react-scss/public/assets/site.webmanifest diff --git a/frontends/react-scss/src/App.tsx b/frontends/auth/react-scss/src/App.tsx similarity index 100% rename from frontends/react-scss/src/App.tsx rename to frontends/auth/react-scss/src/App.tsx diff --git a/frontends/react-scss/src/api/hooks/index.ts b/frontends/auth/react-scss/src/api/hooks/index.ts similarity index 100% rename from frontends/react-scss/src/api/hooks/index.ts rename to frontends/auth/react-scss/src/api/hooks/index.ts diff --git a/frontends/react-scss/src/api/hooks/useAdmin.ts b/frontends/auth/react-scss/src/api/hooks/useAdmin.ts similarity index 100% rename from frontends/react-scss/src/api/hooks/useAdmin.ts rename to frontends/auth/react-scss/src/api/hooks/useAdmin.ts diff --git a/frontends/react-scss/src/api/hooks/useAuth.ts b/frontends/auth/react-scss/src/api/hooks/useAuth.ts similarity index 100% rename from frontends/react-scss/src/api/hooks/useAuth.ts rename to frontends/auth/react-scss/src/api/hooks/useAuth.ts diff --git a/frontends/react-scss/src/api/hooks/useUsers.ts b/frontends/auth/react-scss/src/api/hooks/useUsers.ts similarity index 100% rename from frontends/react-scss/src/api/hooks/useUsers.ts rename to frontends/auth/react-scss/src/api/hooks/useUsers.ts diff --git a/frontends/react-scss/src/api/index.ts b/frontends/auth/react-scss/src/api/index.ts similarity index 100% rename from frontends/react-scss/src/api/index.ts rename to frontends/auth/react-scss/src/api/index.ts diff --git a/frontends/react-scss/src/api/types/auth.types.ts b/frontends/auth/react-scss/src/api/types/auth.types.ts similarity index 100% rename from frontends/react-scss/src/api/types/auth.types.ts rename to frontends/auth/react-scss/src/api/types/auth.types.ts diff --git a/frontends/react-scss/src/api/types/index.ts b/frontends/auth/react-scss/src/api/types/index.ts similarity index 100% rename from frontends/react-scss/src/api/types/index.ts rename to frontends/auth/react-scss/src/api/types/index.ts diff --git a/frontends/react-scss/src/api/types/user.types.ts b/frontends/auth/react-scss/src/api/types/user.types.ts similarity index 100% rename from frontends/react-scss/src/api/types/user.types.ts rename to frontends/auth/react-scss/src/api/types/user.types.ts diff --git a/frontends/react-scss/src/components/index.tsx b/frontends/auth/react-scss/src/components/index.tsx similarity index 100% rename from frontends/react-scss/src/components/index.tsx rename to frontends/auth/react-scss/src/components/index.tsx diff --git a/frontends/react-scss/src/config.ts b/frontends/auth/react-scss/src/config.ts similarity index 100% rename from frontends/react-scss/src/config.ts rename to frontends/auth/react-scss/src/config.ts diff --git a/frontends/react-scss/src/core/api/api.config.ts b/frontends/auth/react-scss/src/core/api/api.config.ts similarity index 100% rename from frontends/react-scss/src/core/api/api.config.ts rename to frontends/auth/react-scss/src/core/api/api.config.ts diff --git a/frontends/react-scss/src/core/api/errors.ts b/frontends/auth/react-scss/src/core/api/errors.ts similarity index 100% rename from frontends/react-scss/src/core/api/errors.ts rename to frontends/auth/react-scss/src/core/api/errors.ts diff --git a/frontends/react-scss/src/core/api/index.ts b/frontends/auth/react-scss/src/core/api/index.ts similarity index 100% rename from frontends/react-scss/src/core/api/index.ts rename to frontends/auth/react-scss/src/core/api/index.ts diff --git a/frontends/react-scss/src/core/api/query.config.ts b/frontends/auth/react-scss/src/core/api/query.config.ts similarity index 100% rename from frontends/react-scss/src/core/api/query.config.ts rename to frontends/auth/react-scss/src/core/api/query.config.ts diff --git a/frontends/react-scss/src/core/app/protected-route.tsx b/frontends/auth/react-scss/src/core/app/protected-route.tsx similarity index 100% rename from frontends/react-scss/src/core/app/protected-route.tsx rename to frontends/auth/react-scss/src/core/app/protected-route.tsx diff --git a/frontends/react-scss/src/core/app/routers.tsx b/frontends/auth/react-scss/src/core/app/routers.tsx similarity index 100% rename from frontends/react-scss/src/core/app/routers.tsx rename to frontends/auth/react-scss/src/core/app/routers.tsx diff --git a/frontends/auth/react-scss/src/core/app/shell.module.scss b/frontends/auth/react-scss/src/core/app/shell.module.scss new file mode 100644 index 0000000..3466896 --- /dev/null +++ b/frontends/auth/react-scss/src/core/app/shell.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// shell.module.scss +// =================== diff --git a/frontends/react-scss/src/core/app/shell.tsx b/frontends/auth/react-scss/src/core/app/shell.tsx similarity index 100% rename from frontends/react-scss/src/core/app/shell.tsx rename to frontends/auth/react-scss/src/core/app/shell.tsx diff --git a/frontends/auth/react-scss/src/core/app/toast.module.scss b/frontends/auth/react-scss/src/core/app/toast.module.scss new file mode 100644 index 0000000..62a8ee3 --- /dev/null +++ b/frontends/auth/react-scss/src/core/app/toast.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// toast.module.scss +// =================== diff --git a/frontends/react-scss/src/core/lib/auth.form.store.ts b/frontends/auth/react-scss/src/core/lib/auth.form.store.ts similarity index 100% rename from frontends/react-scss/src/core/lib/auth.form.store.ts rename to frontends/auth/react-scss/src/core/lib/auth.form.store.ts diff --git a/frontends/react-scss/src/core/lib/auth.store.ts b/frontends/auth/react-scss/src/core/lib/auth.store.ts similarity index 100% rename from frontends/react-scss/src/core/lib/auth.store.ts rename to frontends/auth/react-scss/src/core/lib/auth.store.ts diff --git a/frontends/react-scss/src/core/lib/index.ts b/frontends/auth/react-scss/src/core/lib/index.ts similarity index 100% rename from frontends/react-scss/src/core/lib/index.ts rename to frontends/auth/react-scss/src/core/lib/index.ts diff --git a/frontends/react-scss/src/core/lib/shell.ui.store.ts b/frontends/auth/react-scss/src/core/lib/shell.ui.store.ts similarity index 100% rename from frontends/react-scss/src/core/lib/shell.ui.store.ts rename to frontends/auth/react-scss/src/core/lib/shell.ui.store.ts diff --git a/frontends/react-scss/src/main.tsx b/frontends/auth/react-scss/src/main.tsx similarity index 100% rename from frontends/react-scss/src/main.tsx rename to frontends/auth/react-scss/src/main.tsx diff --git a/frontends/auth/react-scss/src/pages/admin/admin.module.scss b/frontends/auth/react-scss/src/pages/admin/admin.module.scss new file mode 100644 index 0000000..c59b723 --- /dev/null +++ b/frontends/auth/react-scss/src/pages/admin/admin.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// admin.module.scss +// =================== diff --git a/frontends/react-scss/src/pages/admin/index.tsx b/frontends/auth/react-scss/src/pages/admin/index.tsx similarity index 100% rename from frontends/react-scss/src/pages/admin/index.tsx rename to frontends/auth/react-scss/src/pages/admin/index.tsx diff --git a/frontends/auth/react-scss/src/pages/dashboard/dashboard.module.scss b/frontends/auth/react-scss/src/pages/dashboard/dashboard.module.scss new file mode 100644 index 0000000..cbf1f90 --- /dev/null +++ b/frontends/auth/react-scss/src/pages/dashboard/dashboard.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// dashboard.module.scss +// =================== diff --git a/frontends/react-scss/src/pages/dashboard/index.tsx b/frontends/auth/react-scss/src/pages/dashboard/index.tsx similarity index 100% rename from frontends/react-scss/src/pages/dashboard/index.tsx rename to frontends/auth/react-scss/src/pages/dashboard/index.tsx diff --git a/frontends/react-scss/src/pages/landing/index.tsx b/frontends/auth/react-scss/src/pages/landing/index.tsx similarity index 100% rename from frontends/react-scss/src/pages/landing/index.tsx rename to frontends/auth/react-scss/src/pages/landing/index.tsx diff --git a/frontends/auth/react-scss/src/pages/landing/landing.module.scss b/frontends/auth/react-scss/src/pages/landing/landing.module.scss new file mode 100644 index 0000000..164f39a --- /dev/null +++ b/frontends/auth/react-scss/src/pages/landing/landing.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// landing.module.scss +// =================== diff --git a/frontends/react-scss/src/pages/login/index.tsx b/frontends/auth/react-scss/src/pages/login/index.tsx similarity index 100% rename from frontends/react-scss/src/pages/login/index.tsx rename to frontends/auth/react-scss/src/pages/login/index.tsx diff --git a/frontends/auth/react-scss/src/pages/login/login.module.scss b/frontends/auth/react-scss/src/pages/login/login.module.scss new file mode 100644 index 0000000..ea09982 --- /dev/null +++ b/frontends/auth/react-scss/src/pages/login/login.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// login.module.scss +// =================== diff --git a/frontends/react-scss/src/pages/register/index.tsx b/frontends/auth/react-scss/src/pages/register/index.tsx similarity index 100% rename from frontends/react-scss/src/pages/register/index.tsx rename to frontends/auth/react-scss/src/pages/register/index.tsx diff --git a/frontends/auth/react-scss/src/pages/register/register.module.scss b/frontends/auth/react-scss/src/pages/register/register.module.scss new file mode 100644 index 0000000..acdeec0 --- /dev/null +++ b/frontends/auth/react-scss/src/pages/register/register.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// register.module.scss +// =================== diff --git a/frontends/react-scss/src/pages/settings/index.tsx b/frontends/auth/react-scss/src/pages/settings/index.tsx similarity index 100% rename from frontends/react-scss/src/pages/settings/index.tsx rename to frontends/auth/react-scss/src/pages/settings/index.tsx diff --git a/frontends/auth/react-scss/src/pages/settings/settings.module.scss b/frontends/auth/react-scss/src/pages/settings/settings.module.scss new file mode 100644 index 0000000..e95e0f8 --- /dev/null +++ b/frontends/auth/react-scss/src/pages/settings/settings.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// settings.module.scss +// =================== diff --git a/frontends/react-scss/src/styles.scss b/frontends/auth/react-scss/src/styles.scss similarity index 64% rename from frontends/react-scss/src/styles.scss rename to frontends/auth/react-scss/src/styles.scss index 4ac95fe..240ec55 100644 --- a/frontends/react-scss/src/styles.scss +++ b/frontends/auth/react-scss/src/styles.scss @@ -1,5 +1,5 @@ // =================== -// © AngelaMos | 2025 +// © AngelaMos | 2026 // styles.scss // =================== @@ -8,22 +8,16 @@ @forward 'styles/mixins'; @use 'styles/reset'; -@use 'styles/tokens' as *; -@use 'styles/fonts' as *; #root { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; - background: $bg-default; } .app { flex: 1; display: flex; flex-direction: column; - background: $bg-default; - color: $text-default; - font-family: $font-sans; } diff --git a/frontends/react-scss/src/styles/_fonts.scss b/frontends/auth/react-scss/src/styles/_fonts.scss similarity index 100% rename from frontends/react-scss/src/styles/_fonts.scss rename to frontends/auth/react-scss/src/styles/_fonts.scss diff --git a/frontends/react-scss/src/styles/_index.scss b/frontends/auth/react-scss/src/styles/_index.scss similarity index 100% rename from frontends/react-scss/src/styles/_index.scss rename to frontends/auth/react-scss/src/styles/_index.scss diff --git a/frontends/react-scss/src/styles/_mixins.scss b/frontends/auth/react-scss/src/styles/_mixins.scss similarity index 100% rename from frontends/react-scss/src/styles/_mixins.scss rename to frontends/auth/react-scss/src/styles/_mixins.scss diff --git a/frontends/auth/react-scss/src/styles/_reset.scss b/frontends/auth/react-scss/src/styles/_reset.scss new file mode 100644 index 0000000..a9cbdc1 --- /dev/null +++ b/frontends/auth/react-scss/src/styles/_reset.scss @@ -0,0 +1,105 @@ +// =================== +// © AngelaMos | 2026 +// _reset.scss +// =================== + +*, +*::before, +*::after { + box-sizing: border-box; +} + +* { + margin: 0; + padding: 0; + -webkit-tap-highlight-color: transparent; +} + +html { + font-size: 16px; + -moz-text-size-adjust: none; + -webkit-text-size-adjust: none; + text-size-adjust: none; + overflow-x: hidden; +} + +@media (prefers-reduced-motion: no-preference) { + html { + interpolate-size: allow-keywords; + scroll-behavior: smooth; + } +} + +body { + min-height: 100vh; + min-height: 100dvh; + line-height: 1.5; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + overflow-x: hidden; + background-color: #fff; + color: #000; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + line-height: 1.2; + text-wrap: balance; + overflow-wrap: break-word; +} + +p { + text-wrap: pretty; + overflow-wrap: break-word; +} + +img, +picture, +video, +canvas, +svg { + display: block; + max-width: 100%; + height: auto; +} + +input, +button, +textarea, +select { + font: inherit; + color: inherit; +} + +button { + background: none; + border: none; + cursor: pointer; + text-align: inherit; + font-family: inherit; +} + +[hidden] { + display: none !important; +} + +[disabled] { + cursor: not-allowed; + opacity: 0.5; +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} diff --git a/frontends/react-scss/src/styles/_tokens.scss b/frontends/auth/react-scss/src/styles/_tokens.scss similarity index 100% rename from frontends/react-scss/src/styles/_tokens.scss rename to frontends/auth/react-scss/src/styles/_tokens.scss diff --git a/frontends/react-scss/stylelint.config.js b/frontends/auth/react-scss/stylelint.config.js similarity index 100% rename from frontends/react-scss/stylelint.config.js rename to frontends/auth/react-scss/stylelint.config.js diff --git a/frontends/react-scss/tsconfig.app.json b/frontends/auth/react-scss/tsconfig.app.json similarity index 100% rename from frontends/react-scss/tsconfig.app.json rename to frontends/auth/react-scss/tsconfig.app.json diff --git a/frontends/react-scss/tsconfig.json b/frontends/auth/react-scss/tsconfig.json similarity index 100% rename from frontends/react-scss/tsconfig.json rename to frontends/auth/react-scss/tsconfig.json diff --git a/frontends/react-scss/tsconfig.node.json b/frontends/auth/react-scss/tsconfig.node.json similarity index 100% rename from frontends/react-scss/tsconfig.node.json rename to frontends/auth/react-scss/tsconfig.node.json diff --git a/frontends/react-scss/vite.config.ts b/frontends/auth/react-scss/vite.config.ts similarity index 100% rename from frontends/react-scss/vite.config.ts rename to frontends/auth/react-scss/vite.config.ts diff --git a/frontends/open/.env.example b/frontends/open/.env.example new file mode 100644 index 0000000..04ee50a --- /dev/null +++ b/frontends/open/.env.example @@ -0,0 +1,29 @@ +# ============================================================================= +# AngelaMos | 2026 +# .env.example +# ============================================================================= +# Copy to .env for production, .env.development for dev +# ============================================================================= + +# ============================================================================= +# HOST PORTS +# ============================================================================= +NGINX_HOST_PORT=22784 +FRONTEND_HOST_PORT=33067 + +# ============================================================================= +# Application +# ============================================================================= +APP_NAME=no-auth-template +ENVIRONMENT=production + +# ============================================================================= +# Frontend (Vite) +# ============================================================================= +VITE_API_URL=/api +VITE_APP_TITLE=No Auth Template + +# ============================================================================= +# Cloudflare Tunnel (for cloudflared.compose.yml) +# ============================================================================= +CLOUDFLARE_TUNNEL_TOKEN=YOUR_TUNNEL_TOKEN_HERE diff --git a/frontends/open/cloudflared.compose.yml b/frontends/open/cloudflared.compose.yml new file mode 100644 index 0000000..e91fde7 --- /dev/null +++ b/frontends/open/cloudflared.compose.yml @@ -0,0 +1,27 @@ +# ============================================================================= +# AngelaMos | 2026 +# cloudflared.compose.yml +# ============================================================================= +# Cloudflare Tunnel for production remote access +# Usage: docker compose -f compose.yml -f cloudflared.compose.yml up -d +# ============================================================================= + +services: + cloudflared: + image: cloudflare/cloudflared:latest + container_name: ${APP_NAME:-no-auth-template}-tunnel + command: tunnel run --token ${CLOUDFLARE_TUNNEL_TOKEN} + networks: + - app + depends_on: + nginx: + condition: service_started + deploy: + resources: + limits: + cpus: '0.5' + memory: 128M + reservations: + cpus: '0.1' + memory: 32M + restart: unless-stopped diff --git a/frontends/open/compose.yml b/frontends/open/compose.yml new file mode 100644 index 0000000..c1bbec2 --- /dev/null +++ b/frontends/open/compose.yml @@ -0,0 +1,36 @@ +# ============================================================================= +# AngelaMos | 2026 +# compose.yml +# ============================================================================= +# Production compose - Nginx serving built React app +# For Cloudflare tunnel: docker compose -f compose.yml -f cloudflared.compose.yml up +# ============================================================================= + +name: ${APP_NAME:-no-auth-template} + +services: + nginx: + build: + context: . + dockerfile: infra/docker/vite.prod + args: + - VITE_API_URL=${VITE_API_URL:-/api} + - VITE_APP_TITLE=${VITE_APP_TITLE:-My App} + container_name: ${APP_NAME:-no-auth-template}-nginx + ports: + - "${NGINX_HOST_PORT:-22784}:80" + networks: + - app + deploy: + resources: + limits: + cpus: '1.0' + memory: 256M + reservations: + cpus: '0.25' + memory: 64M + restart: unless-stopped + +networks: + app: + driver: bridge diff --git a/frontends/open/dev.compose.yml b/frontends/open/dev.compose.yml new file mode 100644 index 0000000..6443c02 --- /dev/null +++ b/frontends/open/dev.compose.yml @@ -0,0 +1,49 @@ +# ============================================================================= +# AngelaMos | 2026 +# dev.compose.yml +# ============================================================================= +# Development compose - Nginx + Vite dev server with HMR +# Uses .env.development +# ============================================================================= + +name: ${APP_NAME:-no-auth-template}-dev + +services: + nginx: + image: nginx:1.27-alpine + container_name: ${APP_NAME:-no-auth-template}-nginx-dev + ports: + - "${NGINX_HOST_PORT:-58495}:80" + volumes: + - ./infra/nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./infra/nginx/dev.nginx:/etc/nginx/conf.d/default.conf:ro + depends_on: + frontend: + condition: service_started + networks: + - app + restart: unless-stopped + + frontend: + build: + context: ./react-scss + dockerfile: ../infra/docker/vite.dev + container_name: ${APP_NAME:-no-auth-template}-frontend-dev + ports: + - "${FRONTEND_HOST_PORT:-15723}:5173" + volumes: + - ./react-scss:/app + - frontend_modules:/app/node_modules + environment: + - VITE_API_URL=${VITE_API_URL:-/api} + - VITE_APP_TITLE=${VITE_APP_TITLE:-My App} + networks: + - app + restart: unless-stopped + +networks: + app: + driver: bridge + +volumes: + frontend_modules: diff --git a/frontends/open/infra/docker/vite.dev b/frontends/open/infra/docker/vite.dev new file mode 100644 index 0000000..2fafe7f --- /dev/null +++ b/frontends/open/infra/docker/vite.dev @@ -0,0 +1,25 @@ +# ============================================================================= +# AngelaMos | 2026 +# vite.dev +# ============================================================================= +# Development Dockerfile for Vite/React frontend +# Features: pnpm, HMR support, polling for Docker file watching +# ============================================================================= +# syntax=docker/dockerfile:1 + +FROM node:22-slim + +RUN corepack enable && corepack prepare pnpm@latest --activate + +WORKDIR /app + +COPY package.json pnpm-lock.yaml* ./ + +RUN --mount=type=cache,target=/root/.local/share/pnpm/store \ + pnpm install --frozen-lockfile + +COPY . . + +EXPOSE 5173 + +CMD ["pnpm", "dev", "--host", "0.0.0.0"] diff --git a/frontends/open/infra/docker/vite.prod b/frontends/open/infra/docker/vite.prod new file mode 100644 index 0000000..48bbf36 --- /dev/null +++ b/frontends/open/infra/docker/vite.prod @@ -0,0 +1,59 @@ +# ============================================================================= +# AngelaMos | 2026 +# vite.prod +# ============================================================================= +# Production Dockerfile: builds Vite app, serves via Nginx +# ============================================================================= +# syntax=docker/dockerfile:1 + +# ============================================================================ +# BUILD STAGE +# ============================================================================ +FROM node:22-slim AS builder + +RUN corepack enable && corepack prepare pnpm@latest --activate + +WORKDIR /app + +COPY react-scss/package.json react-scss/pnpm-lock.yaml* ./ + +RUN --mount=type=cache,target=/root/.local/share/pnpm/store \ + pnpm install --frozen-lockfile + +COPY react-scss/ . + +ARG VITE_API_URL=/api +ARG VITE_APP_TITLE="My App" + +ENV VITE_API_URL=${VITE_API_URL} \ + VITE_APP_TITLE=${VITE_APP_TITLE} + +RUN pnpm build + +# ============================================================================ +# PRODUCTION STAGE +# ============================================================================ +FROM nginx:1.27-alpine AS production + +RUN rm -rf /usr/share/nginx/html/* && \ + rm /etc/nginx/conf.d/default.conf + +COPY --from=builder /app/dist /usr/share/nginx/html + +COPY --chown=nginx:nginx infra/nginx/nginx.prod.conf /etc/nginx/nginx.conf +COPY --chown=nginx:nginx infra/nginx/prod.nginx /etc/nginx/conf.d/default.conf + +RUN chown -R nginx:nginx /usr/share/nginx/html && \ + chown -R nginx:nginx /var/cache/nginx && \ + chown -R nginx:nginx /var/log/nginx && \ + touch /var/run/nginx.pid && \ + chown -R nginx:nginx /var/run/nginx.pid + +USER nginx + +EXPOSE 80 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:80/health || exit 1 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/frontends/open/infra/nginx/dev.nginx b/frontends/open/infra/nginx/dev.nginx new file mode 100644 index 0000000..7ea1f6e --- /dev/null +++ b/frontends/open/infra/nginx/dev.nginx @@ -0,0 +1,35 @@ +# ============================================================================= +# AngelaMos | 2026 +# dev.nginx +# ============================================================================= +# Development server block: proxies to Vite dev server with HMR + +server { + listen 80; + listen [::]:80; + server_name _; + + access_log /var/log/nginx/access.log main_timed; + error_log /var/log/nginx/error.log debug; + + add_header Cache-Control "no-store, no-cache, must-revalidate" always; + + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + + location / { + proxy_pass http://frontend_dev; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_read_timeout 60s; + proxy_buffering off; + } +} diff --git a/frontends/open/infra/nginx/nginx.conf b/frontends/open/infra/nginx/nginx.conf new file mode 100644 index 0000000..eddade5 --- /dev/null +++ b/frontends/open/infra/nginx/nginx.conf @@ -0,0 +1,76 @@ +# AngelaMos | 2026 +# nginx.conf +# Development nginx configuration + +user nginx; +worker_processes auto; +worker_rlimit_nofile 65535; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 4096; + multi_accept on; + use epoll; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + upstream frontend_dev { + server frontend:5173; + keepalive 8; + } + + limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; + limit_conn_zone $binary_remote_addr zone=conn_limit:10m; + limit_req_status 429; + + log_format main_timed '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" ' + 'rt=$request_time uct="$upstream_connect_time" ' + 'uht="$upstream_header_time" urt="$upstream_response_time"'; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + server_tokens off; + + client_body_buffer_size 128k; + client_header_buffer_size 16k; + client_max_body_size 10m; + large_client_header_buffers 4 16k; + + client_body_timeout 12s; + client_header_timeout 12s; + send_timeout 10s; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_min_length 256; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + gzip_disable "msie6"; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/frontends/open/infra/nginx/nginx.prod.conf b/frontends/open/infra/nginx/nginx.prod.conf new file mode 100644 index 0000000..f73ad69 --- /dev/null +++ b/frontends/open/infra/nginx/nginx.prod.conf @@ -0,0 +1,65 @@ +# AngelaMos | 2026 +# nginx.prod.conf +# Production nginx configuration + +worker_processes auto; +worker_rlimit_nofile 65535; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 4096; + multi_accept on; + use epoll; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; + limit_conn_zone $binary_remote_addr zone=conn_limit:10m; + limit_req_status 429; + + log_format main_timed '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" ' + 'rt=$request_time uct="$upstream_connect_time" ' + 'uht="$upstream_header_time" urt="$upstream_response_time"'; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + server_tokens off; + + client_body_buffer_size 128k; + client_header_buffer_size 16k; + client_max_body_size 10m; + large_client_header_buffers 4 16k; + + client_body_timeout 12s; + client_header_timeout 12s; + send_timeout 10s; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_min_length 256; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + gzip_disable "msie6"; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/frontends/open/infra/nginx/prod.nginx b/frontends/open/infra/nginx/prod.nginx new file mode 100644 index 0000000..f6a747e --- /dev/null +++ b/frontends/open/infra/nginx/prod.nginx @@ -0,0 +1,54 @@ +# ============================================================================= +# AngelaMos | 2026 +# prod.nginx +# ============================================================================= +# Production server block: serves built static files +# Note: SSL handled by Cloudflare, not here + +server { + listen 80; + listen [::]:80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + access_log /var/log/nginx/access.log main_timed buffer=32k flush=5s; + error_log /var/log/nginx/error.log warn; + + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + + location /assets/ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + try_files $uri =404; + } + + location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|avif|woff|woff2|ttf|eot|otf)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + location / { + add_header Cache-Control "no-cache, must-revalidate"; + try_files $uri $uri/ /index.html; + } + + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } +} diff --git a/frontends/open/justfile b/frontends/open/justfile new file mode 100644 index 0000000..bb48190 --- /dev/null +++ b/frontends/open/justfile @@ -0,0 +1,159 @@ +# ============================================================================= +# AngelaMos | 2026 +# justfile +# ============================================================================= + +set dotenv-filename := ".env.development" +set dotenv-load +set export +set shell := ["bash", "-uc"] +set windows-shell := ["powershell.exe", "-NoLogo", "-Command"] + +project := file_name(justfile_directory()) +version := `git describe --tags --always 2>/dev/null || echo "dev"` + +# ============================================================================= +# Default +# ============================================================================= + +default: + @just --list --unsorted + +# ============================================================================= +# Frontend Linting +# ============================================================================= + +[group('lint')] +biome *ARGS: + cd react-scss && pnpm biome check . {{ARGS}} + +[group('lint')] +biome-fix: + cd react-scss && pnpm biome check --write . + +[group('lint')] +stylelint *ARGS: + cd react-scss && pnpm stylelint '**/*.scss' {{ARGS}} + +[group('lint')] +stylelint-fix: + cd react-scss && pnpm stylelint '**/*.scss' --fix + +[group('lint')] +tsc *ARGS: + cd react-scss && pnpm tsc --noEmit {{ARGS}} + +# ============================================================================= +# Docker Compose (Production) +# ============================================================================= + +[group('prod')] +up *ARGS: + docker compose --env-file .env up {{ARGS}} + +[group('prod')] +start *ARGS: + docker compose --env-file .env up -d {{ARGS}} + +[group('prod')] +down *ARGS: + docker compose --env-file .env down {{ARGS}} + +[group('prod')] +stop: + docker compose --env-file .env stop + +[group('prod')] +build *ARGS: + docker compose --env-file .env build {{ARGS}} + +[group('prod')] +rebuild: + docker compose --env-file .env build --no-cache + +[group('prod')] +logs *SERVICE: + docker compose --env-file .env logs -f {{SERVICE}} + +[group('prod')] +ps: + docker compose --env-file .env ps + +# ============================================================================= +# Docker Compose (Production + Cloudflare Tunnel) +# ============================================================================= + +[group('tunnel')] +tunnel-up *ARGS: + docker compose --env-file .env -f compose.yml -f cloudflared.compose.yml up {{ARGS}} + +[group('tunnel')] +tunnel-start *ARGS: + docker compose --env-file .env -f compose.yml -f cloudflared.compose.yml up -d {{ARGS}} + +[group('tunnel')] +tunnel-down *ARGS: + docker compose --env-file .env -f compose.yml -f cloudflared.compose.yml down {{ARGS}} + +[group('tunnel')] +tunnel-logs: + docker compose --env-file .env -f compose.yml -f cloudflared.compose.yml logs -f cloudflared + +# ============================================================================= +# Docker Compose (Development) +# ============================================================================= + +[group('dev')] +dev-up *ARGS: + docker compose -f dev.compose.yml up {{ARGS}} + +[group('dev')] +dev-start *ARGS: + docker compose -f dev.compose.yml up -d {{ARGS}} + +[group('dev')] +dev-down *ARGS: + docker compose -f dev.compose.yml down {{ARGS}} + +[group('dev')] +dev-stop: + docker compose -f dev.compose.yml stop + +[group('dev')] +dev-build *ARGS: + docker compose -f dev.compose.yml build {{ARGS}} + +[group('dev')] +dev-rebuild: + docker compose -f dev.compose.yml build --no-cache + +[group('dev')] +dev-logs *SERVICE: + docker compose -f dev.compose.yml logs -f {{SERVICE}} + +[group('dev')] +dev-ps: + docker compose -f dev.compose.yml ps + +# ============================================================================= +# Utilities +# ============================================================================= + +[group('util')] +init: + bash scripts/init.sh + +[group('util')] +ports: + bash scripts/randomize-ports.sh + +[group('util')] +info: + @echo "Project: {{project}}" + @echo "Version: {{version}}" + @echo "OS: {{os()}} ({{arch()}})" + +[group('util')] +clean: + -rm -rf react-scss/.biome_cache + @echo "Cache directories cleaned" diff --git a/frontends/open/react-scss/.dockerignore b/frontends/open/react-scss/.dockerignore new file mode 100644 index 0000000..a0256ec --- /dev/null +++ b/frontends/open/react-scss/.dockerignore @@ -0,0 +1,15 @@ +node_modules +build +dist +.git +.gitignore +*.md +.env* +.vscode +.idea +*.log +npm-debug.log* +pnpm-debug.log* +.DS_Store +coverage +.nyc_output diff --git a/frontends/open/react-scss/.gitignore b/frontends/open/react-scss/.gitignore new file mode 100644 index 0000000..61cb0c2 --- /dev/null +++ b/frontends/open/react-scss/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local +.vite + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontends/open/react-scss/.stylelintignore b/frontends/open/react-scss/.stylelintignore new file mode 100755 index 0000000..37da03e --- /dev/null +++ b/frontends/open/react-scss/.stylelintignore @@ -0,0 +1,22 @@ +# ©AngelaMos | 2025 +# .stylelintignore + +# Dependencies +node_modules/ + +# Production builds +dist/ +build/ +out/ + +# JS/TS files +**/*.js +**/*.jsx +**/*.ts +**/*.tsx + +# Generated files +*.min.css + +# Error system styles - ignore from linting +src/core/app/_toastStyles.scss diff --git a/frontends/open/react-scss/biome.json b/frontends/open/react-scss/biome.json new file mode 100644 index 0000000..7f29029 --- /dev/null +++ b/frontends/open/react-scss/biome.json @@ -0,0 +1,94 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "includes": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.json"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 82, + "lineEnding": "lf" + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "jsxQuoteStyle": "double", + "semicolons": "asNeeded", + "trailingCommas": "es5", + "arrowParentheses": "always" + } + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "complexity": { + "noExcessiveCognitiveComplexity": { + "level": "error", + "options": { "maxAllowedComplexity": 25 } + }, + "noForEach": "off", + "useLiteralKeys": "off" + }, + "correctness": { + "noUnusedVariables": "error", + "noUnusedImports": "error", + "useExhaustiveDependencies": "warn", + "useHookAtTopLevel": "error", + "noUndeclaredVariables": "error" + }, + "style": { + "useImportType": "error", + "useConst": "error", + "useTemplate": "error", + "useSelfClosingElements": "error", + "useFragmentSyntax": "error", + "noNonNullAssertion": "error", + "useConsistentArrayType": { + "level": "error", + "options": { "syntax": "shorthand" } + }, + "useNamingConvention": "off" + }, + "suspicious": { + "noExplicitAny": "error", + "noDebugger": "error", + "noConsole": "warn", + "noArrayIndexKey": "warn", + "noAssignInExpressions": "error", + "noDoubleEquals": "error", + "noRedeclare": "error", + "noVar": "error" + }, + "security": { + "noDangerouslySetInnerHtml": "error" + }, + "a11y": { + "useAltText": "error", + "useAnchorContent": "error", + "useKeyWithClickEvents": "error", + "noStaticElementInteractions": "error", + "useButtonType": "error", + "useValidAnchor": "error" + } + } + }, + "overrides": [ + { + "includes": ["src/main.tsx"], + "linter": { + "rules": { + "style": { + "noNonNullAssertion": "off" + } + } + } + } + ] +} diff --git a/frontends/open/react-scss/index.html b/frontends/open/react-scss/index.html new file mode 100644 index 0000000..6ba30cc --- /dev/null +++ b/frontends/open/react-scss/index.html @@ -0,0 +1,48 @@ + + + + + + + + + + + No Auth Template + + + + +
+ + + diff --git a/frontends/open/react-scss/package.json b/frontends/open/react-scss/package.json new file mode 100644 index 0000000..88da80f --- /dev/null +++ b/frontends/open/react-scss/package.json @@ -0,0 +1,50 @@ +{ + "name": "no-auth-template", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview", + "lint": "biome check .", + "lint:fix": "biome check --write .", + "format": "biome format --write .", + "typecheck": "tsc -b", + "lint:scss": "stylelint '**/*.scss'", + "lint:scss:fix": "stylelint '**/*.scss' --fix" + }, + "dependencies": { + "@tanstack/react-query": "^5.90.12", + "axios": "^1.13.0", + "react": "^19.2.1", + "react-dom": "^19.2.0", + "react-error-boundary": "^6.0.0", + "react-icon": "^1.0.0", + "react-icons": "^5.5.0", + "react-router-dom": "^7.1.1", + "sonner": "^2.0.7", + "zod": "^4.1.13", + "zustand": "^5.0.9" + }, + "devDependencies": { + "@biomejs/biome": "^2.3.8", + "@tanstack/react-query-devtools": "^5.91.1", + "@types/node": "^24.10.2", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "sass": "^1.95.0", + "stylelint": "^16.26.1", + "stylelint-config-prettier-scss": "^1.0.0", + "stylelint-config-standard-scss": "^16.0.0", + "typescript": "~5.9.3", + "vite": "npm:rolldown-vite@7.2.5", + "vite-tsconfig-paths": "^5.1.0" + }, + "pnpm": { + "overrides": { + "vite": "npm:rolldown-vite@7.2.5" + } + } +} diff --git a/frontends/open/react-scss/pnpm-lock.yaml b/frontends/open/react-scss/pnpm-lock.yaml new file mode 100644 index 0000000..d04a716 --- /dev/null +++ b/frontends/open/react-scss/pnpm-lock.yaml @@ -0,0 +1,2603 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + vite: npm:rolldown-vite@7.2.5 + +importers: + + .: + dependencies: + '@tanstack/react-query': + specifier: ^5.90.12 + version: 5.90.12(react@19.2.1) + axios: + specifier: ^1.13.0 + version: 1.13.2 + react: + specifier: ^19.2.1 + version: 19.2.1 + react-dom: + specifier: ^19.2.0 + version: 19.2.1(react@19.2.1) + react-error-boundary: + specifier: ^6.0.0 + version: 6.0.0(react@19.2.1) + react-icon: + specifier: ^1.0.0 + version: 1.0.0(babel-runtime@5.8.38)(react@19.2.1) + react-icons: + specifier: ^5.5.0 + version: 5.5.0(react@19.2.1) + react-router-dom: + specifier: ^7.1.1 + version: 7.10.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + sonner: + specifier: ^2.0.7 + version: 2.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + zod: + specifier: ^4.1.13 + version: 4.1.13 + zustand: + specifier: ^5.0.9 + version: 5.0.9(@types/react@19.2.7)(react@19.2.1) + devDependencies: + '@biomejs/biome': + specifier: ^2.3.8 + version: 2.3.8 + '@tanstack/react-query-devtools': + specifier: ^5.91.1 + version: 5.91.1(@tanstack/react-query@5.90.12(react@19.2.1))(react@19.2.1) + '@types/node': + specifier: ^24.10.2 + version: 24.10.2 + '@types/react': + specifier: ^19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@vitejs/plugin-react': + specifier: ^5.1.1 + version: 5.1.2(rolldown-vite@7.2.5(@types/node@24.10.2)(sass@1.95.0)) + sass: + specifier: ^1.95.0 + version: 1.95.0 + stylelint: + specifier: ^16.26.1 + version: 16.26.1(typescript@5.9.3) + stylelint-config-prettier-scss: + specifier: ^1.0.0 + version: 1.0.0(stylelint@16.26.1(typescript@5.9.3)) + stylelint-config-standard-scss: + specifier: ^16.0.0 + version: 16.0.0(postcss@8.5.6)(stylelint@16.26.1(typescript@5.9.3)) + typescript: + specifier: ~5.9.3 + version: 5.9.3 + vite: + specifier: npm:rolldown-vite@7.2.5 + version: rolldown-vite@7.2.5(@types/node@24.10.2)(sass@1.95.0) + vite-tsconfig-paths: + specifier: ^5.1.0 + version: 5.1.4(rolldown-vite@7.2.5(@types/node@24.10.2)(sass@1.95.0))(typescript@5.9.3) + +packages: + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@biomejs/biome@2.3.8': + resolution: {integrity: sha512-Qjsgoe6FEBxWAUzwFGFrB+1+M8y/y5kwmg5CHac+GSVOdmOIqsAiXM5QMVGZJ1eCUCLlPZtq4aFAQ0eawEUuUA==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.3.8': + resolution: {integrity: sha512-HM4Zg9CGQ3txTPflxD19n8MFPrmUAjaC7PQdLkugeeC0cQ+PiVrd7i09gaBS/11QKsTDBJhVg85CEIK9f50Qww==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.3.8': + resolution: {integrity: sha512-lUDQ03D7y/qEao7RgdjWVGCu+BLYadhKTm40HkpJIi6kn8LSv5PAwRlew/DmwP4YZ9ke9XXoTIQDO1vAnbRZlA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.3.8': + resolution: {integrity: sha512-PShR4mM0sjksUMyxbyPNMxoKFPVF48fU8Qe8Sfx6w6F42verbwRLbz+QiKNiDPRJwUoMG1nPM50OBL3aOnTevA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.3.8': + resolution: {integrity: sha512-Uo1OJnIkJgSgF+USx970fsM/drtPcQ39I+JO+Fjsaa9ZdCN1oysQmy6oAGbyESlouz+rzEckLTF6DS7cWse95g==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.3.8': + resolution: {integrity: sha512-YGLkqU91r1276uwSjiUD/xaVikdxgV1QpsicT0bIA1TaieM6E5ibMZeSyjQ/izBn4tKQthUSsVZacmoJfa3pDA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.3.8': + resolution: {integrity: sha512-QDPMD5bQz6qOVb3kiBui0zKZXASLo0NIQ9JVJio5RveBEFgDgsvJFUvZIbMbUZT3T00M/1wdzwWXk4GIh0KaAw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.3.8': + resolution: {integrity: sha512-H4IoCHvL1fXKDrTALeTKMiE7GGWFAraDwBYFquE/L/5r1927Te0mYIGseXi4F+lrrwhSWbSGt5qPFswNoBaCxg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.3.8': + resolution: {integrity: sha512-RguzimPoZWtBapfKhKjcWXBVI91tiSprqdBYu7tWhgN8pKRZhw24rFeNZTNf6UiBfjCYCi9eFQs/JzJZIhuK4w==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@cacheable/memory@2.0.6': + resolution: {integrity: sha512-7e8SScMocHxcAb8YhtkbMhGG+EKLRIficb1F5sjvhSYsWTZGxvg4KIDp8kgxnV2PUJ3ddPe6J9QESjKvBWRDkg==} + + '@cacheable/utils@2.3.2': + resolution: {integrity: sha512-8kGE2P+HjfY8FglaOiW+y8qxcaQAfAhVML+i66XJR3YX5FtyDqn6Txctr3K2FrbxLKixRRYYBWMbuGciOhYNDg==} + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-syntax-patches-for-csstree@1.0.20': + resolution: {integrity: sha512-8BHsjXfSciZxjmHQOuVdW2b8WLUPts9a+mfL13/PzEviufUEW2xnvQuOlKs9dRBHgRqJ53SF/DUoK9+MZk72oQ==} + engines: {node: '>=18'} + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + + '@csstools/media-query-list-parser@4.0.3': + resolution: {integrity: sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/selector-specificity@5.0.0': + resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==} + engines: {node: '>=18'} + peerDependencies: + postcss-selector-parser: ^7.0.0 + + '@dual-bundle/import-meta-resolve@4.2.1': + resolution: {integrity: sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==} + + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} + + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@keyv/bigmap@1.3.0': + resolution: {integrity: sha512-KT01GjzV6AQD5+IYrcpoYLkCu1Jod3nau1Z7EsEuViO3TZGRacSbO9MfHmbJ1WaOXFtWLxPVj169cn2WNKPkIg==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.5.4 + + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + + '@napi-rs/wasm-runtime@1.1.0': + resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@oxc-project/runtime@0.97.0': + resolution: {integrity: sha512-yH0zw7z+jEws4dZ4IUKoix5Lh3yhqIJWF9Dc8PWvhpo7U7O+lJrv7ZZL4BeRO0la8LBQFwcCewtLBnVV7hPe/w==} + engines: {node: ^20.19.0 || >=22.12.0} + + '@oxc-project/types@0.97.0': + resolution: {integrity: sha512-lxmZK4xFrdvU0yZiDwgVQTCvh2gHWBJCBk5ALsrtsBWhs0uDIi+FTOnXRQeQfs304imdvTdaakT/lqwQ8hkOXQ==} + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + + '@rolldown/binding-android-arm64@1.0.0-beta.50': + resolution: {integrity: sha512-XlEkrOIHLyGT3avOgzfTFSjG+f+dZMw+/qd+Y3HLN86wlndrB/gSimrJCk4gOhr1XtRtEKfszpadI3Md4Z4/Ag==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-beta.50': + resolution: {integrity: sha512-+JRqKJhoFlt5r9q+DecAGPLZ5PxeLva+wCMtAuoFMWPoZzgcYrr599KQ+Ix0jwll4B4HGP43avu9My8KtSOR+w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-beta.50': + resolution: {integrity: sha512-fFXDjXnuX7/gQZQm/1FoivVtRcyAzdjSik7Eo+9iwPQ9EgtA5/nB2+jmbzaKtMGG3q+BnZbdKHCtOacmNrkIDA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-beta.50': + resolution: {integrity: sha512-F1b6vARy49tjmT/hbloplzgJS7GIvwWZqt+tAHEstCh0JIh9sa8FAMVqEmYxDviqKBaAI8iVvUREm/Kh/PD26Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.50': + resolution: {integrity: sha512-U6cR76N8T8M6lHj7EZrQ3xunLPxSvYYxA8vJsBKZiFZkT8YV4kjgCO3KwMJL0NOjQCPGKyiXO07U+KmJzdPGRw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.50': + resolution: {integrity: sha512-ONgyjofCrrE3bnh5GZb8EINSFyR/hmwTzZ7oVuyUB170lboza1VMCnb8jgE6MsyyRgHYmN8Lb59i3NKGrxrYjw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.50': + resolution: {integrity: sha512-L0zRdH2oDPkmB+wvuTl+dJbXCsx62SkqcEqdM+79LOcB+PxbAxxjzHU14BuZIQdXcAVDzfpMfaHWzZuwhhBTcw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.50': + resolution: {integrity: sha512-gyoI8o/TGpQd3OzkJnh1M2kxy1Bisg8qJ5Gci0sXm9yLFzEXIFdtc4EAzepxGvrT2ri99ar5rdsmNG0zP0SbIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.50': + resolution: {integrity: sha512-zti8A7M+xFDpKlghpcCAzyOi+e5nfUl3QhU023ce5NCgUxRG5zGP2GR9LTydQ1rnIPwZUVBWd4o7NjZDaQxaXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.50': + resolution: {integrity: sha512-eZUssog7qljrrRU9Mi0eqYEPm3Ch0UwB+qlWPMKSUXHNqhm3TvDZarJQdTevGEfu3EHAXJvBIe0YFYr0TPVaMA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.50': + resolution: {integrity: sha512-nmCN0nIdeUnmgeDXiQ+2HU6FT162o+rxnF7WMkBm4M5Ds8qTU7Dzv2Wrf22bo4ftnlrb2hKK6FSwAJSAe2FWLg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.50': + resolution: {integrity: sha512-7kcNLi7Ua59JTTLvbe1dYb028QEPaJPJQHqkmSZ5q3tJueUeb6yjRtx8mw4uIqgWZcnQHAR3PrLN4XRJxvgIkA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.50': + resolution: {integrity: sha512-lL70VTNvSCdSZkDPPVMwWn/M2yQiYvSoXw9hTLgdIWdUfC3g72UaruezusR6ceRuwHCY1Ayu2LtKqXkBO5LIwg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.50': + resolution: {integrity: sha512-4qU4x5DXWB4JPjyTne/wBNPqkbQU8J45bl21geERBKtEittleonioACBL1R0PsBu0Aq21SwMK5a9zdBkWSlQtQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-beta.50': + resolution: {integrity: sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==} + + '@rolldown/pluginutils@1.0.0-beta.53': + resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} + + '@tanstack/query-core@5.90.12': + resolution: {integrity: sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg==} + + '@tanstack/query-devtools@5.91.1': + resolution: {integrity: sha512-l8bxjk6BMsCaVQH6NzQEE/bEgFy1hAs5qbgXl0xhzezlaQbPk6Mgz9BqEg2vTLPOHD8N4k+w/gdgCbEzecGyNg==} + + '@tanstack/react-query-devtools@5.91.1': + resolution: {integrity: sha512-tRnJYwEbH0kAOuToy8Ew7bJw1lX3AjkkgSlf/vzb+NpnqmHPdWM+lA2DSdGQSLi1SU0PDRrrCI1vnZnci96CsQ==} + peerDependencies: + '@tanstack/react-query': ^5.90.10 + react: ^18 || ^19 + + '@tanstack/react-query@5.90.12': + resolution: {integrity: sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg==} + peerDependencies: + react: ^18 || ^19 + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/node@24.10.2': + resolution: {integrity: sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + + '@vitejs/plugin-react@5.1.2': + resolution: {integrity: sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.13.2: + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + + babel-runtime@5.8.38: + resolution: {integrity: sha512-KpgoA8VE/pMmNCrnEeeXqFG24TIH11Z3ZaimIhJWsin8EbfZy3WzFKUTIan10ZIDgRVvi9EkLbruJElJC9dRlg==} + + balanced-match@2.0.0: + resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + + baseline-browser-mapping@2.9.5: + resolution: {integrity: sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA==} + hasBin: true + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cacheable@2.3.0: + resolution: {integrity: sha512-HHiAvOBmlcR2f3SQ7kdlYD8+AUJG+wlFZ/Ze8tl1Vzvz0MdOh8IYA/EFU4ve8t1/sZ0j4MGi7ST5MoTwHessQA==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001759: + resolution: {integrity: sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + core-js@1.2.7: + resolution: {integrity: sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA==} + deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + css-functions-list@3.2.3: + resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==} + engines: {node: '>=12 || >=16'} + + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@11.1.1: + resolution: {integrity: sha512-TPVFSDE7q91Dlk1xpFLvFllf8r0HyOMOlnWy7Z2HBku5H3KhIeOGInexrIeg2D64DosVB/JXkrrk6N/7Wriq4A==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + flat-cache@6.1.19: + resolution: {integrity: sha512-l/K33newPTZMTGAnnzaiqSl6NnH7Namh8jBNjrgjprWxGmZUuxx/sJNIRaijOh3n7q7ESbhNZC+pvVZMFdeU4A==} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hashery@1.3.0: + resolution: {integrity: sha512-fWltioiy5zsSAs9ouEnvhsVJeAXRybGCNNv0lvzpzNOSDbULXRy7ivFWwCCv4I5Am6kSo75hmbsCduOoc2/K4w==} + engines: {node: '>=20'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hookified@1.14.0: + resolution: {integrity: sha512-pi1ynXIMFx/uIIwpWJ/5CEtOHLGtnUB0WhGeeYT+fKcQ+WCQbm3/rrkAXnpfph++PgepNqPdTC2WTj8A6k6zoQ==} + + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immutable@5.1.4: + resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + keyv@5.5.5: + resolution: {integrity: sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + known-css-properties@0.37.0: + resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} + + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + engines: {node: '>= 12.0.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mathml-tag-names@2.1.3: + resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + + mdn-data@2.25.0: + resolution: {integrity: sha512-T2LPsjgUE/tgMmRXREVmwsux89DwWfNjiynOeXuLd2mX6jphGQ2YE3Ukz7LQ2VOFKiVZU/Ee1GqzHiipZCjymw==} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss-media-query-parser@0.2.3: + resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} + + postcss-resolve-nested-selector@0.1.6: + resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} + + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + qified@0.5.3: + resolution: {integrity: sha512-kXuQdQTB6oN3KhI6V4acnBSZx8D2I4xzZvn9+wFLLFCoBNQY/sFnCW6c43OL7pOQ2HvGV4lnWIXNmgfp7cTWhQ==} + engines: {node: '>=20'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@19.2.1: + resolution: {integrity: sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==} + peerDependencies: + react: ^19.2.1 + + react-error-boundary@6.0.0: + resolution: {integrity: sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==} + peerDependencies: + react: '>=16.13.1' + + react-icon@1.0.0: + resolution: {integrity: sha512-VzSlpBHnLanVw79mOxyq98hWDi6DlxK9qPiZ1bAK6bLurMBCaxO/jjyYUrRx9+JGLc/NbnwOmyE/W5Qglbb2QA==} + peerDependencies: + babel-runtime: ^5.3.3 + react: '>=0.12.0' + + react-icons@5.5.0: + resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==} + peerDependencies: + react: '*' + + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + + react-router-dom@7.10.1: + resolution: {integrity: sha512-JNBANI6ChGVjA5bwsUIwJk7LHKmqB4JYnYfzFwyp2t12Izva11elds2jx7Yfoup2zssedntwU0oZ5DEmk5Sdaw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.10.1: + resolution: {integrity: sha512-gHL89dRa3kwlUYtRQ+m8NmxGI6CgqN+k4XyGjwcFoQwwCWF6xXpOCUlDovkXClS0d0XJN/5q7kc5W3kiFEd0Yw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react@19.2.1: + resolution: {integrity: sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==} + engines: {node: '>=0.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rolldown-vite@7.2.5: + resolution: {integrity: sha512-u09tdk/huMiN8xwoiBbig197jKdCamQTtOruSalOzbqGje3jdHiV0njQlAW0YvzoahkirFePNQ4RYlfnRQpXZA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + esbuild: ^0.25.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + rolldown@1.0.0-beta.50: + resolution: {integrity: sha512-JFULvCNl/anKn99eKjOSEubi0lLmNqQDAjyEMME2T4CwezUDL0i6t1O9xZsu2OMehPnV2caNefWpGF+8TnzB6A==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + sass@1.95.0: + resolution: {integrity: sha512-9QMjhLq+UkOg/4bb8Lt8A+hJZvY3t+9xeZMKSBtBEgxrXA3ed5Ts4NDreUkYgJP1BTmrscQE/xYhf7iShow6lw==} + engines: {node: '>=14.0.0'} + hasBin: true + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + sonner@2.0.7: + resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + stylelint-config-prettier-scss@1.0.0: + resolution: {integrity: sha512-Gr2qLiyvJGKeDk0E/+awNTrZB/UtNVPLqCDOr07na/sLekZwm26Br6yYIeBYz3ulsEcQgs5j+2IIMXCC+wsaQA==} + engines: {node: 14.* || 16.* || >= 18} + hasBin: true + peerDependencies: + stylelint: '>=15.0.0' + + stylelint-config-recommended-scss@16.0.2: + resolution: {integrity: sha512-aUTHhPPWCvFyWaxtckJlCPaXTDFsp4pKO8evXNCsW9OwsaUWyMd6jvcUhSmfGWPrTddvzNqK4rS/UuSLcbVGdQ==} + engines: {node: '>=20'} + peerDependencies: + postcss: ^8.3.3 + stylelint: ^16.24.0 + peerDependenciesMeta: + postcss: + optional: true + + stylelint-config-recommended@17.0.0: + resolution: {integrity: sha512-WaMSdEiPfZTSFVoYmJbxorJfA610O0tlYuU2aEwY33UQhSPgFbClrVJYWvy3jGJx+XW37O+LyNLiZOEXhKhJmA==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.23.0 + + stylelint-config-standard-scss@16.0.0: + resolution: {integrity: sha512-/FHECLUu+med/e6OaPFpprG86ShC4SYT7Tzb2PTVdDjJsehhFBOioSlWqYFqJxmGPIwO3AMBxNo+kY3dxrbczA==} + engines: {node: '>=20'} + peerDependencies: + postcss: ^8.3.3 + stylelint: ^16.23.1 + peerDependenciesMeta: + postcss: + optional: true + + stylelint-config-standard@39.0.1: + resolution: {integrity: sha512-b7Fja59EYHRNOTa3aXiuWnhUWXFU2Nfg6h61bLfAb5GS5fX3LMUD0U5t4S8N/4tpHQg3Acs2UVPR9jy2l1g/3A==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.23.0 + + stylelint-scss@6.13.0: + resolution: {integrity: sha512-kZPwFUJkfup2gP1enlrS2h9U5+T5wFoqzJ1n/56AlpwSj28kmFe7ww/QFydvPsg5gLjWchAwWWBLtterynZrOw==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.8.2 + + stylelint@16.26.1: + resolution: {integrity: sha512-v20V59/crfc8sVTAtge0mdafI3AdnzQ2KsWe6v523L4OA1bJO02S7MO2oyXDCS6iWb9ckIPnqAFVItqSBQr7jw==} + engines: {node: '>=18.12.0'} + hasBin: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-hyperlinks@3.2.0: + resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} + engines: {node: '>=14.18'} + + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + update-browserslist-db@1.2.2: + resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + zod@4.1.13: + resolution: {integrity: sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==} + + zustand@5.0.9: + resolution: {integrity: sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + +snapshots: + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.5': {} + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/runtime@7.28.4': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@biomejs/biome@2.3.8': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.3.8 + '@biomejs/cli-darwin-x64': 2.3.8 + '@biomejs/cli-linux-arm64': 2.3.8 + '@biomejs/cli-linux-arm64-musl': 2.3.8 + '@biomejs/cli-linux-x64': 2.3.8 + '@biomejs/cli-linux-x64-musl': 2.3.8 + '@biomejs/cli-win32-arm64': 2.3.8 + '@biomejs/cli-win32-x64': 2.3.8 + + '@biomejs/cli-darwin-arm64@2.3.8': + optional: true + + '@biomejs/cli-darwin-x64@2.3.8': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.3.8': + optional: true + + '@biomejs/cli-linux-arm64@2.3.8': + optional: true + + '@biomejs/cli-linux-x64-musl@2.3.8': + optional: true + + '@biomejs/cli-linux-x64@2.3.8': + optional: true + + '@biomejs/cli-win32-arm64@2.3.8': + optional: true + + '@biomejs/cli-win32-x64@2.3.8': + optional: true + + '@cacheable/memory@2.0.6': + dependencies: + '@cacheable/utils': 2.3.2 + '@keyv/bigmap': 1.3.0(keyv@5.5.5) + hookified: 1.14.0 + keyv: 5.5.5 + + '@cacheable/utils@2.3.2': + dependencies: + hashery: 1.3.0 + keyv: 5.5.5 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-syntax-patches-for-csstree@1.0.20': {} + + '@csstools/css-tokenizer@3.0.4': {} + + '@csstools/media-query-list-parser@4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.1)': + dependencies: + postcss-selector-parser: 7.1.1 + + '@dual-bundle/import-meta-resolve@4.2.1': {} + + '@emnapi/core@1.7.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.7.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@keyv/bigmap@1.3.0(keyv@5.5.5)': + dependencies: + hashery: 1.3.0 + hookified: 1.14.0 + keyv: 5.5.5 + + '@keyv/serialize@1.1.1': {} + + '@napi-rs/wasm-runtime@1.1.0': + dependencies: + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@oxc-project/runtime@0.97.0': {} + + '@oxc-project/types@0.97.0': {} + + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true + + '@rolldown/binding-android-arm64@1.0.0-beta.50': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-beta.50': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-beta.50': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-beta.50': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.50': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.50': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.50': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.50': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.50': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.50': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.50': + dependencies: + '@napi-rs/wasm-runtime': 1.1.0 + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.50': + optional: true + + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.50': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.50': + optional: true + + '@rolldown/pluginutils@1.0.0-beta.50': {} + + '@rolldown/pluginutils@1.0.0-beta.53': {} + + '@tanstack/query-core@5.90.12': {} + + '@tanstack/query-devtools@5.91.1': {} + + '@tanstack/react-query-devtools@5.91.1(@tanstack/react-query@5.90.12(react@19.2.1))(react@19.2.1)': + dependencies: + '@tanstack/query-devtools': 5.91.1 + '@tanstack/react-query': 5.90.12(react@19.2.1) + react: 19.2.1 + + '@tanstack/react-query@5.90.12(react@19.2.1)': + dependencies: + '@tanstack/query-core': 5.90.12 + react: 19.2.1 + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.28.5 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.28.5 + + '@types/node@24.10.2': + dependencies: + undici-types: 7.16.0 + + '@types/react-dom@19.2.3(@types/react@19.2.7)': + dependencies: + '@types/react': 19.2.7 + + '@types/react@19.2.7': + dependencies: + csstype: 3.2.3 + + '@vitejs/plugin-react@5.1.2(rolldown-vite@7.2.5(@types/node@24.10.2)(sass@1.95.0))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) + '@rolldown/pluginutils': 1.0.0-beta.53 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: rolldown-vite@7.2.5(@types/node@24.10.2)(sass@1.95.0) + transitivePeerDependencies: + - supports-color + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + array-union@2.1.0: {} + + astral-regex@2.0.0: {} + + asynckit@0.4.0: {} + + axios@1.13.2: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + babel-runtime@5.8.38: + dependencies: + core-js: 1.2.7 + + balanced-match@2.0.0: {} + + baseline-browser-mapping@2.9.5: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.5 + caniuse-lite: 1.0.30001759 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.2(browserslist@4.28.1) + + cacheable@2.3.0: + dependencies: + '@cacheable/memory': 2.0.6 + '@cacheable/utils': 2.3.2 + hookified: 1.14.0 + keyv: 5.5.5 + qified: 0.5.3 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001759: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colord@2.9.3: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + core-js@1.2.7: {} + + cosmiconfig@9.0.0(typescript@5.9.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.9.3 + + css-functions-list@3.2.3: {} + + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + + cssesc@3.0.0: {} + + csstype@3.2.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + delayed-stream@1.0.0: {} + + detect-libc@1.0.3: + optional: true + + detect-libc@2.1.2: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.267: {} + + emoji-regex@8.0.0: {} + + env-paths@2.2.1: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + escalade@3.2.0: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-uri@3.1.0: {} + + fastest-levenshtein@1.0.16: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@11.1.1: + dependencies: + flat-cache: 6.1.19 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + flat-cache@6.1.19: + dependencies: + cacheable: 2.3.0 + flatted: 3.3.3 + hookified: 1.14.0 + + flatted@3.3.3: {} + + follow-redirects@1.15.11: {} + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globjoin@0.1.4: {} + + globrex@0.1.2: {} + + gopd@1.2.0: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hashery@1.3.0: + dependencies: + hookified: 1.14.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hookified@1.14.0: {} + + html-tags@3.3.1: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + immutable@5.1.4: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + ini@1.3.8: {} + + is-arrayish@0.2.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-plain-object@5.0.0: {} + + isexe@2.0.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@1.0.0: {} + + json5@2.2.3: {} + + keyv@5.5.5: + dependencies: + '@keyv/serialize': 1.1.1 + + kind-of@6.0.3: {} + + known-css-properties@0.37.0: {} + + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 + + lines-and-columns@1.2.4: {} + + lodash.truncate@4.4.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + math-intrinsics@1.1.0: {} + + mathml-tag-names@2.1.3: {} + + mdn-data@2.12.2: {} + + mdn-data@2.25.0: {} + + meow@13.2.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + node-addon-api@7.1.1: + optional: true + + node-releases@2.0.27: {} + + normalize-path@3.0.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-type@4.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + postcss-media-query-parser@0.2.3: {} + + postcss-resolve-nested-selector@0.1.6: {} + + postcss-safe-parser@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-scss@4.0.9(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-selector-parser@7.1.1: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + proxy-from-env@1.1.0: {} + + qified@0.5.3: + dependencies: + hookified: 1.14.0 + + queue-microtask@1.2.3: {} + + react-dom@19.2.1(react@19.2.1): + dependencies: + react: 19.2.1 + scheduler: 0.27.0 + + react-error-boundary@6.0.0(react@19.2.1): + dependencies: + '@babel/runtime': 7.28.4 + react: 19.2.1 + + react-icon@1.0.0(babel-runtime@5.8.38)(react@19.2.1): + dependencies: + babel-runtime: 5.8.38 + react: 19.2.1 + + react-icons@5.5.0(react@19.2.1): + dependencies: + react: 19.2.1 + + react-refresh@0.18.0: {} + + react-router-dom@7.10.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1): + dependencies: + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + react-router: 7.10.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + + react-router@7.10.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1): + dependencies: + cookie: 1.1.1 + react: 19.2.1 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.2.1(react@19.2.1) + + react@19.2.1: {} + + readdirp@4.1.2: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + reusify@1.1.0: {} + + rolldown-vite@7.2.5(@types/node@24.10.2)(sass@1.95.0): + dependencies: + '@oxc-project/runtime': 0.97.0 + fdir: 6.5.0(picomatch@4.0.3) + lightningcss: 1.30.2 + picomatch: 4.0.3 + postcss: 8.5.6 + rolldown: 1.0.0-beta.50 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.10.2 + fsevents: 2.3.3 + sass: 1.95.0 + + rolldown@1.0.0-beta.50: + dependencies: + '@oxc-project/types': 0.97.0 + '@rolldown/pluginutils': 1.0.0-beta.50 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-beta.50 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.50 + '@rolldown/binding-darwin-x64': 1.0.0-beta.50 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.50 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.50 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.50 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.50 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.50 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.50 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.50 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.50 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.50 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.50 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.50 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + sass@1.95.0: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.4 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + set-cookie-parser@2.7.2: {} + + signal-exit@4.1.0: {} + + slash@3.0.0: {} + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + sonner@2.0.7(react-dom@19.2.1(react@19.2.1))(react@19.2.1): + dependencies: + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + source-map-js@1.2.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + stylelint-config-prettier-scss@1.0.0(stylelint@16.26.1(typescript@5.9.3)): + dependencies: + stylelint: 16.26.1(typescript@5.9.3) + + stylelint-config-recommended-scss@16.0.2(postcss@8.5.6)(stylelint@16.26.1(typescript@5.9.3)): + dependencies: + postcss-scss: 4.0.9(postcss@8.5.6) + stylelint: 16.26.1(typescript@5.9.3) + stylelint-config-recommended: 17.0.0(stylelint@16.26.1(typescript@5.9.3)) + stylelint-scss: 6.13.0(stylelint@16.26.1(typescript@5.9.3)) + optionalDependencies: + postcss: 8.5.6 + + stylelint-config-recommended@17.0.0(stylelint@16.26.1(typescript@5.9.3)): + dependencies: + stylelint: 16.26.1(typescript@5.9.3) + + stylelint-config-standard-scss@16.0.0(postcss@8.5.6)(stylelint@16.26.1(typescript@5.9.3)): + dependencies: + stylelint: 16.26.1(typescript@5.9.3) + stylelint-config-recommended-scss: 16.0.2(postcss@8.5.6)(stylelint@16.26.1(typescript@5.9.3)) + stylelint-config-standard: 39.0.1(stylelint@16.26.1(typescript@5.9.3)) + optionalDependencies: + postcss: 8.5.6 + + stylelint-config-standard@39.0.1(stylelint@16.26.1(typescript@5.9.3)): + dependencies: + stylelint: 16.26.1(typescript@5.9.3) + stylelint-config-recommended: 17.0.0(stylelint@16.26.1(typescript@5.9.3)) + + stylelint-scss@6.13.0(stylelint@16.26.1(typescript@5.9.3)): + dependencies: + css-tree: 3.1.0 + is-plain-object: 5.0.0 + known-css-properties: 0.37.0 + mdn-data: 2.25.0 + postcss-media-query-parser: 0.2.3 + postcss-resolve-nested-selector: 0.1.6 + postcss-selector-parser: 7.1.1 + postcss-value-parser: 4.2.0 + stylelint: 16.26.1(typescript@5.9.3) + + stylelint@16.26.1(typescript@5.9.3): + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-syntax-patches-for-csstree': 1.0.20 + '@csstools/css-tokenizer': 3.0.4 + '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.1) + '@dual-bundle/import-meta-resolve': 4.2.1 + balanced-match: 2.0.0 + colord: 2.9.3 + cosmiconfig: 9.0.0(typescript@5.9.3) + css-functions-list: 3.2.3 + css-tree: 3.1.0 + debug: 4.4.3 + fast-glob: 3.3.3 + fastest-levenshtein: 1.0.16 + file-entry-cache: 11.1.1 + global-modules: 2.0.0 + globby: 11.1.0 + globjoin: 0.1.4 + html-tags: 3.3.1 + ignore: 7.0.5 + imurmurhash: 0.1.4 + is-plain-object: 5.0.0 + known-css-properties: 0.37.0 + mathml-tag-names: 2.1.3 + meow: 13.2.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-resolve-nested-selector: 0.1.6 + postcss-safe-parser: 7.0.1(postcss@8.5.6) + postcss-selector-parser: 7.1.1 + postcss-value-parser: 4.2.0 + resolve-from: 5.0.0 + string-width: 4.2.3 + supports-hyperlinks: 3.2.0 + svg-tags: 1.0.0 + table: 6.9.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + - typescript + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-hyperlinks@3.2.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + + svg-tags@1.0.0: {} + + table@6.9.0: + dependencies: + ajv: 8.17.1 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tsconfck@3.1.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + tslib@2.8.1: + optional: true + + typescript@5.9.3: {} + + undici-types@7.16.0: {} + + update-browserslist-db@1.2.2(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + util-deprecate@1.0.2: {} + + vite-tsconfig-paths@5.1.4(rolldown-vite@7.2.5(@types/node@24.10.2)(sass@1.95.0))(typescript@5.9.3): + dependencies: + debug: 4.4.3 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.9.3) + optionalDependencies: + vite: rolldown-vite@7.2.5(@types/node@24.10.2)(sass@1.95.0) + transitivePeerDependencies: + - supports-color + - typescript + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + yallist@3.1.1: {} + + zod@4.1.13: {} + + zustand@5.0.9(@types/react@19.2.7)(react@19.2.1): + optionalDependencies: + '@types/react': 19.2.7 + react: 19.2.1 diff --git a/frontends/open/react-scss/public/assets/android-chrome-192x192.png b/frontends/open/react-scss/public/assets/android-chrome-192x192.png new file mode 100644 index 0000000..6a31898 Binary files /dev/null and b/frontends/open/react-scss/public/assets/android-chrome-192x192.png differ diff --git a/frontends/open/react-scss/public/assets/android-chrome-512x512.png b/frontends/open/react-scss/public/assets/android-chrome-512x512.png new file mode 100644 index 0000000..e79bc03 Binary files /dev/null and b/frontends/open/react-scss/public/assets/android-chrome-512x512.png differ diff --git a/frontends/open/react-scss/public/assets/apple-touch-icon.png b/frontends/open/react-scss/public/assets/apple-touch-icon.png new file mode 100644 index 0000000..be0c040 Binary files /dev/null and b/frontends/open/react-scss/public/assets/apple-touch-icon.png differ diff --git a/frontends/open/react-scss/public/assets/favicon-16x16.png b/frontends/open/react-scss/public/assets/favicon-16x16.png new file mode 100644 index 0000000..32c2cf9 Binary files /dev/null and b/frontends/open/react-scss/public/assets/favicon-16x16.png differ diff --git a/frontends/open/react-scss/public/assets/favicon-32x32.png b/frontends/open/react-scss/public/assets/favicon-32x32.png new file mode 100644 index 0000000..87c75f4 Binary files /dev/null and b/frontends/open/react-scss/public/assets/favicon-32x32.png differ diff --git a/frontends/open/react-scss/public/assets/favicon.ico b/frontends/open/react-scss/public/assets/favicon.ico new file mode 100644 index 0000000..6c43eea Binary files /dev/null and b/frontends/open/react-scss/public/assets/favicon.ico differ diff --git a/frontends/open/react-scss/public/assets/site.webmanifest b/frontends/open/react-scss/public/assets/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/frontends/open/react-scss/public/assets/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/frontends/open/react-scss/src/App.tsx b/frontends/open/react-scss/src/App.tsx new file mode 100644 index 0000000..a7d82d6 --- /dev/null +++ b/frontends/open/react-scss/src/App.tsx @@ -0,0 +1,36 @@ +// =========================== +// ©AngelaMos | 2026 +// App.tsx +// =========================== + +import { QueryClientProvider } from '@tanstack/react-query' +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' +import { RouterProvider } from 'react-router-dom' +import { Toaster } from 'sonner' + +import { queryClient } from '@/core/api' +import { router } from '@/core/app/routers' +import '@/core/app/toast.module.scss' + +export default function App(): React.ReactElement { + return ( + +
+ + +
+ +
+ ) +} diff --git a/frontends/open/react-scss/src/api/hooks/index.ts b/frontends/open/react-scss/src/api/hooks/index.ts new file mode 100644 index 0000000..6221af9 --- /dev/null +++ b/frontends/open/react-scss/src/api/hooks/index.ts @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// index.ts +// =================== diff --git a/frontends/open/react-scss/src/api/index.ts b/frontends/open/react-scss/src/api/index.ts new file mode 100644 index 0000000..0840a7c --- /dev/null +++ b/frontends/open/react-scss/src/api/index.ts @@ -0,0 +1,7 @@ +// =================== +// © AngelaMos | 2025 +// index.ts +// =================== + +export * from './hooks' +export * from './types' diff --git a/frontends/open/react-scss/src/api/types/index.ts b/frontends/open/react-scss/src/api/types/index.ts new file mode 100644 index 0000000..6221af9 --- /dev/null +++ b/frontends/open/react-scss/src/api/types/index.ts @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// index.ts +// =================== diff --git a/frontends/open/react-scss/src/components/index.tsx b/frontends/open/react-scss/src/components/index.tsx new file mode 100644 index 0000000..6b0a732 --- /dev/null +++ b/frontends/open/react-scss/src/components/index.tsx @@ -0,0 +1,4 @@ +/** + * ©AngelaMos | 2025 + * index.tsx + */ diff --git a/frontends/open/react-scss/src/config.ts b/frontends/open/react-scss/src/config.ts new file mode 100644 index 0000000..87c9647 --- /dev/null +++ b/frontends/open/react-scss/src/config.ts @@ -0,0 +1,68 @@ +// =================== +// © AngelaMos | 2026 +// config.ts +// =================== +const API_VERSION = 'v1' + +export const API_ENDPOINTS = { + USERS: { + BASE: `/${API_VERSION}/users`, + BY_ID: (id: string) => `/${API_VERSION}/users/${id}`, + }, +} as const + +export const QUERY_KEYS = { + USERS: { + ALL: ['users'] as const, + BY_ID: (id: string) => [...QUERY_KEYS.USERS.ALL, 'detail', id] as const, + }, +} as const + +export const ROUTES = { + HOME: '/', + DASHBOARD: '/dashboard', + SETTINGS: '/settings', +} as const + +export const STORAGE_KEYS = { + UI: 'ui-storage', +} as const + +export const QUERY_CONFIG = { + STALE_TIME: { + USER: 1000 * 60 * 5, + STATIC: Infinity, + FREQUENT: 1000 * 30, + }, + GC_TIME: { + DEFAULT: 1000 * 60 * 30, + LONG: 1000 * 60 * 60, + }, + RETRY: { + DEFAULT: 3, + NONE: 0, + }, +} as const + +export const HTTP_STATUS = { + OK: 200, + CREATED: 201, + NO_CONTENT: 204, + BAD_REQUEST: 400, + UNAUTHORIZED: 401, + FORBIDDEN: 403, + NOT_FOUND: 404, + CONFLICT: 409, + TOO_MANY_REQUESTS: 429, + INTERNAL_SERVER: 500, +} as const + +export const PAGINATION = { + DEFAULT_PAGE: 1, + DEFAULT_SIZE: 20, + MAX_SIZE: 100, +} as const + +export type ApiEndpoint = typeof API_ENDPOINTS +export type QueryKey = typeof QUERY_KEYS +export type Route = typeof ROUTES diff --git a/frontends/open/react-scss/src/core/api/api.config.ts b/frontends/open/react-scss/src/core/api/api.config.ts new file mode 100644 index 0000000..f48a4d0 --- /dev/null +++ b/frontends/open/react-scss/src/core/api/api.config.ts @@ -0,0 +1,17 @@ +// =================== +// © AngelaMos | 2026 +// api.config.ts +// =================== + +import axios, { type AxiosInstance } from 'axios' + +const getBaseURL = (): string => { + return import.meta.env.VITE_API_URL ?? '/api' +} + +export const apiClient: AxiosInstance = axios.create({ + baseURL: getBaseURL(), + timeout: 15000, + headers: { 'Content-Type': 'application/json' }, + withCredentials: true, +}) diff --git a/frontends/open/react-scss/src/core/api/errors.ts b/frontends/open/react-scss/src/core/api/errors.ts new file mode 100644 index 0000000..fde5ec6 --- /dev/null +++ b/frontends/open/react-scss/src/core/api/errors.ts @@ -0,0 +1,114 @@ +/** + * ©AngelaMos | 2025 + * errors.ts + */ + +import type { AxiosError } from 'axios' + +export const ApiErrorCode = { + NETWORK_ERROR: 'NETWORK_ERROR', + VALIDATION_ERROR: 'VALIDATION_ERROR', + AUTHENTICATION_ERROR: 'AUTHENTICATION_ERROR', + AUTHORIZATION_ERROR: 'AUTHORIZATION_ERROR', + NOT_FOUND: 'NOT_FOUND', + CONFLICT: 'CONFLICT', + RATE_LIMITED: 'RATE_LIMITED', + SERVER_ERROR: 'SERVER_ERROR', + UNKNOWN_ERROR: 'UNKNOWN_ERROR', +} as const + +export type ApiErrorCode = (typeof ApiErrorCode)[keyof typeof ApiErrorCode] + +export class ApiError extends Error { + readonly code: ApiErrorCode + readonly statusCode: number + readonly details?: Record + + constructor( + message: string, + code: ApiErrorCode, + statusCode: number, + details?: Record + ) { + super(message) + this.name = 'ApiError' + this.code = code + this.statusCode = statusCode + this.details = details + } + + getUserMessage(): string { + const messages: Record = { + [ApiErrorCode.NETWORK_ERROR]: + 'Unable to connect. Please check your internet connection.', + [ApiErrorCode.VALIDATION_ERROR]: 'Please check your input and try again.', + [ApiErrorCode.AUTHENTICATION_ERROR]: + 'Your session has expired. Please log in again.', + [ApiErrorCode.AUTHORIZATION_ERROR]: + 'You do not have permission to perform this action.', + [ApiErrorCode.NOT_FOUND]: 'The requested resource was not found.', + [ApiErrorCode.CONFLICT]: + 'This operation conflicts with an existing resource.', + [ApiErrorCode.RATE_LIMITED]: + 'Too many requests. Please wait a moment and try again.', + [ApiErrorCode.SERVER_ERROR]: + 'Something went wrong on our end. Please try again later.', + [ApiErrorCode.UNKNOWN_ERROR]: + 'An unexpected error occurred. Please try again.', + } + return messages[this.code] + } +} + +interface ApiErrorResponse { + detail?: string | { msg: string; type: string }[] + message?: string +} + +export function transformAxiosError(error: AxiosError): ApiError { + if (!error.response) { + return new ApiError('Network error', ApiErrorCode.NETWORK_ERROR, 0) + } + + const { status } = error.response + const data = error.response.data as ApiErrorResponse | undefined + let message = 'An error occurred' + let details: Record | undefined + + if (data?.detail) { + if (typeof data.detail === 'string') { + message = data.detail + } else if (Array.isArray(data.detail)) { + details = { validation: [] } + data.detail.forEach((err) => { + details?.validation.push(err.msg) + }) + message = 'Validation error' + } + } else if (data?.message) { + message = data.message + } + + const codeMap: Record = { + 400: ApiErrorCode.VALIDATION_ERROR, + 401: ApiErrorCode.AUTHENTICATION_ERROR, + 403: ApiErrorCode.AUTHORIZATION_ERROR, + 404: ApiErrorCode.NOT_FOUND, + 409: ApiErrorCode.CONFLICT, + 429: ApiErrorCode.RATE_LIMITED, + 500: ApiErrorCode.SERVER_ERROR, + 502: ApiErrorCode.SERVER_ERROR, + 503: ApiErrorCode.SERVER_ERROR, + 504: ApiErrorCode.SERVER_ERROR, + } + + const code = codeMap[status] || ApiErrorCode.UNKNOWN_ERROR + + return new ApiError(message, code, status, details) +} + +declare module '@tanstack/react-query' { + interface Register { + defaultError: ApiError + } +} diff --git a/frontends/open/react-scss/src/core/api/index.ts b/frontends/open/react-scss/src/core/api/index.ts new file mode 100644 index 0000000..1818cd7 --- /dev/null +++ b/frontends/open/react-scss/src/core/api/index.ts @@ -0,0 +1,8 @@ +// =================== +// © AngelaMos | 2025 +// index.ts +// =================== + +export * from './api.config' +export * from './errors' +export * from './query.config' diff --git a/frontends/open/react-scss/src/core/api/query.config.ts b/frontends/open/react-scss/src/core/api/query.config.ts new file mode 100644 index 0000000..8244b22 --- /dev/null +++ b/frontends/open/react-scss/src/core/api/query.config.ts @@ -0,0 +1,105 @@ +// =================== +// © AngelaMos | 2025 +// query.config.ts +// =================== + +import { MutationCache, QueryCache, QueryClient } from '@tanstack/react-query' +import { toast } from 'sonner' +import { QUERY_CONFIG } from '@/config' +import { ApiError, ApiErrorCode } from './errors' + +const NO_RETRY_ERROR_CODES: readonly ApiErrorCode[] = [ + ApiErrorCode.AUTHENTICATION_ERROR, + ApiErrorCode.AUTHORIZATION_ERROR, + ApiErrorCode.NOT_FOUND, + ApiErrorCode.VALIDATION_ERROR, +] as const + +const shouldRetryQuery = (failureCount: number, error: Error): boolean => { + if (error instanceof ApiError) { + if (NO_RETRY_ERROR_CODES.includes(error.code)) { + return false + } + } + return failureCount < QUERY_CONFIG.RETRY.DEFAULT +} + +const calculateRetryDelay = (attemptIndex: number): number => { + const baseDelay = 1000 + const maxDelay = 30000 + return Math.min(baseDelay * 2 ** attemptIndex, maxDelay) +} + +const handleQueryCacheError = ( + error: Error, + query: { state: { data: unknown } } +): void => { + if (query.state.data !== undefined) { + const message = + error instanceof ApiError + ? error.getUserMessage() + : 'Background update failed' + toast.error(message) + } +} + +const handleMutationCacheError = ( + error: Error, + _variables: unknown, + _context: unknown, + mutation: { options: { onError?: unknown } } +): void => { + if (mutation.options.onError === undefined) { + const message = + error instanceof ApiError ? error.getUserMessage() : 'Operation failed' + toast.error(message) + } +} + +export const QUERY_STRATEGIES = { + standard: { + staleTime: QUERY_CONFIG.STALE_TIME.USER, + gcTime: QUERY_CONFIG.GC_TIME.DEFAULT, + }, + frequent: { + staleTime: QUERY_CONFIG.STALE_TIME.FREQUENT, + gcTime: QUERY_CONFIG.GC_TIME.DEFAULT, + refetchInterval: QUERY_CONFIG.STALE_TIME.FREQUENT, + }, + static: { + staleTime: QUERY_CONFIG.STALE_TIME.STATIC, + gcTime: QUERY_CONFIG.GC_TIME.LONG, + refetchOnMount: false, + refetchOnWindowFocus: false, + }, + auth: { + staleTime: QUERY_CONFIG.STALE_TIME.USER, + gcTime: QUERY_CONFIG.GC_TIME.DEFAULT, + retry: QUERY_CONFIG.RETRY.NONE, + }, +} as const + +export type QueryStrategy = keyof typeof QUERY_STRATEGIES + +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: QUERY_CONFIG.STALE_TIME.USER, + gcTime: QUERY_CONFIG.GC_TIME.DEFAULT, + retry: shouldRetryQuery, + retryDelay: calculateRetryDelay, + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchOnReconnect: true, + }, + mutations: { + retry: QUERY_CONFIG.RETRY.NONE, + }, + }, + queryCache: new QueryCache({ + onError: handleQueryCacheError, + }), + mutationCache: new MutationCache({ + onError: handleMutationCacheError, + }), +}) diff --git a/frontends/open/react-scss/src/core/app/routers.tsx b/frontends/open/react-scss/src/core/app/routers.tsx new file mode 100644 index 0000000..d17138c --- /dev/null +++ b/frontends/open/react-scss/src/core/app/routers.tsx @@ -0,0 +1,34 @@ +// =================== +// © AngelaMos | 2026 +// routers.tsx +// =================== + +import { createBrowserRouter, type RouteObject } from 'react-router-dom' +import { ROUTES } from '@/config' +import { Shell } from './shell' + +const routes: RouteObject[] = [ + { + path: ROUTES.HOME, + lazy: () => import('@/pages/landing'), + }, + { + element: , + children: [ + { + path: ROUTES.DASHBOARD, + lazy: () => import('@/pages/dashboard'), + }, + { + path: ROUTES.SETTINGS, + lazy: () => import('@/pages/settings'), + }, + ], + }, + { + path: '*', + lazy: () => import('@/pages/landing'), + }, +] + +export const router = createBrowserRouter(routes) diff --git a/frontends/open/react-scss/src/core/app/shell.module.scss b/frontends/open/react-scss/src/core/app/shell.module.scss new file mode 100644 index 0000000..3466896 --- /dev/null +++ b/frontends/open/react-scss/src/core/app/shell.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// shell.module.scss +// =================== diff --git a/frontends/open/react-scss/src/core/app/shell.tsx b/frontends/open/react-scss/src/core/app/shell.tsx new file mode 100644 index 0000000..3296f38 --- /dev/null +++ b/frontends/open/react-scss/src/core/app/shell.tsx @@ -0,0 +1,116 @@ +/** + * ©AngelaMos | 2026 + * shell.tsx + */ + +import { Suspense } from 'react' +import { ErrorBoundary } from 'react-error-boundary' +import { GiCardAceClubs, GiCardJoker } from 'react-icons/gi' +import { LuChevronLeft, LuChevronRight, LuMenu } from 'react-icons/lu' +import { NavLink, Outlet, useLocation } from 'react-router-dom' +import { ROUTES } from '@/config' +import { useUIStore } from '@/core/lib' +import styles from './shell.module.scss' + +const NAV_ITEMS = [ + { path: ROUTES.DASHBOARD, label: 'Dashboard', icon: GiCardJoker }, + { path: ROUTES.SETTINGS, label: 'Settings', icon: GiCardAceClubs }, +] + +function ShellErrorFallback({ error }: { error: Error }): React.ReactElement { + return ( +
+

Something went wrong

+
{error.message}
+
+ ) +} + +function ShellLoading(): React.ReactElement { + return
Loading...
+} + +function getPageTitle(pathname: string): string { + const item = NAV_ITEMS.find((i) => i.path === pathname) + return item?.label ?? 'Dashboard' +} + +export function Shell(): React.ReactElement { + const location = useLocation() + const { sidebarOpen, sidebarCollapsed, toggleSidebar, toggleSidebarCollapsed } = + useUIStore() + + const pageTitle = getPageTitle(location.pathname) + + return ( +
+ + + {sidebarOpen && ( + +

{pageTitle}

+
+ + +
+ + }> + + + +
+ + + ) +} diff --git a/frontends/open/react-scss/src/core/app/toast.module.scss b/frontends/open/react-scss/src/core/app/toast.module.scss new file mode 100644 index 0000000..62a8ee3 --- /dev/null +++ b/frontends/open/react-scss/src/core/app/toast.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// toast.module.scss +// =================== diff --git a/frontends/open/react-scss/src/core/lib/index.ts b/frontends/open/react-scss/src/core/lib/index.ts new file mode 100644 index 0000000..cdee46e --- /dev/null +++ b/frontends/open/react-scss/src/core/lib/index.ts @@ -0,0 +1,6 @@ +// =================== +// © AngelaMos | 2026 +// index.ts +// =================== + +export * from './shell.ui.store' diff --git a/frontends/open/react-scss/src/core/lib/shell.ui.store.ts b/frontends/open/react-scss/src/core/lib/shell.ui.store.ts new file mode 100644 index 0000000..d601a53 --- /dev/null +++ b/frontends/open/react-scss/src/core/lib/shell.ui.store.ts @@ -0,0 +1,63 @@ +/** + * ©AngelaMos | 2025 + * ui.store.ts + */ + +import { create } from 'zustand' +import { devtools, persist } from 'zustand/middleware' + +type Theme = 'light' | 'dark' | 'system' + +interface UIState { + theme: Theme + sidebarOpen: boolean + sidebarCollapsed: boolean + setTheme: (theme: Theme) => void + toggleSidebar: () => void + setSidebarOpen: (open: boolean) => void + toggleSidebarCollapsed: () => void +} + +export const useUIStore = create()( + devtools( + persist( + (set) => ({ + theme: 'dark', + sidebarOpen: false, + sidebarCollapsed: false, + + setTheme: (theme) => set({ theme }, false, 'ui/setTheme'), + + toggleSidebar: () => + set( + (state) => ({ sidebarOpen: !state.sidebarOpen }), + false, + 'ui/toggleSidebar' + ), + + setSidebarOpen: (open) => + set({ sidebarOpen: open }, false, 'ui/setSidebarOpen'), + + toggleSidebarCollapsed: () => + set( + (state) => ({ sidebarCollapsed: !state.sidebarCollapsed }), + false, + 'ui/toggleSidebarCollapsed' + ), + }), + { + name: 'ui-storage', + partialize: (state) => ({ + theme: state.theme, + sidebarCollapsed: state.sidebarCollapsed, + }), + } + ), + { name: 'UIStore' } + ) +) + +export const useTheme = (): Theme => useUIStore((s) => s.theme) +export const useSidebarOpen = (): boolean => useUIStore((s) => s.sidebarOpen) +export const useSidebarCollapsed = (): boolean => + useUIStore((s) => s.sidebarCollapsed) diff --git a/frontends/open/react-scss/src/main.tsx b/frontends/open/react-scss/src/main.tsx new file mode 100644 index 0000000..1b1f2c8 --- /dev/null +++ b/frontends/open/react-scss/src/main.tsx @@ -0,0 +1,15 @@ +// =========================== +// ©AngelaMos | 2025 +// main.tsx +// =========================== + +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './App' +import './styles.scss' + +createRoot(document.getElementById('root')!).render( + + + +) diff --git a/frontends/open/react-scss/src/pages/dashboard/dashboard.module.scss b/frontends/open/react-scss/src/pages/dashboard/dashboard.module.scss new file mode 100644 index 0000000..cbf1f90 --- /dev/null +++ b/frontends/open/react-scss/src/pages/dashboard/dashboard.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// dashboard.module.scss +// =================== diff --git a/frontends/open/react-scss/src/pages/dashboard/index.tsx b/frontends/open/react-scss/src/pages/dashboard/index.tsx new file mode 100644 index 0000000..ca90889 --- /dev/null +++ b/frontends/open/react-scss/src/pages/dashboard/index.tsx @@ -0,0 +1,62 @@ +/** + * ©AngelaMos | 2026 + * index.tsx + */ + +import styles from './dashboard.module.scss' + +const AVAILABLE_STORES = [ + { + name: 'useUIStore()', + file: 'core/lib/shell.ui.store.ts', + description: 'Theme, sidebar open/collapsed state', + }, +] + +const SUGGESTED_FEATURES = [ + 'Stats and metrics', + 'Recent activity feed', + 'Quick actions', + 'Charts and analytics', + 'Notifications overview', + 'Task/project summary', +] + +export function Component(): React.ReactElement { + return ( +
+
+
+

Welcome

+

+ Template page — build your dashboard here +

+
+ +
+

Available Stores

+
+ {AVAILABLE_STORES.map((store) => ( +
+ {store.name} +

{store.description}

+ {store.file} +
+ ))} +
+
+ +
+

Suggested Features

+
    + {SUGGESTED_FEATURES.map((feature) => ( +
  • {feature}
  • + ))} +
+
+
+
+ ) +} + +Component.displayName = 'Dashboard' diff --git a/frontends/open/react-scss/src/pages/landing/index.tsx b/frontends/open/react-scss/src/pages/landing/index.tsx new file mode 100644 index 0000000..7ec81be --- /dev/null +++ b/frontends/open/react-scss/src/pages/landing/index.tsx @@ -0,0 +1,12 @@ +// =================== +// © AngelaMos | 2026 +// index.tsx +// =================== + +import styles from './landing.module.scss' + +export function Component(): React.ReactElement { + return
+} + +Component.displayName = 'Landing' diff --git a/frontends/open/react-scss/src/pages/landing/landing.module.scss b/frontends/open/react-scss/src/pages/landing/landing.module.scss new file mode 100644 index 0000000..164f39a --- /dev/null +++ b/frontends/open/react-scss/src/pages/landing/landing.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// landing.module.scss +// =================== diff --git a/frontends/open/react-scss/src/pages/settings/index.tsx b/frontends/open/react-scss/src/pages/settings/index.tsx new file mode 100644 index 0000000..9c8dc9a --- /dev/null +++ b/frontends/open/react-scss/src/pages/settings/index.tsx @@ -0,0 +1,56 @@ +/** + * ©AngelaMos | 2026 + * index.tsx + */ + +import styles from './settings.module.scss' + +const AVAILABLE_STORES = [ + { + name: 'useUIStore()', + file: 'core/lib/shell.ui.store.ts', + description: 'Theme, sidebar open/collapsed state', + }, +] + +export function Component(): React.ReactElement { + return ( +
+
+
+

Settings

+

+ Template page — available stores for building your settings UI +

+
+ +
+

Available Stores

+
+ {AVAILABLE_STORES.map((store) => ( +
+ {store.name} +

{store.description}

+
+ {store.file} +
+
+ ))} +
+
+ +
+

Suggested Features

+
    +
  • Profile form
  • +
  • Theme toggle (dark/light)
  • +
  • Notification settings
  • +
  • Application preferences
  • +
+
+
+
+ ) +} + +Component.displayName = 'Settings' diff --git a/frontends/open/react-scss/src/pages/settings/settings.module.scss b/frontends/open/react-scss/src/pages/settings/settings.module.scss new file mode 100644 index 0000000..e95e0f8 --- /dev/null +++ b/frontends/open/react-scss/src/pages/settings/settings.module.scss @@ -0,0 +1,4 @@ +// =================== +// © AngelaMos | 2026 +// settings.module.scss +// =================== diff --git a/frontends/open/react-scss/src/styles.scss b/frontends/open/react-scss/src/styles.scss new file mode 100644 index 0000000..240ec55 --- /dev/null +++ b/frontends/open/react-scss/src/styles.scss @@ -0,0 +1,23 @@ +// =================== +// © AngelaMos | 2026 +// styles.scss +// =================== + +@forward 'styles/tokens'; +@forward 'styles/fonts'; +@forward 'styles/mixins'; + +@use 'styles/reset'; + +#root { + min-height: 100vh; + min-height: 100dvh; + display: flex; + flex-direction: column; +} + +.app { + flex: 1; + display: flex; + flex-direction: column; +} diff --git a/frontends/open/react-scss/src/styles/_fonts.scss b/frontends/open/react-scss/src/styles/_fonts.scss new file mode 100644 index 0000000..4f51788 --- /dev/null +++ b/frontends/open/react-scss/src/styles/_fonts.scss @@ -0,0 +1,12 @@ +// =================== +// © AngelaMos | 2025 +// _fonts.scss +// =================== + +@use 'tokens' as *; + +$font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', Roboto, + 'Helvetica Neue', Arial, sans-serif; + +$font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, + 'Liberation Mono', monospace; diff --git a/frontends/open/react-scss/src/styles/_index.scss b/frontends/open/react-scss/src/styles/_index.scss new file mode 100644 index 0000000..ac5a826 --- /dev/null +++ b/frontends/open/react-scss/src/styles/_index.scss @@ -0,0 +1,8 @@ +// =================== +// © AngelaMos | 2025 +// _index.scss +// =================== + +@forward 'tokens'; +@forward 'fonts'; +@forward 'mixins'; diff --git a/frontends/open/react-scss/src/styles/_mixins.scss b/frontends/open/react-scss/src/styles/_mixins.scss new file mode 100644 index 0000000..6b5b1ba --- /dev/null +++ b/frontends/open/react-scss/src/styles/_mixins.scss @@ -0,0 +1,120 @@ +// =================== +// © AngelaMos | 2025 +// _mixins.scss +// =================== + +@use 'sass:map'; +@use 'sass:list'; +@use 'tokens' as *; + +$breakpoints: ( + 'xs': $breakpoint-xs, + 'sm': $breakpoint-sm, + 'md': $breakpoint-md, + 'lg': $breakpoint-lg, + 'xl': $breakpoint-xl, + '2xl': $breakpoint-2xl, +); + +@mixin breakpoint-up($size) { + @media (min-width: map.get($breakpoints, $size)) { + @content; + } +} + +@mixin breakpoint-down($size) { + @media (width < map.get($breakpoints, $size)) { + @content; + } +} + +@mixin flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +@mixin flex-between { + display: flex; + align-items: center; + justify-content: space-between; +} + +@mixin flex-column { + display: flex; + flex-direction: column; +} + +@mixin flex-column-center { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +@mixin sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0 0 0 0); + clip-path: inset(50%); + white-space: nowrap; + border: 0; +} + +@mixin truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +@mixin line-clamp($lines: 2) { + display: -webkit-box; + -webkit-line-clamp: #{$lines}; + -webkit-box-orient: vertical; + overflow: hidden; +} + +@mixin transition-fast { + transition-property: background-color, border-color, color, opacity; + transition-duration: $duration-fast; + transition-timing-function: $ease-out; +} + +@mixin transition-normal { + transition-property: background-color, border-color, color, opacity; + transition-duration: $duration-normal; + transition-timing-function: $ease-out; +} + +@mixin absolute-fill { + position: absolute; + inset: 0; +} + +@mixin absolute-center { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +@mixin hover { + @media (hover: hover) and (pointer: fine) { + &:hover { + @content; + } + } +} + +@mixin hide-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; + + &::-webkit-scrollbar { + display: none; + } +} diff --git a/frontends/open/react-scss/src/styles/_reset.scss b/frontends/open/react-scss/src/styles/_reset.scss new file mode 100644 index 0000000..a9cbdc1 --- /dev/null +++ b/frontends/open/react-scss/src/styles/_reset.scss @@ -0,0 +1,105 @@ +// =================== +// © AngelaMos | 2026 +// _reset.scss +// =================== + +*, +*::before, +*::after { + box-sizing: border-box; +} + +* { + margin: 0; + padding: 0; + -webkit-tap-highlight-color: transparent; +} + +html { + font-size: 16px; + -moz-text-size-adjust: none; + -webkit-text-size-adjust: none; + text-size-adjust: none; + overflow-x: hidden; +} + +@media (prefers-reduced-motion: no-preference) { + html { + interpolate-size: allow-keywords; + scroll-behavior: smooth; + } +} + +body { + min-height: 100vh; + min-height: 100dvh; + line-height: 1.5; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + overflow-x: hidden; + background-color: #fff; + color: #000; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + line-height: 1.2; + text-wrap: balance; + overflow-wrap: break-word; +} + +p { + text-wrap: pretty; + overflow-wrap: break-word; +} + +img, +picture, +video, +canvas, +svg { + display: block; + max-width: 100%; + height: auto; +} + +input, +button, +textarea, +select { + font: inherit; + color: inherit; +} + +button { + background: none; + border: none; + cursor: pointer; + text-align: inherit; + font-family: inherit; +} + +[hidden] { + display: none !important; +} + +[disabled] { + cursor: not-allowed; + opacity: 0.5; +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} diff --git a/frontends/open/react-scss/src/styles/_tokens.scss b/frontends/open/react-scss/src/styles/_tokens.scss new file mode 100644 index 0000000..be1f6dd --- /dev/null +++ b/frontends/open/react-scss/src/styles/_tokens.scss @@ -0,0 +1,168 @@ +// =================== +// © AngelaMos | 2025 +// _tokens.scss +// =================== + +// ============================================================================ +// SPACING (8px base system) +// ============================================================================ +$space-0: 0; +$space-px: 1px; +$space-0-5: 0.125rem; +$space-1: 0.25rem; +$space-1-5: 0.375rem; +$space-2: 0.5rem; +$space-2-5: 0.625rem; +$space-3: 0.75rem; +$space-3-5: 0.875rem; +$space-4: 1rem; +$space-5: 1.25rem; +$space-6: 1.5rem; +$space-7: 1.75rem; +$space-8: 2rem; +$space-9: 2.25rem; +$space-10: 2.5rem; +$space-11: 2.75rem; +$space-12: 3rem; +$space-14: 3.5rem; +$space-16: 4rem; +$space-20: 5rem; +$space-24: 6rem; +$space-28: 7rem; +$space-32: 8rem; + +// ============================================================================ +// TYPOGRAPHY SCALE +// ============================================================================ +$font-size-3xs: 0.625rem; +$font-size-2xs: 0.6875rem; +$font-size-xs: 0.75rem; +$font-size-sm: 0.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-size-5xl: 3rem; + +// ============================================================================ +// FONT WEIGHTS +// ============================================================================ +$font-weight-regular: 400; +$font-weight-medium: 500; +$font-weight-semibold: 600; + +// ============================================================================ +// LINE HEIGHTS +// ============================================================================ +$line-height-none: 1; +$line-height-tight: 1.2; +$line-height-snug: 1.375; +$line-height-normal: 1.5; +$line-height-relaxed: 1.625; + +// ============================================================================ +// LETTER SPACING +// ============================================================================ +$tracking-tighter: -0.05em; +$tracking-tight: -0.025em; +$tracking-normal: 0; +$tracking-wide: 0.025em; +$tracking-wider: 0.05em; + +// ============================================================================ +// COLORS +// ============================================================================ +$white: hsl(0, 0%, 100%); +$black: hsl(0, 0%, 0%); + +// Auth +$bg-page: hsl(0, 0%, 10.5%); +$bg-card: hsl(0, 0%, 6.2%); + +// Home/landing +$bg-landing: hsl(0, 0%, 10.8%); + +$bg-default: hsl(0, 0%, 7.1%); +$bg-alternative: hsl(0, 0%, 5.9%); +$bg-surface-75: hsl(0, 0%, 9%); +$bg-surface-100: hsl(0, 0%, 12.2%); +$bg-surface-200: hsl(0, 0%, 14.1%); +$bg-surface-300: hsl(0, 0%, 16.1%); +$bg-control: hsl(0, 0%, 10%); +$bg-selection: hsl(0, 0%, 19.2%); +$bg-overlay: hsl(0, 0%, 14.1%); +$bg-overlay-hover: hsl(0, 0%, 18%); + +$border-muted: hsl(0, 0%, 11.1%); +$border-default: hsl(0, 0%, 18%); +$border-strong: hsl(0, 0%, 22.4%); +$border-stronger: hsl(0, 0%, 27.1%); +$border-control: hsl(0, 0%, 22.4%); + +$text-default: hsl(0, 0%, 98%); +$text-light: hsl(0, 0%, 70.6%); +$text-lighter: hsl(0, 0%, 53.7%); +$text-muted: hsl(0, 0%, 30.2%); + +$error-default: hsl(0, 72%, 51%); +$error-light: hsl(0, 72%, 65%); + +// ============================================================================ +// BORDER RADIUS +// ============================================================================ +$radius-none: 0; +$radius-xs: 2px; +$radius-sm: 4px; +$radius-md: 6px; +$radius-lg: 8px; +$radius-xl: 12px; +$radius-full: 9999px; + +// ============================================================================ +// Z-INDEX SCALE +// ============================================================================ +$z-hide: -1; +$z-base: 0; +$z-dropdown: 100; +$z-sticky: 200; +$z-fixed: 300; +$z-overlay: 400; +$z-modal: 500; +$z-popover: 600; +$z-tooltip: 700; +$z-toast: 800; +$z-max: 9999; + +// ============================================================================ +// TRANSITIONS +// ============================================================================ +$duration-instant: 0ms; +$duration-fast: 100ms; +$duration-normal: 150ms; +$duration-slow: 200ms; + +$ease-out: cubic-bezier(0, 0, 0.2, 1); +$ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + +// ============================================================================ +// BREAKPOINTS +// ============================================================================ +$breakpoint-xs: 360px; +$breakpoint-sm: 480px; +$breakpoint-md: 768px; +$breakpoint-lg: 1024px; +$breakpoint-xl: 1280px; +$breakpoint-2xl: 1536px; + +// ============================================================================ +// CONTAINER WIDTHS +// ============================================================================ +$container-xs: 20rem; +$container-sm: 24rem; +$container-md: 28rem; +$container-lg: 32rem; +$container-xl: 36rem; +$container-2xl: 42rem; +$container-full: 100%; diff --git a/frontends/open/react-scss/stylelint.config.js b/frontends/open/react-scss/stylelint.config.js new file mode 100755 index 0000000..56ebd7b --- /dev/null +++ b/frontends/open/react-scss/stylelint.config.js @@ -0,0 +1,107 @@ +// ©AngelaMos | 2025 +// stylelint.config.js + +/** @type {import('stylelint').Config} */ +export default { + extends: ['stylelint-config-standard-scss', 'stylelint-config-prettier-scss'], + rules: { + 'block-no-empty': true, + 'declaration-no-important': true, + 'color-no-invalid-hex': true, + 'property-no-unknown': true, + 'selector-pseudo-class-no-unknown': [ + true, + { + ignorePseudoClasses: ['global'], + }, + ], + + 'selector-class-pattern': [ + '^[a-z]([a-z0-9-]+)?(__[a-z0-9]([a-z0-9-]+)?)?(--[a-z0-9]([a-z0-9-]+)?)?$|^[a-z][a-zA-Z0-9]*$', + { + message: + 'Selector should be in BEM format (e.g., .block__element--modifier) or CSS Modules camelCase (e.g., .testButton)', + }, + ], + + 'value-keyword-case': [ + 'lower', + { + camelCaseSvgKeywords: true, + ignoreKeywords: [ + 'BlinkMacSystemFont', + 'SFMono-Regular', + 'Menlo', + 'Monaco', + 'Consolas', + 'Roboto', + 'Arial', + 'Helvetica', + 'Times', + 'Georgia', + 'Verdana', + 'Tahoma', + 'Trebuchet', + 'Impact', + 'Comic', + ], + }, + ], + + 'property-no-vendor-prefix': [ + true, + { + ignoreProperties: ['text-size-adjust', 'appearance', 'backdrop-filter'], + }, + ], + 'value-no-vendor-prefix': true, + 'selector-no-vendor-prefix': true, + + 'property-no-deprecated': [ + true, + { + ignoreProperties: ['clip'], + }, + ], + + 'container-name-pattern': null, + 'layer-name-pattern': null, + + 'scss/at-rule-no-unknown': true, + 'scss/declaration-nested-properties-no-divided-groups': true, + 'scss/dollar-variable-no-missing-interpolation': true, + 'scss/dollar-variable-empty-line-before': null, + + 'declaration-empty-line-before': null, + 'custom-property-empty-line-before': null, + + 'no-descending-specificity': null, + + 'media-feature-name-no-unknown': [ + true, + { + ignoreMediaFeatureNames: ['map'], + }, + ], + + 'color-function-notation': null, + 'hue-degree-notation': null, + }, + ignoreFiles: [ + 'node_modules/**', + 'dist/**', + 'build/**', + '**/*.js', + '**/*.ts', + '**/*.tsx', + ], + overrides: [ + { + files: ['**/styles/_reset.scss', '**/styles/_fonts.scss'], + rules: { + 'declaration-no-important': null, + 'scss/comment-no-empty': null, + }, + }, + ], +} diff --git a/frontends/open/react-scss/tsconfig.app.json b/frontends/open/react-scss/tsconfig.app.json new file mode 100644 index 0000000..afe17f6 --- /dev/null +++ b/frontends/open/react-scss/tsconfig.app.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src"] +} diff --git a/frontends/open/react-scss/tsconfig.json b/frontends/open/react-scss/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/frontends/open/react-scss/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/frontends/open/react-scss/tsconfig.node.json b/frontends/open/react-scss/tsconfig.node.json new file mode 100644 index 0000000..a96b3e5 --- /dev/null +++ b/frontends/open/react-scss/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/frontends/open/react-scss/vite.config.ts b/frontends/open/react-scss/vite.config.ts new file mode 100644 index 0000000..a8d5d47 --- /dev/null +++ b/frontends/open/react-scss/vite.config.ts @@ -0,0 +1,71 @@ +/** + * ©AngelaMos | 2025 + * vite.config.ts + */ + +import path from 'node:path' +import react from '@vitejs/plugin-react' +import { defineConfig, loadEnv } from 'vite' +import tsconfigPaths from 'vite-tsconfig-paths' + +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, path.resolve(__dirname, '..'), '') + const isDev = mode === 'development' + + return { + plugins: [react(), tsconfigPaths()], + + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, + + css: { + preprocessorOptions: { + scss: {}, + }, + }, + + server: { + port: 5173, + host: '0.0.0.0', + proxy: { + '/api': { + target: env.VITE_API_TARGET || 'http://localhost:8000', + changeOrigin: true, + rewrite: (p) => p.replace(/^\/api/, ''), + }, + }, + }, + + build: { + target: 'esnext', + cssTarget: 'chrome100', + sourcemap: isDev ? true : 'hidden', + minify: 'oxc', + rollupOptions: { + output: { + manualChunks(id: string): string | undefined { + if (id.includes('node_modules')) { + if (id.includes('react-dom') || id.includes('react-router')) { + return 'vendor-react' + } + if (id.includes('@tanstack/react-query')) { + return 'vendor-query' + } + if (id.includes('zustand')) { + return 'vendor-state' + } + } + return undefined + }, + }, + }, + }, + + preview: { + port: 4173, + }, + } +}) diff --git a/frontends/open/scripts/init.sh b/frontends/open/scripts/init.sh new file mode 100755 index 0000000..696e630 --- /dev/null +++ b/frontends/open/scripts/init.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# ============================================================================= +# AngelaMos | 2026 +# init.sh +# ============================================================================= +# Run once after copying the template. +# Prompts for a project name, randomizes ports, updates all the right files. + +set -euo pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +# ── prompt ──────────────────────────────────────────────────────────────────── +echo "" +read -rp "Project name (kebab-case): " RAW + +SLUG="$(echo "$RAW" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd '[:alnum:]-')" +[[ -z "$SLUG" ]] && { echo "Error: empty name" >&2; exit 1; } + +TITLE="$(echo "$SLUG" | tr '-' ' ' | python3 -c "import sys; print(sys.stdin.read().strip().title())")" + +# ── ports ───────────────────────────────────────────────────────────────────── +mapfile -t ports < <(python3 -c " +import random +for p in random.sample(range(10000, 65001), 4): print(p) +") + +PROD_NGINX=${ports[0]} +PROD_FRONTEND=${ports[1]} +DEV_NGINX=${ports[2]} +DEV_FRONTEND=${ports[3]} + +echo "" +echo " slug: $SLUG" +echo " title: $TITLE" +echo " prod nginx: $PROD_NGINX" +echo " prod frontend: $PROD_FRONTEND" +echo " dev nginx: $DEV_NGINX" +echo " dev frontend: $DEV_FRONTEND" +echo "" + +# ── .env (prod) ─────────────────────────────────────────────────────────────── +sed -i "s|^APP_NAME=.*|APP_NAME=$SLUG|" "$DIR/.env" +sed -i "s|^VITE_APP_TITLE=.*|VITE_APP_TITLE=$TITLE|" "$DIR/.env" +sed -i "s|^NGINX_HOST_PORT=.*|NGINX_HOST_PORT=$PROD_NGINX|" "$DIR/.env" +sed -i "s|^FRONTEND_HOST_PORT=.*|FRONTEND_HOST_PORT=$PROD_FRONTEND|" "$DIR/.env" +echo " updated .env" + +# ── .env.development ────────────────────────────────────────────────────────── +sed -i "s|^APP_NAME=.*|APP_NAME=$SLUG-dev|" "$DIR/.env.development" +sed -i "s|^VITE_APP_TITLE=.*|VITE_APP_TITLE=\"$TITLE (Dev)\"|" "$DIR/.env.development" +sed -i "s|^NGINX_HOST_PORT=.*|NGINX_HOST_PORT=$DEV_NGINX|" "$DIR/.env.development" +sed -i "s|^FRONTEND_HOST_PORT=.*|FRONTEND_HOST_PORT=$DEV_FRONTEND|" "$DIR/.env.development" +echo " updated .env.development" + +# ── .env.example ────────────────────────────────────────────────────────────── +sed -i "s|^APP_NAME=.*|APP_NAME=$SLUG|" "$DIR/.env.example" +sed -i "s|^VITE_APP_TITLE=.*|VITE_APP_TITLE=$TITLE|" "$DIR/.env.example" +sed -i "s|^NGINX_HOST_PORT=.*|NGINX_HOST_PORT=$PROD_NGINX|" "$DIR/.env.example" +sed -i "s|^FRONTEND_HOST_PORT=.*|FRONTEND_HOST_PORT=$PROD_FRONTEND|" "$DIR/.env.example" +echo " updated .env.example" + +# ── compose.yml ─────────────────────────────────────────────────────────────── +sed -i "s|\${APP_NAME:-[a-z0-9-]*}|\${APP_NAME:-$SLUG}|g" "$DIR/compose.yml" +sed -i "s|\${NGINX_HOST_PORT:-[0-9]\+}|\${NGINX_HOST_PORT:-$PROD_NGINX}|g" "$DIR/compose.yml" +echo " updated compose.yml" + +# ── dev.compose.yml ─────────────────────────────────────────────────────────── +sed -i "s|\${APP_NAME:-[a-z0-9-]*}|\${APP_NAME:-$SLUG}|g" "$DIR/dev.compose.yml" +sed -i "s|\${NGINX_HOST_PORT:-[0-9]\+}|\${NGINX_HOST_PORT:-$DEV_NGINX}|g" "$DIR/dev.compose.yml" +sed -i "s|\${FRONTEND_HOST_PORT:-[0-9]\+}|\${FRONTEND_HOST_PORT:-$DEV_FRONTEND}|g" "$DIR/dev.compose.yml" +echo " updated dev.compose.yml" + +# ── cloudflared.compose.yml ─────────────────────────────────────────────────── +sed -i "s|\${APP_NAME:-[a-z0-9-]*}|\${APP_NAME:-$SLUG}|g" "$DIR/cloudflared.compose.yml" +echo " updated cloudflared.compose.yml" + +# ── react-scss/package.json ─────────────────────────────────────────────────── +sed -i "s|\"name\": \".*\"|\"name\": \"$SLUG\"|" "$DIR/react-scss/package.json" +echo " updated react-scss/package.json" + +# ── react-scss/index.html ───────────────────────────────────────────────────── +sed -i "s|Full Stack Template No Auth|$TITLE|g" "$DIR/react-scss/index.html" +echo " updated react-scss/index.html" + +echo "" +echo "Done. Go build something." diff --git a/frontends/open/scripts/randomize-ports.sh b/frontends/open/scripts/randomize-ports.sh new file mode 100755 index 0000000..95348d0 --- /dev/null +++ b/frontends/open/scripts/randomize-ports.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# ============================================================================= +# AngelaMos | 2026 +# randomize-ports.sh +# ============================================================================= +# Picks 4 unique random ports (10000-65000) and updates: +# .env -> prod nginx + frontend ports +# .env.development -> dev nginx + frontend ports +# compose.yml -> prod default fallbacks +# dev.compose.yml -> dev default fallbacks + +set -euo pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +mapfile -t ports < <(python3 -c " +import random +sample = random.sample(range(10000, 65001), 4) +for p in sample: print(p) +") + +PROD_NGINX=${ports[0]} +PROD_FRONTEND=${ports[1]} +DEV_NGINX=${ports[2]} +DEV_FRONTEND=${ports[3]} + +echo "New ports:" +echo " prod nginx: $PROD_NGINX" +echo " prod frontend: $PROD_FRONTEND" +echo " dev nginx: $DEV_NGINX" +echo " dev frontend: $DEV_FRONTEND" +echo "" + +update_env() { + local file="$1" nginx_port="$2" frontend_port="$3" + sed -i "s/^NGINX_HOST_PORT=.*/NGINX_HOST_PORT=$nginx_port/" "$file" + sed -i "s/^FRONTEND_HOST_PORT=.*/FRONTEND_HOST_PORT=$frontend_port/" "$file" + echo " updated $file" +} + +update_compose_defaults() { + local file="$1" nginx_port="$2" frontend_port="${3:-}" + sed -i "s/\${NGINX_HOST_PORT:-[0-9]\+}/\${NGINX_HOST_PORT:-$nginx_port}/g" "$file" + if [[ -n "$frontend_port" ]]; then + sed -i "s/\${FRONTEND_HOST_PORT:-[0-9]\+}/\${FRONTEND_HOST_PORT:-$frontend_port}/g" "$file" + fi + echo " updated $file" +} + +update_env "$DIR/.env" "$PROD_NGINX" "$PROD_FRONTEND" +update_env "$DIR/.env.development" "$DEV_NGINX" "$DEV_FRONTEND" +update_compose_defaults "$DIR/compose.yml" "$PROD_NGINX" +update_compose_defaults "$DIR/dev.compose.yml" "$DEV_NGINX" "$DEV_FRONTEND" + +echo "" +echo "Done." diff --git a/frontends/react-scss/src/core/app/shell.module.scss b/frontends/react-scss/src/core/app/shell.module.scss deleted file mode 100644 index 645ca25..0000000 --- a/frontends/react-scss/src/core/app/shell.module.scss +++ /dev/null @@ -1,331 +0,0 @@ -// =================== -// © AngelaMos | 2025 -// shell.module.scss -// =================== - -@use '@/styles' as *; - -$sidebar-width: 240px; -$sidebar-collapsed-width: 64px; -$header-height: 56px; - -.shell { - display: flex; - min-height: 100vh; - min-height: 100dvh; -} - -.sidebar { - position: fixed; - top: 0; - left: 0; - bottom: 0; - width: $sidebar-width; - background: $bg-surface-100; - border-right: 1px solid $border-default; - display: flex; - flex-direction: column; - z-index: $z-fixed; - @include transition-fast; - - &.collapsed { - width: $sidebar-collapsed-width; - } - - @include breakpoint-down('sm') { - transform: translateX(-100%); - - &.open { - transform: translateX(0); - } - - &.collapsed { - width: $sidebar-width; - } - } -} - -.sidebarHeader { - height: $header-height; - padding: 0 $space-3; - display: flex; - align-items: center; - justify-content: space-between; - border-bottom: 1px solid $border-default; - - .sidebar.collapsed & { - justify-content: center; - padding: 0; - } -} - -.logo { - font-size: $font-size-base; - font-weight: $font-weight-semibold; - color: $text-default; - @include transition-fast; - - .sidebar.collapsed & { - display: none; - } -} - -.nav { - flex: 1; - padding: $space-3; - display: flex; - flex-direction: column; - gap: $space-1; -} - -.navItem { - display: flex; - align-items: center; - gap: $space-3; - padding: $space-2 $space-3; - border-radius: $radius-md; - font-size: $font-size-sm; - color: $text-light; - @include transition-fast; - - @include hover { - background: $bg-surface-200; - color: $text-default; - } - - &.active { - background: $bg-selection; - color: $text-default; - } - - .sidebar.collapsed & { - justify-content: center; - } -} - -.navIcon { - width: 17px; - height: 17px; - flex-shrink: 0; -} - -.navLabel { - @include transition-fast; - - .sidebar.collapsed & { - display: none; - } -} - -.adminItem { - margin-top: auto; - border-top: 1px solid $border-default; - padding-top: $space-3; -} - -.collapseBtn { - width: 45px; - height: 45px; - border-radius: $radius-md; - color: $text-light; - @include flex-center; - @include transition-fast; - - svg { - width: 23.5px; - height: 23.5px; - } - - @include hover { - background: $bg-surface-200; - color: $text-default; - } - - @include breakpoint-down('sm') { - display: none; - } -} - -.sidebarFooter { - padding: $space-3; - border-top: 1px solid $border-default; -} - -.logoutBtn { - width: 100%; - display: flex; - align-items: center; - gap: $space-3; - padding: $space-3; - border-radius: $radius-md; - font-size: $font-size-sm; - color: $text-default; - @include transition-fast; - - @include hover { - background: $bg-surface-200; - } - - .sidebar.collapsed & { - justify-content: center; - - .logoutText { - display: none; - } - } -} - -.logoutIcon { - width: 18px; - height: 18px; - flex-shrink: 0; -} - -.logoutText { - font-weight: $font-weight-medium; - @include transition-fast; -} - -.overlay { - position: fixed; - inset: 0; - background: rgb(0, 0, 0, 50%); - z-index: calc($z-fixed - 1); - display: none; - border: none; - padding: 0; - cursor: pointer; - - @include breakpoint-down('sm') { - display: block; - } -} - -.main { - flex: 1; - display: flex; - flex-direction: column; - margin-left: $sidebar-width; - min-width: 0; - @include transition-fast; - - &.collapsed { - margin-left: $sidebar-collapsed-width; - } - - @include breakpoint-down('sm') { - margin-left: 0; - - &.collapsed { - margin-left: 0; - } - } -} - -.header { - position: sticky; - top: 0; - height: $header-height; - background: $bg-surface-100; - border-bottom: 1px solid $border-default; - z-index: $z-sticky; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 $space-4; -} - -.headerLeft { - display: flex; - align-items: center; - gap: $space-3; -} - -.menuBtn { - display: none; - width: 36px; - height: 36px; - border-radius: $radius-md; - color: $text-light; - align-items: center; - justify-content: center; - @include transition-fast; - - svg { - width: 20px; - height: 20px; - } - - @include hover { - background: $bg-surface-200; - color: $text-default; - } - - @media (width <= 479px) { - display: flex; - } -} - -.pageTitle { - font-size: $font-size-base; - font-weight: $font-weight-medium; - color: $text-default; - margin-left: 7px; -} - -.headerRight { - display: flex; - align-items: center; - gap: $space-3; -} - -.avatar { - width: 32px; - height: 32px; - border-radius: $radius-full; - background: $bg-surface-300; - color: $text-light; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - @include flex-center; - cursor: pointer; - @include transition-fast; - - @include hover { - filter: brightness(1.2); - } -} - -.content { - flex: 1; - overflow-y: auto; -} - -.loading { - @include flex-center; - height: 100%; - color: $text-muted; -} - -.error { - @include flex-column-center; - height: 100%; - gap: $space-4; - padding: $space-6; - color: $error-default; - - h2 { - font-size: $font-size-xl; - font-weight: $font-weight-semibold; - } - - pre { - font-family: $font-mono; - font-size: $font-size-sm; - padding: $space-4; - background: $bg-surface-200; - border-radius: $radius-lg; - overflow-x: auto; - max-width: 100%; - } -} diff --git a/frontends/react-scss/src/core/app/toast.module.scss b/frontends/react-scss/src/core/app/toast.module.scss deleted file mode 100644 index d50ab7f..0000000 --- a/frontends/react-scss/src/core/app/toast.module.scss +++ /dev/null @@ -1,67 +0,0 @@ -// =================== -// © AngelaMos | 2025 -// toast.module.scss -// =================== - -@use '@/styles' as *; - -:global { - [data-sonner-toaster] { - --normal-bg: #{$bg-surface-100}; - --normal-border: #{$border-default}; - --normal-text: #{$text-default}; - - --success-bg: #{$bg-surface-100}; - --success-border: #{$border-default}; - --success-text: #{$text-default}; - - --error-bg: #{$bg-surface-100}; - --error-border: #{$error-default}; - --error-text: #{$text-default}; - - --warning-bg: #{$bg-surface-100}; - --warning-border: #{$border-default}; - --warning-text: #{$text-default}; - - --info-bg: #{$bg-surface-100}; - --info-border: #{$border-default}; - --info-text: #{$text-default}; - - font-family: $font-sans; - } - - [data-sonner-toast] { - border-radius: $radius-md; - padding: $space-3 $space-4; - font-size: $font-size-sm; - border: 1px solid $border-default; - background: $bg-surface-100; - color: $text-default; - - [data-title] { - font-weight: $font-weight-medium; - } - - [data-description] { - color: $text-light; - font-size: $font-size-xs; - } - - [data-close-button] { - background: none; - border: none; - padding: 0; - cursor: pointer; - color: $text-muted; - @include transition-fast; - - @include hover { - color: $text-default; - } - } - } - - [data-sonner-toast][data-type='error'] { - border-color: $error-default; - } -} diff --git a/frontends/react-scss/src/pages/admin/admin.module.scss b/frontends/react-scss/src/pages/admin/admin.module.scss deleted file mode 100644 index ef51500..0000000 --- a/frontends/react-scss/src/pages/admin/admin.module.scss +++ /dev/null @@ -1,468 +0,0 @@ -// =================== -// © AngelaMos | 2025 -// admin.module.scss -// =================== - -@use '@/styles' as *; - -.page { - padding: $space-6; - min-height: calc(100vh - 56px); - background-color: $bg-default; -} - -.header { - @include flex-between; - margin-bottom: $space-6; -} - -.title { - font-size: $font-size-2xl; - font-weight: $font-weight-semibold; - color: $text-default; -} - -.createBtn { - display: flex; - align-items: center; - gap: $space-2; - padding: $space-2 $space-4; - background-color: $white; - border: none; - border-radius: $radius-md; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $black; - cursor: pointer; - @include transition-fast; - - @include hover { - filter: brightness(0.9); - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } - - svg { - width: 16px; - height: 16px; - } -} - -.table { - width: 100%; - background: $bg-surface-100; - border: 1px solid $border-default; - border-radius: $radius-lg; - overflow: hidden; -} - -.tableHeader { - display: grid; - grid-template-columns: 2fr 1fr 1fr 1fr 100px; - gap: $space-4; - padding: $space-3 $space-4; - background: $bg-surface-200; - border-bottom: 1px solid $border-default; - - @include breakpoint-down('md') { - display: none; - } -} - -.tableHeaderCell { - font-size: $font-size-xs; - font-weight: $font-weight-medium; - color: $text-lighter; - text-transform: uppercase; - letter-spacing: $tracking-wide; -} - -.tableBody { - @include flex-column; -} - -.tableRow { - display: grid; - grid-template-columns: 2fr 1fr 1fr 1fr 100px; - gap: $space-4; - padding: $space-3 $space-4; - border-bottom: 1px solid $border-default; - @include transition-fast; - - &:last-child { - border-bottom: none; - } - - @include hover { - background: $bg-surface-75; - } - - @include breakpoint-down('md') { - grid-template-columns: 1fr; - gap: $space-2; - } -} - -.tableCell { - display: flex; - align-items: center; - font-size: $font-size-sm; - color: $text-default; - min-width: 0; - - @include breakpoint-down('md') { - &::before { - content: attr(data-label); - font-size: $font-size-xs; - color: $text-lighter; - margin-right: $space-2; - min-width: 80px; - } - } -} - -.email { - @include truncate; -} - -.badge { - display: inline-flex; - align-items: center; - padding: $space-1 $space-2; - border-radius: $radius-full; - font-size: $font-size-xs; - font-weight: $font-weight-medium; - - &.admin { - background: $bg-selection; - color: $text-default; - } - - &.user { - background: $bg-surface-200; - color: $text-light; - } - - &.active { - background: hsl(142 76% 36% / 20%); - color: hsl(142, 76%, 46%); - } - - &.inactive { - background: hsl(0 72% 51% / 20%); - color: $error-light; - } -} - -.actions { - display: flex; - gap: $space-2; - justify-content: flex-end; - - @include breakpoint-down('md') { - justify-content: flex-start; - } -} - -.actionBtn { - width: 32px; - height: 32px; - @include flex-center; - border: 1px solid $border-default; - border-radius: $radius-md; - background: transparent; - color: $text-light; - cursor: pointer; - @include transition-fast; - - @include hover { - background: $bg-surface-200; - color: $text-default; - } - - &.delete { - @include hover { - border-color: $error-default; - color: $error-default; - } - } - - svg { - width: 16px; - height: 16px; - } -} - -.pagination { - @include flex-between; - padding: $space-4; - border-top: 1px solid $border-default; -} - -.paginationInfo { - font-size: $font-size-sm; - color: $text-lighter; -} - -.paginationBtns { - display: flex; - gap: $space-2; -} - -.paginationBtn { - padding: $space-2 $space-3; - border: 1px solid $border-default; - border-radius: $radius-md; - background: transparent; - font-size: $font-size-sm; - color: $text-light; - cursor: pointer; - @include transition-fast; - - @include hover { - background: $bg-surface-200; - color: $text-default; - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } -} - -.empty { - @include flex-column-center; - padding: $space-12; - color: $text-muted; - font-size: $font-size-sm; -} - -.loading { - @include flex-center; - padding: $space-12; - color: $text-muted; -} - -.modal { - position: fixed; - inset: 0; - z-index: $z-modal; - @include flex-center; -} - -.modalOverlay { - @include absolute-fill; - background: rgb(0, 0, 0, 70%); -} - -.modalContent { - position: relative; - width: 100%; - max-width: 400px; - margin: $space-4; - background: $bg-surface-100; - border: 1px solid $border-default; - border-radius: $radius-lg; - padding: $space-6; -} - -.modalHeader { - @include flex-between; - margin-bottom: $space-5; -} - -.modalTitle { - font-size: $font-size-lg; - font-weight: $font-weight-semibold; - color: $text-default; -} - -.modalClose { - width: 32px; - height: 32px; - @include flex-center; - border: none; - border-radius: $radius-md; - background: transparent; - color: $text-light; - cursor: pointer; - @include transition-fast; - - @include hover { - background: $bg-surface-200; - color: $text-default; - } - - svg { - width: 20px; - height: 20px; - } -} - -.form { - @include flex-column; - gap: $space-4; -} - -.field { - @include flex-column; - gap: $space-2; -} - -.label { - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $text-default; -} - -.input { - width: 100%; - height: 44px; - padding: 0 $space-3; - background: transparent; - border: 1px solid $border-default; - border-radius: $radius-md; - font-size: $font-size-sm; - color: $text-default; - @include transition-fast; - - &::placeholder { - color: $text-muted; - } - - &:focus { - outline: none; - border-color: $border-strong; - } -} - -.select { - width: 100%; - height: 44px; - padding: 0 $space-3; - background: transparent; - border: 1px solid $border-default; - border-radius: $radius-md; - font-size: $font-size-sm; - color: $text-default; - cursor: pointer; - @include transition-fast; - - &:focus { - outline: none; - border-color: $border-strong; - } - - option { - background: $bg-surface-100; - color: $text-default; - } -} - -.checkbox { - display: flex; - align-items: center; - gap: $space-2; - cursor: pointer; - - input { - width: 18px; - height: 18px; - accent-color: $white; - } - - span { - font-size: $font-size-sm; - color: $text-light; - } -} - -.formActions { - display: flex; - gap: $space-3; - margin-top: $space-2; -} - -.submitBtn { - flex: 1; - height: 44px; - @include flex-center; - background: $white; - border: none; - border-radius: $radius-md; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $black; - cursor: pointer; - @include transition-fast; - - @include hover { - filter: brightness(0.9); - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } -} - -.cancelBtn { - flex: 1; - height: 44px; - @include flex-center; - background: transparent; - border: 1px solid $border-default; - border-radius: $radius-md; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $text-light; - cursor: pointer; - @include transition-fast; - - @include hover { - background: $bg-surface-200; - color: $text-default; - } -} - -.deleteConfirm { - @include flex-column; - gap: $space-4; -} - -.deleteText { - font-size: $font-size-sm; - color: $text-light; - line-height: $line-height-relaxed; -} - -.deleteEmail { - font-weight: $font-weight-medium; - color: $text-default; -} - -.deleteBtn { - flex: 1; - height: 44px; - @include flex-center; - background: $error-default; - border: none; - border-radius: $radius-md; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $white; - cursor: pointer; - @include transition-fast; - - @include hover { - filter: brightness(0.9); - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } -} diff --git a/frontends/react-scss/src/pages/dashboard/dashboard.module.scss b/frontends/react-scss/src/pages/dashboard/dashboard.module.scss deleted file mode 100644 index ecac295..0000000 --- a/frontends/react-scss/src/pages/dashboard/dashboard.module.scss +++ /dev/null @@ -1,152 +0,0 @@ -// =================== -// © AngelaMos | 2025 -// dashboard.module.scss -// =================== - -@use '@/styles' as *; - -.page { - min-height: calc(100vh - 56px); - padding: $space-6; - background-color: $bg-default; -} - -.container { - max-width: 800px; - margin: 0 auto; -} - -.header { - margin-bottom: $space-6; -} - -.title { - font-size: $font-size-2xl; - font-weight: $font-weight-semibold; - color: $text-default; - margin-bottom: $space-2; -} - -.subtitle { - font-size: $font-size-sm; - color: $text-lighter; -} - -.userCard { - display: flex; - align-items: center; - gap: $space-4; - padding: $space-5; - background: $bg-surface-100; - border: 1px solid $border-default; - border-radius: $radius-lg; - margin-bottom: $space-8; -} - -.avatar { - width: 56px; - height: 56px; - border-radius: $radius-full; - background: $bg-surface-300; - color: $text-default; - font-size: $font-size-xl; - font-weight: $font-weight-semibold; - @include flex-center; - flex-shrink: 0; -} - -.userInfo { - @include flex-column; - gap: $space-1; - min-width: 0; -} - -.userName { - font-size: $font-size-base; - font-weight: $font-weight-medium; - color: $text-default; - @include truncate; -} - -.userEmail { - font-size: $font-size-sm; - color: $text-light; - @include truncate; -} - -.userRole { - display: inline-flex; - align-self: flex-start; - padding: $space-0-5 $space-2; - background: $bg-surface-200; - border-radius: $radius-full; - font-size: $font-size-xs; - color: $text-lighter; - text-transform: uppercase; - letter-spacing: $tracking-wide; -} - -.section { - margin-bottom: $space-8; -} - -.sectionTitle { - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $text-light; - text-transform: uppercase; - letter-spacing: $tracking-wide; - margin-bottom: $space-4; -} - -.grid { - display: grid; - gap: $space-4; - - @include breakpoint-up('md') { - grid-template-columns: repeat(3, 1fr); - } -} - -.card { - padding: $space-4; - background: $bg-surface-100; - border: 1px solid $border-default; - border-radius: $radius-lg; -} - -.hookName { - display: inline-block; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $text-default; - background: $bg-surface-200; - padding: $space-1 $space-2; - border-radius: $radius-sm; - margin-bottom: $space-2; -} - -.description { - font-size: $font-size-sm; - color: $text-light; - margin-bottom: $space-2; - line-height: $line-height-relaxed; -} - -.file { - font-size: $font-size-xs; - color: $text-muted; - font-family: $font-mono; -} - -.list { - @include flex-column; - gap: $space-2; - padding-left: $space-5; - list-style: disc; - - li { - font-size: $font-size-sm; - color: $text-light; - } -} diff --git a/frontends/react-scss/src/pages/landing/landing.module.scss b/frontends/react-scss/src/pages/landing/landing.module.scss deleted file mode 100644 index 3013e87..0000000 --- a/frontends/react-scss/src/pages/landing/landing.module.scss +++ /dev/null @@ -1,161 +0,0 @@ -// =================== -// © AngelaMos | 2025 -// landing.module.scss -// =================== - -@use '@/styles' as *; - -.page { - min-height: 100vh; - min-height: 100dvh; - @include flex-column-center; - background-color: $bg-default; - background-image: radial-gradient( - circle, - $bg-landing 1px, - transparent 1px - ); - background-size: 20px 20px; - padding: $space-8; -} - -.header { - text-align: center; - margin-bottom: $space-5; -} - -.title { - font-size: $font-size-4xl; - font-weight: $font-weight-semibold; - color: $text-default; - letter-spacing: $tracking-tight; - margin-bottom: $space-2; -} - -.subtitle { - font-size: $font-size-lg; - color: $text-light; - margin-bottom: $space-3; -} - -.github { - display: inline-flex; - color: $text-muted; - font-size: $font-size-2xl; - @include transition-fast; - - @include hover { - &:hover { - color: $text-default; - } - } -} - -.content { - max-width: 800px; - text-align: center; -} - -.description { - font-size: $font-size-base; - color: $text-light; - line-height: $line-height-relaxed; - margin-bottom: $space-8; -} - -.sections { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: $space-6; - margin-bottom: $space-10; - text-align: left; - - @include breakpoint-down(md) { - grid-template-columns: 1fr; - } -} - -.section { - padding: $space-5; - background-color: $bg-surface-75; - border: 1px solid $border-muted; - border-radius: $radius-lg; -} - -.sectionTitle { - font-size: $font-size-sm; - font-weight: $font-weight-semibold; - color: $text-default; - margin-bottom: $space-3; - letter-spacing: $tracking-wide; - text-transform: uppercase; -} - -.features { - list-style: none; - padding: 0; - margin: 0; - display: flex; - flex-direction: column; - gap: $space-2; - - li { - font-size: $font-size-sm; - color: $text-muted; - - &::before { - content: '→'; - margin-right: $space-2; - color: $text-lighter; - } - } -} - -.actions { - display: flex; - gap: $space-3; - justify-content: center; - flex-wrap: wrap; -} - -.button { - display: inline-flex; - align-items: center; - justify-content: center; - padding: $space-3 $space-5; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $bg-default; - background-color: $text-default; - border-radius: $radius-md; - text-decoration: none; - @include transition-fast; - - @include hover { - &:hover { - filter: brightness(0.9); - } - } -} - -.buttonOutline { - display: inline-flex; - align-items: center; - justify-content: center; - padding: $space-3 $space-5; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $text-default; - background-color: transparent; - border: 1px solid $border-default; - border-radius: $radius-md; - text-decoration: none; - @include transition-fast; - - @include hover { - &:hover { - border-color: $border-strong; - background-color: $bg-surface-75; - } - } -} diff --git a/frontends/react-scss/src/pages/login/login.module.scss b/frontends/react-scss/src/pages/login/login.module.scss deleted file mode 100644 index 6d01ed2..0000000 --- a/frontends/react-scss/src/pages/login/login.module.scss +++ /dev/null @@ -1,171 +0,0 @@ -// =================== -// © AngelaMos | 2025 -// login.module.scss -// =================== - -@use '@/styles' as *; - -.page { - min-height: 100vh; - min-height: 100dvh; - @include flex-center; - background-color: $bg-default; - background-image: radial-gradient( - circle, - $bg-page 1px, - transparent 1px - ); - background-size: 22px 22px; - padding: $space-4; -} - -.card { - width: 100%; - max-width: 400px; - background-color: $black; - background-image: radial-gradient( - circle, - $bg-card 1px, - transparent 1px - ); - background-size: 20px 20px; - border: 1px solid $border-default; - border-radius: $radius-lg; - padding: $space-8; -} - -.header { - margin-bottom: $space-6; -} - -.title { - font-size: $font-size-2xl; - font-weight: $font-weight-semibold; - color: $white; - margin-bottom: $space-2; -} - -.subtitle { - font-size: $font-size-sm; - color: $text-light; -} - -.form { - @include flex-column; - gap: $space-5; -} - -.field { - @include flex-column; - gap: $space-2; -} - -.label { - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $white; -} - -.input { - width: 100%; - height: 48px; - padding: 0 $space-4; - background-color: transparent; - border: 1px solid $border-default; - border-radius: $radius-md; - font-size: $font-size-base; - color: $white; - @include transition-fast; - - &::placeholder { - color: $text-muted; - } - - &:focus { - outline: none; - border-color: $border-strong; - } - - &[aria-invalid='true'] { - border-color: $error-default; - } -} - -.inputWrapper { - position: relative; - width: 100%; -} - -.eyeButton { - position: absolute; - right: $space-3; - top: 50%; - transform: translateY(-50%); - display: flex; - align-items: center; - justify-content: center; - background: transparent; - border: none; - color: $text-muted; - cursor: pointer; - padding: $space-1; - @include transition-fast; - - &:hover { - color: $white; - } - - svg { - width: 20px; - height: 20px; - } -} - -.error { - font-size: $font-size-xs; - color: $error-default; -} - -.submit { - width: 100%; - height: 48px; - margin-top: $space-2; - display: flex; - align-items: center; - justify-content: center; - background-color: $white; - border: none; - border-radius: $radius-md; - font-size: $font-size-base; - font-weight: $font-weight-medium; - color: $black; - cursor: pointer; - @include transition-fast; - - @include hover { - filter: brightness(0.9); - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } -} - -.footer { - margin-top: $space-6; - text-align: center; - font-size: $font-size-sm; - color: $text-light; -} - -.link { - color: $text-default; - text-decoration: underline; - text-underline-offset: 4px; - @include transition-fast; - - @include hover { - color: $text-light; - } -} diff --git a/frontends/react-scss/src/pages/register/register.module.scss b/frontends/react-scss/src/pages/register/register.module.scss deleted file mode 100644 index 55ad571..0000000 --- a/frontends/react-scss/src/pages/register/register.module.scss +++ /dev/null @@ -1,171 +0,0 @@ -// =================== -// © AngelaMos | 2025 -// register.module.scss -// =================== - -@use '@/styles' as *; - -.page { - min-height: 100vh; - min-height: 100dvh; - @include flex-center; - background-color: $bg-default; - background-image: radial-gradient( - circle, - $bg-page 1px, - transparent 1px - ); - background-size: 22px 22px; - padding: $space-4; -} - -.card { - width: 100%; - max-width: 400px; - background-color: $black; - background-image: radial-gradient( - circle, - $bg-card 1px, - transparent 1px - ); - background-size: 20px 20px; - border: 1px solid $border-default; - border-radius: $radius-lg; - padding: $space-8; -} - -.header { - margin-bottom: $space-6; -} - -.title { - font-size: $font-size-2xl; - font-weight: $font-weight-semibold; - color: $white; - margin-bottom: $space-2; -} - -.subtitle { - font-size: $font-size-sm; - color: $text-light; -} - -.form { - @include flex-column; - gap: $space-5; -} - -.field { - @include flex-column; - gap: $space-2; -} - -.label { - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $white; -} - -.input { - width: 100%; - height: 48px; - padding: 0 $space-4; - background-color: transparent; - border: 1px solid $border-default; - border-radius: $radius-md; - font-size: $font-size-base; - color: $white; - @include transition-fast; - - &::placeholder { - color: $text-muted; - } - - &:focus { - outline: none; - border-color: $border-strong; - } - - &[aria-invalid='true'] { - border-color: $error-default; - } -} - -.inputWrapper { - position: relative; - width: 100%; -} - -.eyeButton { - position: absolute; - right: $space-3; - top: 50%; - transform: translateY(-50%); - display: flex; - align-items: center; - justify-content: center; - background: transparent; - border: none; - color: $text-muted; - cursor: pointer; - padding: $space-1; - @include transition-fast; - - &:hover { - color: $white; - } - - svg { - width: 20px; - height: 20px; - } -} - -.error { - font-size: $font-size-xs; - color: $error-default; -} - -.submit { - width: 100%; - height: 48px; - margin-top: $space-2; - display: flex; - align-items: center; - justify-content: center; - background-color: $white; - border: none; - border-radius: $radius-md; - font-size: $font-size-base; - font-weight: $font-weight-medium; - color: $black; - cursor: pointer; - @include transition-fast; - - @include hover { - filter: brightness(0.9); - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } -} - -.footer { - margin-top: $space-6; - text-align: center; - font-size: $font-size-sm; - color: $text-light; -} - -.link { - color: $text-default; - text-decoration: underline; - text-underline-offset: 4px; - @include transition-fast; - - @include hover { - color: $text-light; - } -} diff --git a/frontends/react-scss/src/pages/settings/settings.module.scss b/frontends/react-scss/src/pages/settings/settings.module.scss deleted file mode 100644 index 16d8585..0000000 --- a/frontends/react-scss/src/pages/settings/settings.module.scss +++ /dev/null @@ -1,109 +0,0 @@ -// =================== -// © AngelaMos | 2025 -// settings.module.scss -// =================== - -@use '@/styles' as *; - -.page { - min-height: calc(100vh - 56px); - padding: $space-6; - background-color: $bg-default; -} - -.container { - max-width: 800px; - margin: 0 auto; -} - -.header { - margin-bottom: $space-8; -} - -.title { - font-size: $font-size-2xl; - font-weight: $font-weight-semibold; - color: $text-default; - margin-bottom: $space-2; -} - -.subtitle { - font-size: $font-size-sm; - color: $text-lighter; -} - -.section { - margin-bottom: $space-8; -} - -.sectionTitle { - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $text-light; - text-transform: uppercase; - letter-spacing: $tracking-wide; - margin-bottom: $space-4; -} - -.grid { - display: grid; - gap: $space-4; - - @include breakpoint-up('md') { - grid-template-columns: repeat(2, 1fr); - } -} - -.card { - padding: $space-4; - background: $bg-surface-100; - border: 1px solid $border-default; - border-radius: $radius-lg; -} - -.hookName { - display: inline-block; - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $text-default; - background: $bg-surface-200; - padding: $space-1 $space-2; - border-radius: $radius-sm; - margin-bottom: $space-2; -} - -.description { - font-size: $font-size-sm; - color: $text-light; - margin-bottom: $space-3; - line-height: $line-height-relaxed; -} - -.meta { - @include flex-column; - gap: $space-1; -} - -.file { - font-size: $font-size-xs; - color: $text-lighter; - font-family: $font-mono; -} - -.endpoint { - font-size: $font-size-xs; - color: $text-muted; - font-family: $font-mono; -} - -.list { - @include flex-column; - gap: $space-2; - padding-left: $space-5; - list-style: disc; - - li { - font-size: $font-size-sm; - color: $text-light; - } -} diff --git a/frontends/react-scss/src/styles/_reset.scss b/frontends/react-scss/src/styles/_reset.scss deleted file mode 100644 index b6af56b..0000000 --- a/frontends/react-scss/src/styles/_reset.scss +++ /dev/null @@ -1,198 +0,0 @@ -// =================== -// © AngelaMos | 2025 -// _reset.scss -// =================== - -@use 'tokens' as *; -@use 'fonts' as *; - -*, -*::before, -*::after { - box-sizing: border-box; -} - -* { - margin: 0; - padding: 0; - -webkit-tap-highlight-color: transparent; -} - -html { - font-size: 16px; - -moz-text-size-adjust: none; - -webkit-text-size-adjust: none; - text-size-adjust: none; - overflow-x: hidden; -} - -@media (prefers-reduced-motion: no-preference) { - html { - interpolate-size: allow-keywords; - scroll-behavior: smooth; - } -} - -body { - min-height: 100vh; - min-height: 100dvh; - line-height: $line-height-normal; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - overflow-x: hidden; - background-color: $bg-default; - color: $text-default; - font-family: $font-sans; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - line-height: $line-height-tight; - text-wrap: balance; - overflow-wrap: break-word; - font-weight: $font-weight-semibold; -} - -p { - text-wrap: pretty; - overflow-wrap: break-word; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: none; -} - -img, -picture, -video, -canvas, -svg { - display: block; - max-width: 100%; - height: auto; -} - -input, -button, -textarea, -select { - font: inherit; - color: inherit; -} - -input[type='text'], -input[type='email'], -input[type='password'], -input[type='search'], -input[type='number'], -input[type='tel'], -input[type='url'], -textarea, -select { - font-size: $font-size-sm; - appearance: none; -} - -button { - background: none; - border: none; - cursor: pointer; - text-align: inherit; - font-family: inherit; -} - -fieldset { - border: none; -} - -table { - border-collapse: collapse; - border-spacing: 0; -} - -textarea:not([rows]) { - min-height: 10em; -} - -:target { - scroll-margin-block: 5ex; -} - -:focus-visible { - outline: 2px solid $border-strong; - outline-offset: 2px; -} - -:focus:not(:focus-visible) { - outline: none; -} - -[hidden] { - display: none !important; -} - -[disabled] { - cursor: not-allowed; - opacity: 0.5; -} - -dialog { - padding: 0; - border: none; - background: transparent; -} - -summary { - cursor: pointer; -} - -@media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - scroll-behavior: auto !important; - } -} - -@supports (padding: max(0px)) { - body { - padding-left: max(0px, env(safe-area-inset-left)); - padding-right: max(0px, env(safe-area-inset-right)); - } -} - -::-webkit-scrollbar { - width: 6px; - height: 6px; -} - -::-webkit-scrollbar-track { - background: transparent; -} - -::-webkit-scrollbar-thumb { - background: $border-default; - border-radius: 3px; -} - -::-webkit-scrollbar-thumb:hover { - background: $border-strong; -} - -::selection { - background-color: $bg-selection; -} diff --git a/stacks/fastapi-react/frontend/index.html b/stacks/fastapi-react/frontend/index.html index 7f253ff..c10c3cc 100644 --- a/stacks/fastapi-react/frontend/index.html +++ b/stacks/fastapi-react/frontend/index.html @@ -26,8 +26,11 @@ /> + + + Full Stack Template
- NavBar Template + FST
-
-
- -
- setConfirmPassword(e.target.value)} - autoComplete="new-password" - /> - +
+ +
+ setPassword(e.target.value)} + autoComplete="new-password" + /> + +
-
- - - - -

- Already have an account?{' '} - - Login - -

+ +
+ +
+ setConfirmPassword(e.target.value)} + autoComplete="new-password" + /> + +
+
+ + + + +

+ Already have an account?{' '} + + Login + +

+
) diff --git a/stacks/fastapi-react/frontend/src/pages/register/register.module.scss b/stacks/fastapi-react/frontend/src/pages/register/register.module.scss index 55ad571..6d2221d 100644 --- a/stacks/fastapi-react/frontend/src/pages/register/register.module.scss +++ b/stacks/fastapi-react/frontend/src/pages/register/register.module.scss @@ -1,53 +1,116 @@ // =================== -// © AngelaMos | 2025 +// © AngelaMos | 2026 // register.module.scss // =================== -@use '@/styles' as *; +@use '../../styles' as *; .page { min-height: 100vh; min-height: 100dvh; - @include flex-center; - background-color: $bg-default; - background-image: radial-gradient( - circle, - $bg-page 1px, - transparent 1px - ); - background-size: 22px 22px; - padding: $space-4; + background: $bg-default; + color: $text-default; + @include flex-column; + + @include breakpoint-up('md') { + flex-direction: row; + } } -.card { - width: 100%; - max-width: 400px; - background-color: $black; - background-image: radial-gradient( - circle, - $bg-card 1px, - transparent 1px - ); - background-size: 20px 20px; - border: 1px solid $border-default; - border-radius: $radius-lg; - padding: $space-8; +.panel { + padding: $space-10 $space-6; + @include flex-column; + justify-content: center; + + @include breakpoint-up('md') { + flex: 1; + padding: $space-12 $space-8; + } + + @include breakpoint-up('lg') { + padding: $space-16 $space-12; + } } -.header { - margin-bottom: $space-6; +.backLink { + font-family: $font-mono; + font-size: $font-size-2xs; + text-transform: uppercase; + letter-spacing: $tracking-wide; + color: $text-lighter; + text-decoration: none; + @include transition-fast; + display: inline-block; + margin-bottom: $space-4; + + @include hover { + color: $accent-default; + } } .title { - font-size: $font-size-2xl; - font-weight: $font-weight-semibold; - color: $white; - margin-bottom: $space-2; + font-weight: $font-weight-black; + font-size: clamp(#{$font-size-4xl}, 6vw, #{$font-size-7xl}); + line-height: 0.92; + letter-spacing: $tracking-tighter; } -.subtitle { - font-size: $font-size-sm; - color: $text-light; +.meta { + display: flex; + flex-wrap: wrap; + gap: $space-4; + margin-top: $space-6; +} + +.metaItem { + font-family: $font-mono; + font-size: $font-size-2xs; + text-transform: uppercase; + letter-spacing: $tracking-widest; + color: $text-lighter; +} + +.pattern { + height: 40px; + margin-top: $space-10; + background: repeating-linear-gradient( + -45deg, + transparent, + transparent 4px, + $border-default 4px, + $border-default 5px + ); + + @include breakpoint-down('md') { + display: none; + } +} + +.formPanel { + @include flex-column-center; + padding: $space-6; + + @include breakpoint-up('md') { + flex: 1; + background: $bg-alternative; + border-left: 1px solid $border-default; + padding: $space-12 $space-8; + } +} + +.formContainer { + width: 100%; + max-width: 380px; +} + +.formLabel { + display: block; + font-family: $font-mono; + font-size: $font-size-2xs; + text-transform: uppercase; + letter-spacing: $tracking-widest; + color: $accent-default; + margin-bottom: $space-6; } .form { @@ -57,43 +120,45 @@ .field { @include flex-column; - gap: $space-2; + gap: $space-1-5; } .label { - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $white; + font-family: $font-mono; + font-size: $font-size-2xs; + text-transform: uppercase; + letter-spacing: $tracking-widest; + color: $text-lighter; +} + +.inputWrapper { + position: relative; + display: flex; + align-items: center; } .input { width: 100%; - height: 48px; - padding: 0 $space-4; - background-color: transparent; - border: 1px solid $border-default; - border-radius: $radius-md; - font-size: $font-size-base; - color: $white; + padding: $space-2-5 $space-3; + background: transparent; + border: 1px solid $border-strong; + color: $text-default; + font-size: $font-size-sm; + outline: none; @include transition-fast; + border-radius: 0; &::placeholder { color: $text-muted; } &:focus { - outline: none; - border-color: $border-strong; - } - - &[aria-invalid='true'] { - border-color: $error-default; + border-color: $accent-default; } } -.inputWrapper { - position: relative; - width: 100%; +.inputWrapper .input { + padding-right: $space-10; } .eyeButton { @@ -101,49 +166,38 @@ right: $space-3; top: 50%; transform: translateY(-50%); + color: $text-lighter; + @include transition-fast; display: flex; align-items: center; - justify-content: center; - background: transparent; - border: none; - color: $text-muted; - cursor: pointer; - padding: $space-1; - @include transition-fast; - &:hover { - color: $white; + @include hover { + color: $text-default; } svg { - width: 20px; - height: 20px; + width: 16px; + height: 16px; } } -.error { - font-size: $font-size-xs; - color: $error-default; -} - .submit { width: 100%; - height: 48px; + padding: $space-3 $space-4; margin-top: $space-2; - display: flex; - align-items: center; - justify-content: center; - background-color: $white; - border: none; - border-radius: $radius-md; - font-size: $font-size-base; + background: $accent-default; + color: $bg-default; + font-family: $font-mono; + font-size: $font-size-xs; font-weight: $font-weight-medium; - color: $black; - cursor: pointer; + text-transform: uppercase; + letter-spacing: $tracking-wider; + border: 1px solid $accent-default; @include transition-fast; @include hover { - filter: brightness(0.9); + background: $accent-hover; + border-color: $accent-hover; } &:disabled { @@ -154,18 +208,20 @@ .footer { margin-top: $space-6; + font-family: $font-mono; + font-size: $font-size-2xs; + text-transform: uppercase; + letter-spacing: $tracking-wide; + color: $text-lighter; text-align: center; - font-size: $font-size-sm; - color: $text-light; } .link { - color: $text-default; - text-decoration: underline; - text-underline-offset: 4px; + color: $accent-default; + text-decoration: none; @include transition-fast; @include hover { - color: $text-light; + color: $accent-hover; } } diff --git a/stacks/fastapi-react/frontend/src/pages/settings/index.tsx b/stacks/fastapi-react/frontend/src/pages/settings/index.tsx index 8a22eb6..5693f61 100644 --- a/stacks/fastapi-react/frontend/src/pages/settings/index.tsx +++ b/stacks/fastapi-react/frontend/src/pages/settings/index.tsx @@ -38,10 +38,10 @@ export function Component(): React.ReactElement {
+ System Configuration

Settings

- Template page — available hooks and stores for building your settings - UI + Available hooks and stores for building your settings UI

diff --git a/stacks/fastapi-react/frontend/src/pages/settings/settings.module.scss b/stacks/fastapi-react/frontend/src/pages/settings/settings.module.scss index 16d8585..28a93ee 100644 --- a/stacks/fastapi-react/frontend/src/pages/settings/settings.module.scss +++ b/stacks/fastapi-react/frontend/src/pages/settings/settings.module.scss @@ -1,109 +1,139 @@ // =================== -// © AngelaMos | 2025 +// © AngelaMos | 2026 // settings.module.scss // =================== -@use '@/styles' as *; +@use '../../styles' as *; .page { - min-height: calc(100vh - 56px); - padding: $space-6; - background-color: $bg-default; + @include flex-column; } .container { - max-width: 800px; - margin: 0 auto; + @include flex-column; + gap: $space-10; } .header { - margin-bottom: $space-8; + @include flex-column; + gap: $space-2; +} + +.tag { + font-family: $font-mono; + font-size: $font-size-2xs; + text-transform: uppercase; + letter-spacing: $tracking-widest; + color: $accent-default; } .title { - font-size: $font-size-2xl; - font-weight: $font-weight-semibold; - color: $text-default; - margin-bottom: $space-2; + font-weight: $font-weight-black; + font-size: $font-size-3xl; + letter-spacing: $tracking-tight; + line-height: $line-height-tight; + + @include breakpoint-up('md') { + font-size: $font-size-4xl; + } } .subtitle { font-size: $font-size-sm; color: $text-lighter; + max-width: 50ch; } .section { - margin-bottom: $space-8; + @include flex-column; + gap: $space-5; + padding-top: $space-6; + border-top: 1px solid $border-default; } .sectionTitle { - font-size: $font-size-sm; - font-weight: $font-weight-medium; - color: $text-light; + font-family: $font-mono; + font-size: $font-size-2xs; text-transform: uppercase; - letter-spacing: $tracking-wide; - margin-bottom: $space-4; + letter-spacing: $tracking-widest; + color: $text-lighter; + font-weight: $font-weight-medium; } .grid { display: grid; + grid-template-columns: 1fr; gap: $space-4; @include breakpoint-up('md') { - grid-template-columns: repeat(2, 1fr); + grid-template-columns: 1fr 1fr; } } .card { - padding: $space-4; - background: $bg-surface-100; + padding: $space-4 $space-5; border: 1px solid $border-default; - border-radius: $radius-lg; + @include flex-column; + gap: $space-2; } .hookName { - display: inline-block; + font-family: $font-mono; font-size: $font-size-sm; - font-weight: $font-weight-medium; color: $text-default; - background: $bg-surface-200; - padding: $space-1 $space-2; - border-radius: $radius-sm; - margin-bottom: $space-2; + font-weight: $font-weight-medium; } .description { font-size: $font-size-sm; - color: $text-light; - margin-bottom: $space-3; + color: $text-lighter; line-height: $line-height-relaxed; } .meta { - @include flex-column; - gap: $space-1; + display: flex; + flex-wrap: wrap; + gap: $space-3; + margin-top: $space-1; } .file { - font-size: $font-size-xs; - color: $text-lighter; font-family: $font-mono; + font-size: $font-size-3xs; + color: $text-muted; } .endpoint { - font-size: $font-size-xs; - color: $text-muted; font-family: $font-mono; + font-size: $font-size-3xs; + color: $accent-muted; } .list { + list-style: none; @include flex-column; - gap: $space-2; - padding-left: $space-5; - list-style: disc; + gap: $space-3; li { font-size: $font-size-sm; color: $text-light; + padding-left: $space-5; + position: relative; + padding-bottom: $space-3; + border-bottom: 1px solid $border-muted; + + &::before { + content: '>'; + position: absolute; + left: 0; + font-family: $font-mono; + color: $accent-default; + font-size: $font-size-xs; + } + + &:last-child { + border-bottom: none; + padding-bottom: 0; + } } } diff --git a/stacks/fastapi-react/frontend/src/styles.scss b/stacks/fastapi-react/frontend/src/styles.scss index 4ac95fe..c0e6d11 100644 --- a/stacks/fastapi-react/frontend/src/styles.scss +++ b/stacks/fastapi-react/frontend/src/styles.scss @@ -1,5 +1,5 @@ // =================== -// © AngelaMos | 2025 +// © AngelaMos | 2026 // styles.scss // =================== @@ -8,22 +8,21 @@ @forward 'styles/mixins'; @use 'styles/reset'; -@use 'styles/tokens' as *; -@use 'styles/fonts' as *; +@use 'styles/fonts' as f; + +body { + font-family: f.$font-sans; +} #root { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; - background: $bg-default; } .app { flex: 1; display: flex; flex-direction: column; - background: $bg-default; - color: $text-default; - font-family: $font-sans; } diff --git a/stacks/fastapi-react/frontend/src/styles/_fonts.scss b/stacks/fastapi-react/frontend/src/styles/_fonts.scss index 4f51788..17cfd74 100644 --- a/stacks/fastapi-react/frontend/src/styles/_fonts.scss +++ b/stacks/fastapi-react/frontend/src/styles/_fonts.scss @@ -5,8 +5,8 @@ @use 'tokens' as *; -$font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', Roboto, +$font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; -$font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, - 'Liberation Mono', monospace; +$font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'SF Mono', Menlo, + Consolas, 'Liberation Mono', monospace; diff --git a/stacks/fastapi-react/frontend/src/styles/_reset.scss b/stacks/fastapi-react/frontend/src/styles/_reset.scss index b6af56b..3ef859d 100644 --- a/stacks/fastapi-react/frontend/src/styles/_reset.scss +++ b/stacks/fastapi-react/frontend/src/styles/_reset.scss @@ -1,11 +1,8 @@ // =================== -// © AngelaMos | 2025 +// © AngelaMos | 2026 // _reset.scss // =================== -@use 'tokens' as *; -@use 'fonts' as *; - *, *::before, *::after { @@ -36,14 +33,13 @@ html { body { min-height: 100vh; min-height: 100dvh; - line-height: $line-height-normal; + line-height: 1.5; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; overflow-x: hidden; - background-color: $bg-default; - color: $text-default; - font-family: $font-sans; + background-color: hsl(0, 0%, 7.1%); + color: hsl(0, 0%, 98%); } h1, @@ -52,10 +48,9 @@ h3, h4, h5, h6 { - line-height: $line-height-tight; + line-height: 1.2; text-wrap: balance; overflow-wrap: break-word; - font-weight: $font-weight-semibold; } p { @@ -63,16 +58,6 @@ p { overflow-wrap: break-word; } -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: none; -} - img, picture, video, @@ -91,19 +76,6 @@ select { color: inherit; } -input[type='text'], -input[type='email'], -input[type='password'], -input[type='search'], -input[type='number'], -input[type='tel'], -input[type='url'], -textarea, -select { - font-size: $font-size-sm; - appearance: none; -} - button { background: none; border: none; @@ -112,32 +84,6 @@ button { font-family: inherit; } -fieldset { - border: none; -} - -table { - border-collapse: collapse; - border-spacing: 0; -} - -textarea:not([rows]) { - min-height: 10em; -} - -:target { - scroll-margin-block: 5ex; -} - -:focus-visible { - outline: 2px solid $border-strong; - outline-offset: 2px; -} - -:focus:not(:focus-visible) { - outline: none; -} - [hidden] { display: none !important; } @@ -147,16 +93,6 @@ textarea:not([rows]) { opacity: 0.5; } -dialog { - padding: 0; - border: none; - background: transparent; -} - -summary { - cursor: pointer; -} - @media (prefers-reduced-motion: reduce) { *, *::before, @@ -167,32 +103,3 @@ summary { scroll-behavior: auto !important; } } - -@supports (padding: max(0px)) { - body { - padding-left: max(0px, env(safe-area-inset-left)); - padding-right: max(0px, env(safe-area-inset-right)); - } -} - -::-webkit-scrollbar { - width: 6px; - height: 6px; -} - -::-webkit-scrollbar-track { - background: transparent; -} - -::-webkit-scrollbar-thumb { - background: $border-default; - border-radius: 3px; -} - -::-webkit-scrollbar-thumb:hover { - background: $border-strong; -} - -::selection { - background-color: $bg-selection; -} diff --git a/stacks/fastapi-react/frontend/src/styles/_tokens.scss b/stacks/fastapi-react/frontend/src/styles/_tokens.scss index be1f6dd..9296f5f 100644 --- a/stacks/fastapi-react/frontend/src/styles/_tokens.scss +++ b/stacks/fastapi-react/frontend/src/styles/_tokens.scss @@ -166,3 +166,36 @@ $container-lg: 32rem; $container-xl: 36rem; $container-2xl: 42rem; $container-full: 100%; + +// ============================================================================ +// ACCENT COLORS +// ============================================================================ +$accent-default: hsl(22, 85%, 52%); +$accent-hover: hsl(22, 85%, 60%); +$accent-muted: hsl(22, 40%, 35%); + +// ============================================================================ +// EXTENDED TYPOGRAPHY SCALE +// ============================================================================ +$font-size-6xl: 3.75rem; +$font-size-7xl: 4.5rem; +$font-size-8xl: 6rem; +$font-size-9xl: 8rem; + +// ============================================================================ +// EXTENDED FONT WEIGHTS +// ============================================================================ +$font-weight-bold: 700; +$font-weight-extrabold: 800; +$font-weight-black: 900; + +// ============================================================================ +// EXTENDED LETTER SPACING +// ============================================================================ +$tracking-widest: 0.1em; +$tracking-mega: 0.2em; + +// ============================================================================ +// SUCCESS COLOR +// ============================================================================ +$success-default: hsl(142, 60%, 45%);