diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/.dockerignore b/PROJECTS/advanced/ai-threat-detection/frontend/.dockerignore
new file mode 100644
index 00000000..a0256ec7
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/.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/PROJECTS/advanced/ai-threat-detection/frontend/.gitignore b/PROJECTS/advanced/ai-threat-detection/frontend/.gitignore
new file mode 100644
index 00000000..61cb0c2e
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/.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/PROJECTS/advanced/ai-threat-detection/frontend/.stylelintignore b/PROJECTS/advanced/ai-threat-detection/frontend/.stylelintignore
new file mode 100644
index 00000000..37da03e1
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/.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/PROJECTS/advanced/ai-threat-detection/frontend/biome.json b/PROJECTS/advanced/ai-threat-detection/frontend/biome.json
new file mode 100644
index 00000000..7f29029a
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/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/PROJECTS/advanced/ai-threat-detection/frontend/index.html b/PROJECTS/advanced/ai-threat-detection/frontend/index.html
new file mode 100644
index 00000000..9d7b000a
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/index.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+ AngelusVigil
+
+
+
+
+
+
+
+
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/package.json b/PROJECTS/advanced/ai-threat-detection/frontend/package.json
new file mode 100644
index 00000000..ddddc732
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "frontend-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.20",
+ "axios": "^1.13.4",
+ "react": "^19.2.4",
+ "react-dom": "^19.2.4",
+ "react-error-boundary": "^6.1.0",
+ "react-icon": "^1.0.0",
+ "react-icons": "^5.5.0",
+ "react-router-dom": "^7.13.0",
+ "sonner": "^2.0.7",
+ "zod": "^4.3.6",
+ "zustand": "^5.0.10"
+ },
+ "devDependencies": {
+ "@biomejs/biome": "^2.3.13",
+ "@tanstack/react-query-devtools": "^5.91.2",
+ "@types/node": "^25.1.0",
+ "@types/react": "^19.2.10",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^5.1.2",
+ "sass": "^1.97.3",
+ "stylelint": "^17.0.0",
+ "stylelint-config-prettier-scss": "^1.0.0",
+ "stylelint-config-standard-scss": "^17.0.0",
+ "typescript": "~5.9.3",
+ "vite": "npm:rolldown-vite@7.3.1",
+ "vite-tsconfig-paths": "^6.0.5"
+ },
+ "pnpm": {
+ "overrides": {
+ "vite": "npm:rolldown-vite@7.2.5"
+ }
+ }
+}
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/android-chrome-192x192.png b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/android-chrome-192x192.png
new file mode 100644
index 00000000..587a3c29
Binary files /dev/null and b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/android-chrome-192x192.png differ
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/android-chrome-512x512.png b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/android-chrome-512x512.png
new file mode 100644
index 00000000..d4b40f14
Binary files /dev/null and b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/android-chrome-512x512.png differ
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/apple-touch-icon.png b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/apple-touch-icon.png
new file mode 100644
index 00000000..910b753c
Binary files /dev/null and b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/apple-touch-icon.png differ
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/favicon-16x16.png b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/favicon-16x16.png
new file mode 100644
index 00000000..eb19c0dd
Binary files /dev/null and b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/favicon-16x16.png differ
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/favicon-32x32.png b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/favicon-32x32.png
new file mode 100644
index 00000000..fc1293cc
Binary files /dev/null and b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/favicon-32x32.png differ
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/favicon.ico b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/favicon.ico
new file mode 100644
index 00000000..ee279807
Binary files /dev/null and b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/favicon.ico differ
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/site.webmanifest b/PROJECTS/advanced/ai-threat-detection/frontend/public/assets/site.webmanifest
new file mode 100644
index 00000000..1dd91123
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/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"}
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/App.tsx b/PROJECTS/advanced/ai-threat-detection/frontend/src/App.tsx
new file mode 100644
index 00000000..efbd5388
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/App.tsx
@@ -0,0 +1,35 @@
+// ===================
+// © 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'
+
+export default function App(): React.ReactElement {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/api/hooks/index.ts b/PROJECTS/advanced/ai-threat-detection/frontend/src/api/hooks/index.ts
new file mode 100644
index 00000000..6221af9a
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/api/hooks/index.ts
@@ -0,0 +1,4 @@
+// ===================
+// © AngelaMos | 2026
+// index.ts
+// ===================
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/api/index.ts b/PROJECTS/advanced/ai-threat-detection/frontend/src/api/index.ts
new file mode 100644
index 00000000..8eb1f51a
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/api/index.ts
@@ -0,0 +1,7 @@
+// ===================
+// © AngelaMos | 2026
+// index.ts
+// ===================
+
+export * from './hooks'
+export * from './types'
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/api/types/index.ts b/PROJECTS/advanced/ai-threat-detection/frontend/src/api/types/index.ts
new file mode 100644
index 00000000..6221af9a
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/api/types/index.ts
@@ -0,0 +1,4 @@
+// ===================
+// © AngelaMos | 2026
+// index.ts
+// ===================
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/components/index.tsx b/PROJECTS/advanced/ai-threat-detection/frontend/src/components/index.tsx
new file mode 100644
index 00000000..9f7eeadd
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/components/index.tsx
@@ -0,0 +1,4 @@
+/**
+ * ©AngelaMos | 2026
+ * index.tsx
+ */
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/config.ts b/PROJECTS/advanced/ai-threat-detection/frontend/src/config.ts
new file mode 100644
index 00000000..f608e046
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/config.ts
@@ -0,0 +1,78 @@
+// ===================
+// © AngelaMos | 2026
+// config.ts
+// ===================
+
+export const API_ENDPOINTS = {
+ HEALTH: '/health',
+ READY: '/ready',
+ THREATS: {
+ LIST: '/threats',
+ BY_ID: (id: string) => `/threats/${id}`,
+ },
+ STATS: '/stats',
+ MODELS: {
+ STATUS: '/models/status',
+ RETRAIN: '/models/retrain',
+ },
+} as const
+
+export const WS_ENDPOINTS = {
+ ALERTS: '/ws/alerts',
+} as const
+
+export const QUERY_KEYS = {
+ THREATS: {
+ ALL: ['threats'] as const,
+ LIST: (params: Record) =>
+ [...QUERY_KEYS.THREATS.ALL, 'list', params] as const,
+ BY_ID: (id: string) =>
+ [...QUERY_KEYS.THREATS.ALL, 'detail', id] as const,
+ },
+ STATS: {
+ ALL: ['stats'] as const,
+ BY_RANGE: (range: string) =>
+ [...QUERY_KEYS.STATS.ALL, range] as const,
+ },
+ MODELS: {
+ ALL: ['models'] as const,
+ STATUS: () => [...QUERY_KEYS.MODELS.ALL, 'status'] as const,
+ },
+} as const
+
+export const ROUTES = {
+ DASHBOARD: '/',
+ THREATS: '/threats',
+ MODELS: '/models',
+} as const
+
+export const STORAGE_KEYS = {
+ UI: 'ui-storage',
+} as const
+
+export const QUERY_CONFIG = {
+ STALE_TIME: {
+ STANDARD: 1000 * 60 * 5,
+ FREQUENT: 1000 * 30,
+ STATIC: Infinity,
+ },
+ GC_TIME: {
+ DEFAULT: 1000 * 60 * 30,
+ LONG: 1000 * 60 * 60,
+ },
+ RETRY: {
+ DEFAULT: 3,
+ NONE: 0,
+ },
+} as const
+
+export const PAGINATION = {
+ DEFAULT_LIMIT: 50,
+ MAX_LIMIT: 100,
+} as const
+
+export const ALERTS = {
+ MAX_ITEMS: 50,
+ RECONNECT_BASE_MS: 1000,
+ RECONNECT_MAX_MS: 30000,
+} as const
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/core/api/api.config.ts b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/api/api.config.ts
new file mode 100644
index 00000000..0901c75c
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/api/api.config.ts
@@ -0,0 +1,24 @@
+// ===================
+// © AngelaMos | 2026
+// api.config.ts
+// ===================
+
+import axios, { type AxiosError, type AxiosInstance } from 'axios'
+import { transformAxiosError } from './errors'
+
+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' },
+})
+
+apiClient.interceptors.response.use(
+ (response) => response,
+ (error: AxiosError): Promise => {
+ return Promise.reject(transformAxiosError(error))
+ },
+)
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/core/api/errors.ts b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/api/errors.ts
new file mode 100644
index 00000000..786c90d4
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/api/errors.ts
@@ -0,0 +1,100 @@
+// ===================
+// © AngelaMos | 2026
+// errors.ts
+// ===================
+
+import type { AxiosError } from 'axios'
+
+export const ApiErrorCode = {
+ NETWORK_ERROR: 'NETWORK_ERROR',
+ VALIDATION_ERROR: 'VALIDATION_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 to the server. Check your connection.',
+ [ApiErrorCode.VALIDATION_ERROR]: 'Invalid request parameters.',
+ [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 the server. 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'
+ const details: Record | undefined = undefined
+
+ if (data?.detail) {
+ if (typeof data.detail === 'string') {
+ message = data.detail
+ }
+ } else if (data?.message) {
+ message = data.message
+ }
+
+ const codeMap: Record = {
+ 400: ApiErrorCode.VALIDATION_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/PROJECTS/advanced/ai-threat-detection/frontend/src/core/api/index.ts b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/api/index.ts
new file mode 100644
index 00000000..f6dc3639
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/api/index.ts
@@ -0,0 +1,8 @@
+// ===================
+// © AngelaMos | 2026
+// index.ts
+// ===================
+
+export * from './api.config'
+export * from './errors'
+export * from './query.config'
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/core/api/query.config.ts b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/api/query.config.ts
new file mode 100644
index 00000000..c2559e62
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/api/query.config.ts
@@ -0,0 +1,98 @@
+// ===================
+// © AngelaMos | 2026
+// 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.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.STANDARD,
+ 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,
+ },
+} as const
+
+export type QueryStrategy = keyof typeof QUERY_STRATEGIES
+
+export const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: QUERY_CONFIG.STALE_TIME.STANDARD,
+ 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/PROJECTS/advanced/ai-threat-detection/frontend/src/core/app/routers.tsx b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/app/routers.tsx
new file mode 100644
index 00000000..2cee809e
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/app/routers.tsx
@@ -0,0 +1,34 @@
+// ===================
+// © AngelaMos | 2026
+// routers.tsx
+// ===================
+
+import { Navigate, createBrowserRouter, type RouteObject } from 'react-router-dom'
+import { ROUTES } from '@/config'
+import { Shell } from './shell'
+
+const routes: RouteObject[] = [
+ {
+ element: ,
+ children: [
+ {
+ path: ROUTES.DASHBOARD,
+ lazy: () => import('@/pages/dashboard'),
+ },
+ {
+ path: ROUTES.THREATS,
+ lazy: () => import('@/pages/threats'),
+ },
+ {
+ path: ROUTES.MODELS,
+ lazy: () => import('@/pages/models'),
+ },
+ ],
+ },
+ {
+ path: '*',
+ element: ,
+ },
+]
+
+export const router = createBrowserRouter(routes)
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/core/app/shell.module.scss b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/app/shell.module.scss
new file mode 100644
index 00000000..53789f3c
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/app/shell.module.scss
@@ -0,0 +1,331 @@
+// ===================
+// © AngelaMos | 2026
+// 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/PROJECTS/advanced/ai-threat-detection/frontend/src/core/app/shell.tsx b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/app/shell.tsx
new file mode 100644
index 00000000..7c564fb1
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/app/shell.tsx
@@ -0,0 +1,118 @@
+// ===================
+// © AngelaMos | 2026
+// shell.tsx
+// ===================
+
+import { Suspense } from 'react'
+import { ErrorBoundary } from 'react-error-boundary'
+import { LuChevronLeft, LuChevronRight, LuCpu, LuLayoutDashboard, LuMenu, LuShield } 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: LuLayoutDashboard },
+ { path: ROUTES.THREATS, label: 'Threats', icon: LuShield },
+ { path: ROUTES.MODELS, label: 'Models', icon: LuCpu },
+]
+
+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 && (
+
+ )
+}
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/core/app/toast.module.scss b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/app/toast.module.scss
new file mode 100644
index 00000000..29b3f614
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/core/app/toast.module.scss
@@ -0,0 +1,67 @@
+// ===================
+// © AngelaMos | 2026
+// 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/PROJECTS/advanced/ai-threat-detection/frontend/src/main.tsx b/PROJECTS/advanced/ai-threat-detection/frontend/src/main.tsx
new file mode 100644
index 00000000..ac319510
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/main.tsx
@@ -0,0 +1,15 @@
+// ===========================
+// ©AngelaMos | 2026
+// 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/PROJECTS/advanced/ai-threat-detection/frontend/src/pages/dashboard/dashboard.module.scss b/PROJECTS/advanced/ai-threat-detection/frontend/src/pages/dashboard/dashboard.module.scss
new file mode 100644
index 00000000..ec05e16e
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/pages/dashboard/dashboard.module.scss
@@ -0,0 +1,152 @@
+// ===================
+// © AngelaMos | 2026
+// 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/PROJECTS/advanced/ai-threat-detection/frontend/src/pages/dashboard/index.tsx b/PROJECTS/advanced/ai-threat-detection/frontend/src/pages/dashboard/index.tsx
new file mode 100644
index 00000000..4b1fca5d
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/pages/dashboard/index.tsx
@@ -0,0 +1,10 @@
+// ===================
+// © AngelaMos | 2026
+// index.tsx
+// ===================
+
+export function Component(): React.ReactElement {
+ return Dashboard
+}
+
+Component.displayName = 'DashboardPage'
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/pages/models/index.tsx b/PROJECTS/advanced/ai-threat-detection/frontend/src/pages/models/index.tsx
new file mode 100644
index 00000000..9f0c013d
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/pages/models/index.tsx
@@ -0,0 +1,10 @@
+// ===================
+// © AngelaMos | 2026
+// index.tsx
+// ===================
+
+export function Component(): React.ReactElement {
+ return Models
+}
+
+Component.displayName = 'ModelsPage'
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/pages/threats/index.tsx b/PROJECTS/advanced/ai-threat-detection/frontend/src/pages/threats/index.tsx
new file mode 100644
index 00000000..3dd9c9ac
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/pages/threats/index.tsx
@@ -0,0 +1,10 @@
+// ===================
+// © AngelaMos | 2026
+// index.tsx
+// ===================
+
+export function Component(): React.ReactElement {
+ return Threats
+}
+
+Component.displayName = 'ThreatsPage'
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/styles.scss b/PROJECTS/advanced/ai-threat-detection/frontend/src/styles.scss
new file mode 100644
index 00000000..e06571ba
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/styles.scss
@@ -0,0 +1,29 @@
+// ===================
+// © AngelaMos | 2026
+// styles.scss
+// ===================
+
+@forward 'styles/tokens';
+@forward 'styles/fonts';
+@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/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_fonts.scss b/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_fonts.scss
new file mode 100644
index 00000000..10d59dca
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_fonts.scss
@@ -0,0 +1,12 @@
+// ===================
+// © AngelaMos | 2026
+// _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/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_index.scss b/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_index.scss
new file mode 100644
index 00000000..9d5d028c
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_index.scss
@@ -0,0 +1,8 @@
+// ===================
+// © AngelaMos | 2026
+// _index.scss
+// ===================
+
+@forward 'tokens';
+@forward 'fonts';
+@forward 'mixins';
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_mixins.scss b/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_mixins.scss
new file mode 100644
index 00000000..288b4e67
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_mixins.scss
@@ -0,0 +1,120 @@
+// ===================
+// © AngelaMos | 2026
+// _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/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_reset.scss b/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_reset.scss
new file mode 100644
index 00000000..fb9eca5b
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_reset.scss
@@ -0,0 +1,198 @@
+// ===================
+// © AngelaMos | 2026
+// _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/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_tokens.scss b/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_tokens.scss
new file mode 100644
index 00000000..f3c63ba5
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/src/styles/_tokens.scss
@@ -0,0 +1,168 @@
+// ===================
+// © AngelaMos | 2026
+// _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/PROJECTS/advanced/ai-threat-detection/frontend/stylelint.config.js b/PROJECTS/advanced/ai-threat-detection/frontend/stylelint.config.js
new file mode 100644
index 00000000..56ebd7bc
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/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/PROJECTS/advanced/ai-threat-detection/frontend/tsconfig.app.json b/PROJECTS/advanced/ai-threat-detection/frontend/tsconfig.app.json
new file mode 100644
index 00000000..afe17f60
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/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/PROJECTS/advanced/ai-threat-detection/frontend/tsconfig.json b/PROJECTS/advanced/ai-threat-detection/frontend/tsconfig.json
new file mode 100644
index 00000000..1ffef600
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/PROJECTS/advanced/ai-threat-detection/frontend/tsconfig.node.json b/PROJECTS/advanced/ai-threat-detection/frontend/tsconfig.node.json
new file mode 100644
index 00000000..a96b3e59
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/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/PROJECTS/advanced/ai-threat-detection/frontend/vite.config.ts b/PROJECTS/advanced/ai-threat-detection/frontend/vite.config.ts
new file mode 100644
index 00000000..a4ad5b31
--- /dev/null
+++ b/PROJECTS/advanced/ai-threat-detection/frontend/vite.config.ts
@@ -0,0 +1,71 @@
+/**
+ * ©AngelaMos | 2026
+ * 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,
+ },
+ }
+})