diff --git a/k8s/01-namespace.yaml b/k8s/01-namespace.yaml
new file mode 100644
index 0000000..a1c9461
--- /dev/null
+++ b/k8s/01-namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: gmrelay-test
diff --git a/k8s/02-secrets.yaml b/k8s/02-secrets.yaml
new file mode 100644
index 0000000..0c7da3b
--- /dev/null
+++ b/k8s/02-secrets.yaml
@@ -0,0 +1,10 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: gmrelay-secrets
+ namespace: gmrelay-test
+type: Opaque
+stringData:
+ POSTGRES_PASSWORD: "TestPassword123"
+ TELEGRAM_BOT_TOKEN: "8641931558:AAGBARuuTZUXX7V3YrtJwN3y_q0NUVkFrqk"
+ TELEGRAM_BOT_USERNAME: "GmRelayTestBot"
diff --git a/k8s/03-configmap.yaml b/k8s/03-configmap.yaml
new file mode 100644
index 0000000..7c3ea26
--- /dev/null
+++ b/k8s/03-configmap.yaml
@@ -0,0 +1,9 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: gmrelay-config
+ namespace: gmrelay-test
+data:
+ ConnectionStrings__gmrelaydb: "Host=postgres;Port=5432;Database=gmrelay_db;Username=gmrelay;Password=$(POSTGRES_PASSWORD)"
+ Telegram__MiniAppUrl: ""
+ ASPNETCORE_URLS: "http://+:8080"
diff --git a/k8s/04-postgres.yaml b/k8s/04-postgres.yaml
new file mode 100644
index 0000000..ea60c59
--- /dev/null
+++ b/k8s/04-postgres.yaml
@@ -0,0 +1,68 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: postgres-pvc
+ namespace: gmrelay-test
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: postgres
+ namespace: gmrelay-test
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: postgres
+ template:
+ metadata:
+ labels:
+ app: postgres
+ spec:
+ containers:
+ - name: postgres
+ image: postgres:17-alpine
+ ports:
+ - containerPort: 5432
+ env:
+ - name: POSTGRES_USER
+ value: "gmrelay"
+ - name: POSTGRES_DB
+ value: "gmrelay_db"
+ - name: POSTGRES_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: gmrelay-secrets
+ key: POSTGRES_PASSWORD
+ volumeMounts:
+ - name: postgres-data
+ mountPath: /var/lib/postgresql/data
+ resources:
+ requests:
+ memory: "128Mi"
+ cpu: "100m"
+ limits:
+ memory: "512Mi"
+ cpu: "500m"
+ volumes:
+ - name: postgres-data
+ persistentVolumeClaim:
+ claimName: postgres-pvc
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: postgres
+ namespace: gmrelay-test
+spec:
+ selector:
+ app: postgres
+ ports:
+ - port: 5432
+ targetPort: 5432
diff --git a/k8s/05-web.yaml b/k8s/05-web.yaml
new file mode 100644
index 0000000..9563a5e
--- /dev/null
+++ b/k8s/05-web.yaml
@@ -0,0 +1,88 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: gmrelay-web
+ namespace: gmrelay-test
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: gmrelay-web
+ template:
+ metadata:
+ labels:
+ app: gmrelay-web
+ spec:
+ containers:
+ - name: web
+ image: gmrelay-web:design-refresh
+ ports:
+ - containerPort: 8080
+ envFrom:
+ - configMapRef:
+ name: gmrelay-config
+ env:
+ - name: POSTGRES_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: gmrelay-secrets
+ key: POSTGRES_PASSWORD
+ - name: ConnectionStrings__gmrelaydb
+ value: "Host=postgres;Port=5432;Database=gmrelay_db;Username=gmrelay;Password=$(POSTGRES_PASSWORD)"
+ - name: Telegram__BotToken
+ valueFrom:
+ secretKeyRef:
+ name: gmrelay-secrets
+ key: TELEGRAM_BOT_TOKEN
+ - name: Telegram__BotUsername
+ valueFrom:
+ secretKeyRef:
+ name: gmrelay-secrets
+ key: TELEGRAM_BOT_USERNAME
+ - name: Telegram__MiniAppUrl
+ value: ""
+ resources:
+ requests:
+ memory: "128Mi"
+ cpu: "100m"
+ limits:
+ memory: "512Mi"
+ cpu: "500m"
+ volumeMounts:
+ - name: dataprotection-keys
+ mountPath: /app/dataprotection-keys
+ volumes:
+ - name: dataprotection-keys
+ emptyDir: {}
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: gmrelay-web
+ namespace: gmrelay-test
+spec:
+ selector:
+ app: gmrelay-web
+ ports:
+ - port: 8080
+ targetPort: 8080
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: gmrelay-web
+ namespace: gmrelay-test
+ annotations:
+ nginx.ingress.kubernetes.io/rewrite-target: /
+spec:
+ rules:
+ - host: gmrelay.local
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: gmrelay-web
+ port:
+ number: 8080
diff --git a/k8s/06-bot.yaml b/k8s/06-bot.yaml
new file mode 100644
index 0000000..a4ccc56
--- /dev/null
+++ b/k8s/06-bot.yaml
@@ -0,0 +1,42 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: gmrelay-bot
+ namespace: gmrelay-test
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: gmrelay-bot
+ template:
+ metadata:
+ labels:
+ app: gmrelay-bot
+ spec:
+ containers:
+ - name: bot
+ image: gmrelay-bot:design-refresh
+ ports:
+ - containerPort: 8081
+ env:
+ - name: POSTGRES_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: gmrelay-secrets
+ key: POSTGRES_PASSWORD
+ - name: ConnectionStrings__gmrelaydb
+ value: "Host=postgres;Port=5432;Database=gmrelay_db;Username=gmrelay;Password=$(POSTGRES_PASSWORD)"
+ - name: Telegram__BotToken
+ valueFrom:
+ secretKeyRef:
+ name: gmrelay-secrets
+ key: TELEGRAM_BOT_TOKEN
+ - name: Telegram__MiniAppUrl
+ value: ""
+ resources:
+ requests:
+ memory: "128Mi"
+ cpu: "100m"
+ limits:
+ memory: "256Mi"
+ cpu: "500m"
diff --git a/src/GmRelay.Web/Components/App.razor b/src/GmRelay.Web/Components/App.razor
index 374c17b..3a904ab 100644
--- a/src/GmRelay.Web/Components/App.razor
+++ b/src/GmRelay.Web/Components/App.razor
@@ -10,7 +10,7 @@
-
+
diff --git a/src/GmRelay.Web/Components/Layout/MainLayout.razor.css b/src/GmRelay.Web/Components/Layout/MainLayout.razor.css
index 47c29e4..2953d97 100644
--- a/src/GmRelay.Web/Components/Layout/MainLayout.razor.css
+++ b/src/GmRelay.Web/Components/Layout/MainLayout.razor.css
@@ -30,8 +30,9 @@
/* === Error UI === */
#blazor-error-ui {
- background: var(--bg-secondary);
- border-top: 1px solid var(--border-color);
+ background: var(--status-danger-bg);
+ color: var(--status-danger);
+ border-top: 1px solid rgba(239, 68, 68, 0.15);
bottom: 0;
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.3);
box-sizing: border-box;
@@ -41,8 +42,9 @@
position: fixed;
width: 100%;
z-index: 1000;
- color: var(--text-secondary);
font-size: 0.875rem;
+ font-family: 'Jura', sans-serif;
+ font-weight: 500;
}
#blazor-error-ui .reload {
diff --git a/src/GmRelay.Web/Components/Layout/NavMenu.razor.css b/src/GmRelay.Web/Components/Layout/NavMenu.razor.css
index 4194f99..5eb2268 100644
--- a/src/GmRelay.Web/Components/Layout/NavMenu.razor.css
+++ b/src/GmRelay.Web/Components/Layout/NavMenu.razor.css
@@ -23,12 +23,11 @@
}
.nav-brand-text {
+ font-family: 'Cinzel Decorative', 'Cinzel', serif;
font-size: 1.125rem;
font-weight: 700;
- background: var(--accent-gradient);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
+ letter-spacing: 0.04em;
+ color: var(--text-primary);
}
.nav-toggle {
@@ -87,9 +86,10 @@
}
.nav-section ::deep .nav-item.active {
- background: rgba(124, 58, 237, 0.15);
- color: var(--accent-primary);
- border: 1px solid rgba(124, 58, 237, 0.2);
+ background: linear-gradient(135deg, rgba(139, 92, 246, 0.15) 0%, rgba(34, 211, 238, 0.08) 100%);
+ color: var(--text-accent);
+ border: 1px solid rgba(139, 92, 246, 0.25);
+ box-shadow: 0 0 12px rgba(139, 92, 246, 0.1);
}
.nav-icon {
@@ -145,7 +145,7 @@
border: 1px solid transparent;
border-radius: var(--radius-sm);
color: var(--text-muted);
- font-family: 'Inter', sans-serif;
+ font-family: 'Jura', sans-serif;
font-size: 0.8125rem;
cursor: pointer;
transition: all var(--transition-normal);
diff --git a/src/GmRelay.Web/wwwroot/app.css b/src/GmRelay.Web/wwwroot/app.css
index 8b837b4..3561089 100644
--- a/src/GmRelay.Web/wwwroot/app.css
+++ b/src/GmRelay.Web/wwwroot/app.css
@@ -1,60 +1,58 @@
/* ============================================
- GM-Relay Design System v1.9.9
- Dark RPG Dashboard Theme
+ GM-Relay Design System v2.0.0
+ Dark RPG Dashboard Theme — Refined
============================================ */
-/* --- Google Fonts loaded in App.razor --- */
-
/* === CSS Custom Properties === */
:root {
- /* Background */
- --bg-primary: #0a0e1a;
- --bg-secondary: #111827;
- --bg-card: rgba(17, 24, 39, 0.7);
- --bg-card-hover: rgba(24, 33, 54, 0.85);
- --bg-surface: rgba(255, 255, 255, 0.04);
- --bg-input: rgba(255, 255, 255, 0.06);
+ /* Background — deeper, richer darks */
+ --bg-primary: #050810;
+ --bg-secondary: #0a0f1c;
+ --bg-card: rgba(10, 15, 28, 0.75);
+ --bg-card-hover: rgba(15, 22, 40, 0.9);
+ --bg-surface: rgba(255, 255, 255, 0.035);
+ --bg-input: rgba(255, 255, 255, 0.05);
- /* Accent */
- --accent-primary: #7c3aed;
- --accent-primary-hover: #6d28d9;
- --accent-secondary: #06b6d4;
- --accent-gradient: linear-gradient(135deg, #7c3aed 0%, #06b6d4 100%);
- --accent-gradient-hover: linear-gradient(135deg, #6d28d9 0%, #0891b2 100%);
+ /* Accent — sharper, more vibrant */
+ --accent-primary: #8b5cf6;
+ --accent-primary-hover: #7c3aed;
+ --accent-secondary: #22d3ee;
+ --accent-gradient: linear-gradient(135deg, #8b5cf6 0%, #22d3ee 100%);
+ --accent-gradient-hover: linear-gradient(135deg, #7c3aed 0%, #06b6d4 100%);
/* Text */
- --text-primary: #f1f5f9;
+ --text-primary: #e2e8f0;
--text-secondary: #94a3b8;
--text-muted: #64748b;
--text-accent: #a78bfa;
/* Status */
--status-success: #22c55e;
- --status-success-bg: rgba(34, 197, 94, 0.15);
+ --status-success-bg: rgba(34, 197, 94, 0.12);
--status-warning: #f59e0b;
- --status-warning-bg: rgba(245, 158, 11, 0.15);
+ --status-warning-bg: rgba(245, 158, 11, 0.12);
--status-danger: #ef4444;
- --status-danger-bg: rgba(239, 68, 68, 0.15);
- --status-info: #06b6d4;
- --status-info-bg: rgba(6, 182, 212, 0.15);
+ --status-danger-bg: rgba(239, 68, 68, 0.12);
+ --status-info: #22d3ee;
+ --status-info-bg: rgba(34, 211, 238, 0.12);
--status-neutral: #64748b;
- --status-neutral-bg: rgba(100, 116, 139, 0.15);
+ --status-neutral-bg: rgba(100, 116, 139, 0.12);
/* Border */
- --border-color: rgba(255, 255, 255, 0.08);
- --border-glow: rgba(124, 58, 237, 0.4);
+ --border-color: rgba(255, 255, 255, 0.06);
+ --border-glow: rgba(139, 92, 246, 0.35);
/* Glass */
- --glass-bg: rgba(255, 255, 255, 0.05);
- --glass-border: rgba(255, 255, 255, 0.1);
- --glass-blur: 16px;
+ --glass-bg: rgba(255, 255, 255, 0.04);
+ --glass-border: rgba(255, 255, 255, 0.08);
+ --glass-blur: 20px;
/* Shadows */
- --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.3);
- --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.4);
- --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.5);
- --shadow-glow: 0 0 20px rgba(124, 58, 237, 0.2);
- --shadow-glow-hover: 0 0 30px rgba(124, 58, 237, 0.35);
+ --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.4);
+ --shadow-md: 0 4px 20px rgba(0, 0, 0, 0.5);
+ --shadow-lg: 0 8px 40px rgba(0, 0, 0, 0.6);
+ --shadow-glow: 0 0 24px rgba(139, 92, 246, 0.18);
+ --shadow-glow-hover: 0 0 36px rgba(139, 92, 246, 0.3);
/* Radius */
--radius-sm: 8px;
@@ -64,8 +62,8 @@
/* Transition */
--transition-fast: 0.15s ease;
- --transition-normal: 0.25s ease;
- --transition-smooth: 0.35s cubic-bezier(0.4, 0, 0.2, 1);
+ --transition-normal: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ --transition-smooth: 0.4s cubic-bezier(0.4, 0, 0.2, 1);
/* Sidebar */
--sidebar-width: 260px;
@@ -96,7 +94,7 @@ html {
}
html, body {
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ font-family: 'Jura', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
margin: 0;
@@ -106,22 +104,61 @@ html, body {
-moz-osx-font-smoothing: grayscale;
}
+/* Atmospheric background */
+body::before {
+ content: '';
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: 0;
+ background:
+ radial-gradient(ellipse 80% 60% at 20% 40%, rgba(139, 92, 246, 0.08) 0%, transparent 60%),
+ radial-gradient(ellipse 60% 50% at 80% 20%, rgba(34, 211, 238, 0.05) 0%, transparent 50%),
+ radial-gradient(ellipse 50% 40% at 50% 90%, rgba(139, 92, 246, 0.06) 0%, transparent 50%);
+ animation: atmosphere-drift 25s ease-in-out infinite alternate;
+}
+
+@keyframes atmosphere-drift {
+ 0% { transform: translate(0, 0) scale(1); }
+ 100% { transform: translate(-2%, 1%) scale(1.02); }
+}
+
+/* Subtle grain overlay */
+body::after {
+ content: '';
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: 1;
+ opacity: 0.025;
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
+ background-repeat: repeat;
+ background-size: 256px 256px;
+}
+
/* === Typography === */
h1, h2, h3, h4, h5, h6 {
+ font-family: 'Cinzel', 'Cinzel Decorative', serif;
color: var(--text-primary);
font-weight: 600;
- letter-spacing: -0.02em;
+ letter-spacing: 0.02em;
line-height: 1.3;
margin-top: 0;
}
-h1 { font-size: 1.875rem; }
-h2 { font-size: 1.5rem; }
-h3 { font-size: 1.25rem; }
+h1 { font-size: 1.75rem; }
+h2 { font-size: 1.375rem; }
+h3 { font-size: 1.125rem; }
h1:focus { outline: none; }
-p { line-height: 1.6; }
+p { line-height: 1.7; }
a {
color: var(--accent-secondary);
@@ -130,13 +167,13 @@ a {
}
a:hover {
- color: #22d3ee;
+ color: #67e8f9;
}
/* === Scrollbar === */
::-webkit-scrollbar {
- width: 6px;
- height: 6px;
+ width: 5px;
+ height: 5px;
}
::-webkit-scrollbar-track {
@@ -144,12 +181,12 @@ a:hover {
}
::-webkit-scrollbar-thumb {
- background: rgba(255, 255, 255, 0.15);
+ background: rgba(255, 255, 255, 0.12);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
- background: rgba(255, 255, 255, 0.25);
+ background: rgba(255, 255, 255, 0.22);
}
/* === Glass Card === */
@@ -161,13 +198,34 @@ a:hover {
border-radius: var(--radius-lg);
padding: 1.5rem;
transition: all var(--transition-smooth);
+ position: relative;
+ z-index: 2;
+}
+
+.glass-card::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ border-radius: inherit;
+ padding: 1px;
+ background: linear-gradient(135deg, rgba(139, 92, 246, 0.15) 0%, transparent 40%, transparent 60%, rgba(34, 211, 238, 0.1) 100%);
+ -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
+ -webkit-mask-composite: xor;
+ mask-composite: exclude;
+ pointer-events: none;
+ opacity: 0;
+ transition: opacity var(--transition-normal);
+}
+
+.glass-card:hover::before {
+ opacity: 1;
}
.glass-card:hover {
background: var(--bg-card-hover);
border-color: var(--border-glow);
box-shadow: var(--shadow-glow-hover);
- transform: translateY(-2px);
+ transform: translateY(-3px);
}
/* === Buttons === */
@@ -178,44 +236,53 @@ a:hover {
padding: 0.625rem 1.25rem;
border: none;
border-radius: var(--radius-sm);
- font-family: 'Inter', sans-serif;
+ font-family: 'Jura', sans-serif;
font-size: 0.875rem;
- font-weight: 500;
+ font-weight: 600;
cursor: pointer;
transition: all var(--transition-normal);
text-decoration: none;
line-height: 1.4;
+ letter-spacing: 0.02em;
+ position: relative;
+ overflow: hidden;
+ z-index: 2;
}
.btn-gm-primary {
background: var(--accent-gradient);
color: white;
- box-shadow: 0 2px 12px rgba(124, 58, 237, 0.3);
+ box-shadow: 0 2px 16px rgba(139, 92, 246, 0.35);
}
.btn-gm-primary:hover {
background: var(--accent-gradient-hover);
- box-shadow: 0 4px 20px rgba(124, 58, 237, 0.45);
- transform: translateY(-1px);
+ box-shadow: 0 4px 24px rgba(139, 92, 246, 0.5);
+ transform: translateY(-2px);
color: white;
}
.btn-gm-primary:active {
transform: translateY(0);
+ box-shadow: 0 1px 8px rgba(139, 92, 246, 0.3);
}
.btn-gm-success {
background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
color: white;
- box-shadow: 0 2px 12px rgba(34, 197, 94, 0.3);
+ box-shadow: 0 2px 16px rgba(34, 197, 94, 0.3);
}
.btn-gm-success:hover {
- box-shadow: 0 4px 20px rgba(34, 197, 94, 0.45);
- transform: translateY(-1px);
+ box-shadow: 0 4px 24px rgba(34, 197, 94, 0.45);
+ transform: translateY(-2px);
color: white;
}
+.btn-gm-success:active {
+ transform: translateY(0);
+}
+
.btn-gm-outline {
background: transparent;
color: var(--text-secondary);
@@ -226,22 +293,28 @@ a:hover {
background: var(--bg-surface);
color: var(--text-primary);
border-color: var(--glass-border);
+ box-shadow: 0 0 12px rgba(139, 92, 246, 0.1);
}
.btn-gm-danger {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
color: white;
+ box-shadow: 0 2px 12px rgba(239, 68, 68, 0.25);
}
.btn-gm-danger:hover {
box-shadow: 0 4px 20px rgba(239, 68, 68, 0.4);
- transform: translateY(-1px);
+ transform: translateY(-2px);
color: white;
}
+.btn-gm-danger:active {
+ transform: translateY(0);
+}
+
.btn-gm[disabled],
.btn-gm:disabled {
- opacity: 0.5;
+ opacity: 0.45;
pointer-events: none;
}
@@ -254,14 +327,15 @@ a:hover {
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 600;
- letter-spacing: 0.02em;
+ letter-spacing: 0.03em;
white-space: nowrap;
+ font-family: 'Jura', sans-serif;
}
.status-badge::before {
content: '';
- width: 6px;
- height: 6px;
+ width: 5px;
+ height: 5px;
border-radius: 50%;
flex-shrink: 0;
}
@@ -269,35 +343,35 @@ a:hover {
.status-success {
background: var(--status-success-bg);
color: var(--status-success);
- border: 1px solid rgba(34, 197, 94, 0.25);
+ border: 1px solid rgba(34, 197, 94, 0.2);
}
.status-success::before { background: var(--status-success); box-shadow: 0 0 6px var(--status-success); }
.status-warning {
background: var(--status-warning-bg);
color: var(--status-warning);
- border: 1px solid rgba(245, 158, 11, 0.25);
+ border: 1px solid rgba(245, 158, 11, 0.2);
}
.status-warning::before { background: var(--status-warning); box-shadow: 0 0 6px var(--status-warning); }
.status-danger {
background: var(--status-danger-bg);
color: var(--status-danger);
- border: 1px solid rgba(239, 68, 68, 0.25);
+ border: 1px solid rgba(239, 68, 68, 0.2);
}
.status-danger::before { background: var(--status-danger); box-shadow: 0 0 6px var(--status-danger); }
.status-info {
background: var(--status-info-bg);
color: var(--status-info);
- border: 1px solid rgba(6, 182, 212, 0.25);
+ border: 1px solid rgba(34, 211, 238, 0.2);
}
.status-info::before { background: var(--status-info); box-shadow: 0 0 6px var(--status-info); }
.status-neutral {
background: var(--status-neutral-bg);
color: var(--status-neutral);
- border: 1px solid rgba(100, 116, 139, 0.25);
+ border: 1px solid rgba(100, 116, 139, 0.2);
}
.status-neutral::before { background: var(--status-neutral); }
@@ -309,16 +383,18 @@ a:hover {
border: 1px solid var(--border-color);
border-radius: var(--radius-sm);
color: var(--text-primary);
- font-family: 'Inter', sans-serif;
+ font-family: 'Jura', sans-serif;
font-size: 0.875rem;
+ font-weight: 500;
transition: all var(--transition-normal);
outline: none;
+ letter-spacing: 0.01em;
}
.gm-form-control:focus {
border-color: var(--accent-primary);
- box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.15);
- background: rgba(255, 255, 255, 0.08);
+ box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.12);
+ background: rgba(255, 255, 255, 0.07);
}
.gm-form-control::placeholder {
@@ -329,9 +405,10 @@ a:hover {
display: block;
margin-bottom: 0.375rem;
font-size: 0.8125rem;
- font-weight: 500;
+ font-weight: 600;
color: var(--text-secondary);
- letter-spacing: 0.01em;
+ letter-spacing: 0.03em;
+ font-family: 'Jura', sans-serif;
}
.gm-form-hint {
@@ -357,10 +434,12 @@ select {
border: 1px solid var(--border-color) !important;
border-radius: var(--radius-sm) !important;
color: var(--text-primary) !important;
- font-family: 'Inter', sans-serif !important;
+ font-family: 'Jura', sans-serif !important;
font-size: 0.875rem !important;
+ font-weight: 500 !important;
padding: 0.625rem 0.875rem !important;
transition: all var(--transition-normal) !important;
+ letter-spacing: 0.01em !important;
}
.form-control:focus,
@@ -368,14 +447,14 @@ input:focus,
textarea:focus,
select:focus {
border-color: var(--accent-primary) !important;
- box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.15) !important;
- background: rgba(255, 255, 255, 0.08) !important;
+ box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.12) !important;
+ background: rgba(255, 255, 255, 0.07) !important;
outline: none !important;
}
/* Color scheme for date/time pickers */
input[type="datetime-local"]::-webkit-calendar-picker-indicator {
- filter: invert(0.7);
+ filter: invert(0.65);
}
select option {
@@ -389,18 +468,20 @@ select option {
border-collapse: separate;
border-spacing: 0;
font-size: 0.875rem;
+ font-family: 'Jura', sans-serif;
}
.gm-table thead th {
- padding: 0.75rem 1rem;
+ padding: 0.875rem 1rem;
text-align: left;
- font-weight: 600;
- font-size: 0.75rem;
+ font-weight: 700;
+ font-size: 0.7rem;
text-transform: uppercase;
- letter-spacing: 0.06em;
+ letter-spacing: 0.08em;
color: var(--text-muted);
border-bottom: 1px solid var(--border-color);
background: transparent;
+ font-family: 'Jura', sans-serif;
}
.gm-table tbody td {
@@ -470,23 +551,26 @@ select option {
display: flex;
align-items: center;
gap: 0.625rem;
+ font-weight: 500;
+ position: relative;
+ z-index: 2;
}
.gm-alert-info {
background: var(--status-info-bg);
- border: 1px solid rgba(6, 182, 212, 0.2);
+ border: 1px solid rgba(34, 211, 238, 0.15);
color: var(--status-info);
}
.gm-alert-danger {
background: var(--status-danger-bg);
- border: 1px solid rgba(239, 68, 68, 0.2);
+ border: 1px solid rgba(239, 68, 68, 0.15);
color: var(--status-danger);
}
.gm-alert-success {
background: var(--status-success-bg);
- border: 1px solid rgba(34, 197, 94, 0.2);
+ border: 1px solid rgba(34, 197, 94, 0.15);
color: var(--status-success);
}
@@ -499,6 +583,9 @@ select option {
padding: 0;
margin: 0 0 1.5rem 0;
font-size: 0.8125rem;
+ font-family: 'Jura', sans-serif;
+ position: relative;
+ z-index: 2;
}
.gm-breadcrumb li {
@@ -525,13 +612,14 @@ select option {
.gm-breadcrumb .active {
color: var(--text-primary);
+ font-weight: 600;
}
/* === Loading Skeleton === */
.skeleton {
background: linear-gradient(90deg,
var(--bg-surface) 25%,
- rgba(255, 255, 255, 0.08) 50%,
+ rgba(255, 255, 255, 0.06) 50%,
var(--bg-surface) 75%);
background-size: 200% 100%;
animation: skeleton-shimmer 1.5s ease-in-out infinite;
@@ -562,6 +650,8 @@ select option {
justify-content: center;
padding: 3rem 1.5rem;
text-align: center;
+ position: relative;
+ z-index: 2;
}
.empty-state-icon {
@@ -575,12 +665,14 @@ select option {
font-weight: 600;
color: var(--text-primary);
margin-bottom: 0.5rem;
+ font-family: 'Cinzel', serif;
}
.empty-state-text {
font-size: 0.875rem;
color: var(--text-muted);
max-width: 320px;
+ line-height: 1.6;
}
/* === Page Container === */
@@ -588,22 +680,29 @@ select option {
max-width: 960px;
margin: 0 auto;
padding: 1.5rem;
- animation: fadeIn 0.4s ease-out;
+ animation: fadeIn 0.5s ease-out;
+ position: relative;
+ z-index: 2;
}
.page-header {
margin-bottom: 2rem;
+ position: relative;
+ z-index: 2;
}
.page-header h2 {
- font-size: 1.5rem;
+ font-size: 1.375rem;
margin-bottom: 0.25rem;
+ font-family: 'Cinzel', serif;
+ font-weight: 700;
}
.page-header p {
color: var(--text-muted);
font-size: 0.875rem;
margin: 0;
+ font-family: 'Jura', sans-serif;
}
/* === Grid === */
@@ -611,6 +710,8 @@ select option {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1rem;
+ position: relative;
+ z-index: 2;
}
/* === Batch bulk operations === */
@@ -618,6 +719,8 @@ select option {
display: grid;
gap: 1rem;
margin-bottom: 1.5rem;
+ position: relative;
+ z-index: 2;
}
.batch-bulk-card {
@@ -627,6 +730,14 @@ select option {
border: 1px solid var(--glass-border);
border-radius: var(--radius-md);
padding: 1.25rem;
+ position: relative;
+ z-index: 2;
+ transition: all var(--transition-smooth);
+}
+
+.batch-bulk-card:hover {
+ border-color: var(--border-glow);
+ box-shadow: var(--shadow-glow);
}
.batch-bulk-header {
@@ -641,6 +752,7 @@ select option {
font-size: 1rem;
margin-bottom: 0.25rem;
overflow-wrap: anywhere;
+ font-family: 'Cinzel', serif;
}
.batch-bulk-header p {
@@ -676,6 +788,8 @@ select option {
/* === Campaign templates === */
.campaign-template-panel {
margin-bottom: 1.5rem;
+ position: relative;
+ z-index: 2;
}
.campaign-template-fields {
@@ -707,6 +821,7 @@ select option {
font-size: 0.9375rem;
margin-bottom: 0.25rem;
overflow-wrap: anywhere;
+ font-family: 'Cinzel', serif;
}
.campaign-template-info p {
@@ -747,12 +862,12 @@ select option {
/* === Animations === */
@keyframes fadeIn {
- from { opacity: 0; transform: translateY(8px); }
+ from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideUp {
- from { opacity: 0; transform: translateY(20px); }
+ from { opacity: 0; transform: translateY(24px); }
to { opacity: 1; transform: translateY(0); }
}
@@ -767,28 +882,28 @@ select option {
}
@keyframes glow-pulse {
- 0%, 100% { box-shadow: 0 0 15px rgba(124, 58, 237, 0.15); }
- 50% { box-shadow: 0 0 25px rgba(124, 58, 237, 0.3); }
+ 0%, 100% { box-shadow: 0 0 15px rgba(139, 92, 246, 0.12); }
+ 50% { box-shadow: 0 0 28px rgba(139, 92, 246, 0.25); }
}
.animate-fade-in {
- animation: fadeIn 0.4s ease-out both;
+ animation: fadeIn 0.5s ease-out both;
}
.animate-slide-up {
- animation: slideUp 0.5s cubic-bezier(0.4, 0, 0.2, 1) both;
+ animation: slideUp 0.6s cubic-bezier(0.4, 0, 0.2, 1) both;
}
/* Stagger children animation */
.stagger-children > * {
- animation: fadeIn 0.4s ease-out both;
+ animation: fadeIn 0.5s ease-out both;
}
-.stagger-children > *:nth-child(1) { animation-delay: 0.05s; }
-.stagger-children > *:nth-child(2) { animation-delay: 0.1s; }
-.stagger-children > *:nth-child(3) { animation-delay: 0.15s; }
-.stagger-children > *:nth-child(4) { animation-delay: 0.2s; }
-.stagger-children > *:nth-child(5) { animation-delay: 0.25s; }
-.stagger-children > *:nth-child(6) { animation-delay: 0.3s; }
+.stagger-children > *:nth-child(1) { animation-delay: 0.06s; }
+.stagger-children > *:nth-child(2) { animation-delay: 0.12s; }
+.stagger-children > *:nth-child(3) { animation-delay: 0.18s; }
+.stagger-children > *:nth-child(4) { animation-delay: 0.24s; }
+.stagger-children > *:nth-child(5) { animation-delay: 0.30s; }
+.stagger-children > *:nth-child(6) { animation-delay: 0.36s; }
/* === Blazor Overrides === */
.valid.modified:not([type=checkbox]) {
@@ -809,7 +924,7 @@ select option {
.blazor-error-boundary {
background: var(--status-danger-bg);
- border: 1px solid rgba(239, 68, 68, 0.2);
+ border: 1px solid rgba(239, 68, 68, 0.15);
padding: 1rem 1.25rem;
color: var(--status-danger);
border-radius: var(--radius-md);
@@ -824,7 +939,9 @@ select option {
.form-label {
color: var(--text-secondary) !important;
font-size: 0.8125rem !important;
- font-weight: 500 !important;
+ font-weight: 600 !important;
+ font-family: 'Jura', sans-serif !important;
+ letter-spacing: 0.03em !important;
}
.form-text {
@@ -850,8 +967,10 @@ select option {
left: -50%;
width: 200%;
height: 200%;
- background: radial-gradient(ellipse at 30% 50%, rgba(124, 58, 237, 0.12) 0%, transparent 50%),
- radial-gradient(ellipse at 70% 50%, rgba(6, 182, 212, 0.08) 0%, transparent 50%);
+ background:
+ radial-gradient(ellipse at 30% 50%, rgba(139, 92, 246, 0.15) 0%, transparent 50%),
+ radial-gradient(ellipse at 70% 50%, rgba(34, 211, 238, 0.1) 0%, transparent 50%),
+ radial-gradient(ellipse at 50% 80%, rgba(139, 92, 246, 0.08) 0%, transparent 40%);
animation: bg-drift 20s ease-in-out infinite alternate;
}
@@ -862,10 +981,10 @@ select option {
.login-card {
position: relative;
- z-index: 1;
+ z-index: 2;
background: var(--glass-bg);
- backdrop-filter: blur(20px);
- -webkit-backdrop-filter: blur(20px);
+ backdrop-filter: blur(24px);
+ -webkit-backdrop-filter: blur(24px);
border: 1px solid var(--glass-border);
border-radius: var(--radius-xl);
padding: 2.5rem;
@@ -873,7 +992,21 @@ select option {
max-width: 400px;
margin: 1rem;
text-align: center;
- animation: slideUp 0.6s cubic-bezier(0.4, 0, 0.2, 1);
+ animation: slideUp 0.7s cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow: 0 8px 40px rgba(0, 0, 0, 0.5), 0 0 30px rgba(139, 92, 246, 0.1);
+}
+
+.login-card::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ border-radius: inherit;
+ padding: 1px;
+ background: linear-gradient(135deg, rgba(139, 92, 246, 0.2) 0%, transparent 35%, transparent 65%, rgba(34, 211, 238, 0.15) 100%);
+ -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
+ -webkit-mask-composite: xor;
+ mask-composite: exclude;
+ pointer-events: none;
}
.login-logo {
@@ -894,12 +1027,15 @@ select option {
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
+ font-family: 'Cinzel Decorative', 'Cinzel', serif;
+ letter-spacing: 0.04em;
}
.login-subtitle {
color: var(--text-muted);
font-size: 0.875rem;
margin-bottom: 2rem;
+ font-family: 'Jura', sans-serif;
}
/* === Telegram Mini App entry === */
@@ -911,6 +1047,8 @@ select option {
justify-content: center;
padding: 1rem;
background: var(--bg-primary);
+ position: relative;
+ z-index: 2;
}
.mini-app-auth-card {
@@ -921,6 +1059,8 @@ select option {
border-radius: var(--radius-md);
background: var(--glass-bg);
text-align: center;
+ position: relative;
+ z-index: 2;
}
.mini-app-logo {
@@ -936,6 +1076,7 @@ select option {
.mini-app-auth-card h1 {
font-size: 1.375rem;
margin-bottom: 0.5rem;
+ font-family: 'Cinzel Decorative', 'Cinzel', serif;
}
.mini-app-auth-card p {
@@ -966,6 +1107,8 @@ body.telegram-mini-app .content {
/* === Mobile Sessions Cards (instead of table) === */
.session-card-mobile {
display: none;
+ position: relative;
+ z-index: 2;
}
body.telegram-mini-app .session-table-desktop {
@@ -984,10 +1127,13 @@ body.telegram-mini-app .session-card-mobile {
padding: 1rem;
margin-bottom: 0.75rem;
transition: all var(--transition-smooth);
+ position: relative;
+ z-index: 2;
}
.session-card:hover {
border-color: var(--border-glow);
+ box-shadow: var(--shadow-glow);
}
.session-card-header {
@@ -1094,6 +1240,8 @@ body.telegram-mini-app .session-card-mobile {
text-align: center;
padding: 2rem;
animation: fadeIn 0.5s ease-out;
+ position: relative;
+ z-index: 2;
}
.error-page-icon {
@@ -1105,6 +1253,7 @@ body.telegram-mini-app .session-card-mobile {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 0.5rem;
+ font-family: 'Cinzel', serif;
}
.error-page-text {
@@ -1128,10 +1277,12 @@ body.telegram-mini-app .session-card-mobile {
/* === Participant list === */
.participant-panel {
- background: rgba(0, 0, 0, 0.2);
+ background: rgba(0, 0, 0, 0.25);
border-radius: 0.75rem;
padding: 0.75rem;
margin: 0.5rem 0;
+ position: relative;
+ z-index: 2;
}
.participant-list {
@@ -1148,6 +1299,11 @@ body.telegram-mini-app .session-card-mobile {
background: rgba(255, 255, 255, 0.03);
border-radius: 0.5rem;
gap: 0.5rem;
+ transition: background var(--transition-fast);
+}
+
+.participant-row:hover {
+ background: rgba(255, 255, 255, 0.05);
}
.participant-info {
@@ -1159,8 +1315,9 @@ body.telegram-mini-app .session-card-mobile {
}
.participant-name {
- font-weight: 500;
+ font-weight: 600;
color: var(--text-primary);
+ font-family: 'Jura', sans-serif;
}
.participant-username {
@@ -1172,9 +1329,293 @@ body.telegram-mini-app .session-card-mobile {
background: transparent;
border: none;
color: var(--text-primary);
- font-weight: 500;
+ font-weight: 600;
cursor: pointer;
font-size: inherit;
- font-family: inherit;
+ font-family: 'Jura', sans-serif;
padding: 0;
+ transition: color var(--transition-fast);
+}
+
+.btn-gm-link:hover {
+ color: var(--accent-secondary);
+}
+
+/* === Sidebar refinements (MainLayout & NavMenu) === */
+.page {
+ display: flex;
+ min-height: 100vh;
+ position: relative;
+ z-index: 2;
+}
+
+.sidebar {
+ width: var(--sidebar-width);
+ background: var(--bg-secondary);
+ border-right: 1px solid var(--border-color);
+ display: flex;
+ flex-direction: column;
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ z-index: 10;
+ overflow-y: auto;
+ overflow-x: hidden;
+}
+
+.main-area {
+ flex: 1;
+ margin-left: var(--sidebar-width);
+ min-height: 100vh;
+ position: relative;
+ z-index: 2;
+}
+
+.content {
+ padding: 1.5rem;
+ min-height: 100vh;
+}
+
+.nav-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 1.25rem 1rem;
+ border-bottom: 1px solid var(--border-color);
+}
+
+.nav-brand {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ text-decoration: none;
+ color: var(--text-primary);
+}
+
+.nav-brand-icon {
+ width: 1.75rem;
+ height: 1.75rem;
+ object-fit: contain;
+}
+
+.nav-brand-text {
+ font-family: 'Cinzel Decorative', 'Cinzel', serif;
+ font-size: 1.125rem;
+ font-weight: 700;
+ letter-spacing: 0.04em;
+ color: var(--text-primary);
+}
+
+.nav-toggle {
+ display: none;
+ background: none;
+ border: none;
+ color: var(--text-secondary);
+ cursor: pointer;
+ padding: 0.5rem;
+ border-radius: var(--radius-sm);
+ transition: all var(--transition-fast);
+}
+
+.nav-toggle:hover {
+ background: var(--bg-surface);
+ color: var(--text-primary);
+}
+
+.nav-body {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ padding: 1rem;
+ gap: 0.5rem;
+}
+
+.nav-section {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+ margin-bottom: auto;
+}
+
+.nav-item {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.625rem 0.875rem;
+ border-radius: var(--radius-sm);
+ color: var(--text-secondary);
+ text-decoration: none;
+ font-size: 0.875rem;
+ font-weight: 600;
+ font-family: 'Jura', sans-serif;
+ letter-spacing: 0.02em;
+ transition: all var(--transition-fast);
+ border: 1px solid transparent;
+}
+
+.nav-item:hover {
+ background: var(--bg-surface);
+ color: var(--text-primary);
+ border-color: var(--border-color);
+}
+
+.nav-item.active {
+ background: linear-gradient(135deg, rgba(139, 92, 246, 0.15) 0%, rgba(34, 211, 238, 0.08) 100%);
+ color: var(--text-accent);
+ border-color: rgba(139, 92, 246, 0.25);
+ box-shadow: 0 0 12px rgba(139, 92, 246, 0.1);
+}
+
+.nav-item.active .nav-icon {
+ color: var(--accent-primary);
+}
+
+.nav-icon {
+ width: 1.125rem;
+ height: 1.125rem;
+ flex-shrink: 0;
+ color: var(--text-muted);
+ transition: color var(--transition-fast);
+}
+
+.nav-item:hover .nav-icon {
+ color: var(--text-primary);
+}
+
+.nav-footer {
+ margin-top: auto;
+ padding-top: 1rem;
+ border-top: 1px solid var(--border-color);
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.nav-user {
+ display: flex;
+ align-items: center;
+ gap: 0.625rem;
+ padding: 0.5rem 0.75rem;
+}
+
+.nav-user-avatar {
+ width: 2rem;
+ height: 2rem;
+ border-radius: 50%;
+ background: var(--accent-gradient);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.875rem;
+ font-weight: 700;
+ color: white;
+ flex-shrink: 0;
+ font-family: 'Jura', sans-serif;
+}
+
+.nav-user-name {
+ font-size: 0.8125rem;
+ font-weight: 600;
+ color: var(--text-secondary);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ font-family: 'Jura', sans-serif;
+}
+
+.nav-logout-btn {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.5rem 0.75rem;
+ border-radius: var(--radius-sm);
+ color: var(--text-muted);
+ background: transparent;
+ border: 1px solid transparent;
+ cursor: pointer;
+ font-size: 0.8125rem;
+ font-weight: 600;
+ font-family: 'Jura', sans-serif;
+ letter-spacing: 0.02em;
+ transition: all var(--transition-fast);
+ width: 100%;
+}
+
+.nav-logout-btn:hover {
+ background: rgba(239, 68, 68, 0.08);
+ color: var(--status-danger);
+ border-color: rgba(239, 68, 68, 0.15);
+}
+
+.nav-version {
+ font-size: 0.6875rem;
+ color: var(--text-muted);
+ text-align: center;
+ font-family: 'Jura', sans-serif;
+ letter-spacing: 0.05em;
+ opacity: 0.6;
+}
+
+/* === Blazor error UI === */
+#blazor-error-ui {
+ background: var(--status-danger-bg);
+ color: var(--status-danger);
+ bottom: 0;
+ box-shadow: 0 -1px 2px rgba(0,0,0,0.2);
+ display: none;
+ left: 0;
+ padding: 0.6rem 1.25rem;
+ position: fixed;
+ width: 100%;
+ z-index: 1000;
+ font-family: 'Jura', sans-serif;
+ font-size: 0.875rem;
+ font-weight: 500;
+}
+
+#blazor-error-ui .dismiss {
+ cursor: pointer;
+ position: absolute;
+ right: 0.75rem;
+ top: 0.5rem;
+}
+
+#blazor-error-ui .reload {
+ color: var(--text-accent);
+ margin-left: 0.5rem;
+ text-decoration: underline;
+}
+
+/* === Mobile responsive sidebar === */
+@media (max-width: 768px) {
+ .sidebar {
+ width: 100%;
+ position: fixed;
+ z-index: 100;
+ transform: translateX(-100%);
+ transition: transform var(--transition-normal);
+ }
+
+ .sidebar.open {
+ transform: translateX(0);
+ }
+
+ .main-area {
+ margin-left: 0;
+ }
+
+ .nav-toggle {
+ display: block;
+ position: fixed;
+ top: 1rem;
+ left: 1rem;
+ z-index: 101;
+ background: var(--bg-secondary);
+ border: 1px solid var(--border-color);
+ }
+
+ .nav-body.open {
+ display: flex;
+ }
}