Fix: Update all components to match Synthwave design showcase

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
2026-11-05 01:53:51 +01:00
co-authored by Junie
parent 587ebd86ba
commit 0d16342f9f
9 changed files with 417 additions and 235 deletions
@@ -11,46 +11,46 @@ import { Router, ActivatedRoute } from '@angular/router';
providers: [MailService],
template: `
<div class="glass-panel p-6">
<h2 class="text-lg font-orbitron font-bold text-synth-cyan mb-6 flex items-center gap-2">
<span class="text-synth-pink">✉️</span> COMPOSE MESSAGE
<h2 class="text-lg font-orbitron font-bold text-cyan-500 mb-6 flex items-center gap-2">
<span class="text-pink-500">✉️</span> COMPOSE MESSAGE
</h2>
@if (success) {
<div class="mb-4 p-3 rounded-lg bg-synth-cyan/20 border border-synth-cyan/40 text-synth-cyan text-sm font-mono">
<div class="mb-4 p-3 rounded-lg bg-cyan-500/20 border border-cyan-500/40 text-cyan-500 text-sm font-mono">
Message sent successfully!
</div>
}
<form (ngSubmit)="onSend()" class="space-y-4">
<div>
<label for="to" class="block text-xs font-mono text-synth-cyan mb-1">TO</label>
<label for="to" class="block text-xs font-mono text-cyan-500 mb-1">TO</label>
<input id="to" type="email" [(ngModel)]="to" name="to" required
class="w-full bg-synth-darker/80 border border-synth-cyan/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-synth-pink focus:border-synth-cyan transition-all"
class="w-full bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-500 transition-all"
placeholder="recipient@example.com">
</div>
<div>
<label for="subject" class="block text-xs font-mono text-synth-cyan mb-1">SUBJECT</label>
<label for="subject" class="block text-xs font-mono text-cyan-500 mb-1">SUBJECT</label>
<input id="subject" type="text" [(ngModel)]="subject" name="subject" required
class="w-full bg-synth-darker/80 border border-synth-cyan/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-synth-pink focus:border-synth-cyan transition-all"
class="w-full bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-500 transition-all"
placeholder="Enter subject...">
</div>
<div>
<div class="flex items-center justify-between mb-1">
<label for="body" class="text-xs font-mono text-synth-cyan">BODY</label>
<label for="body" class="text-xs font-mono text-cyan-500">BODY</label>
<label class="flex items-center gap-2 cursor-pointer">
<span class="text-xs font-mono text-slate-400">HTML</span>
<input type="checkbox" [(ngModel)]="isHtml" name="isHtml" class="sr-only">
<span class="w-10 h-5 rounded-full bg-synth-dark/50 border border-synth-cyan/30 relative transition-all"
[class.bg-synth-cyan/30]="isHtml" [class.border-synth-cyan]="isHtml">
<span class="absolute top-0.5 left-0.5 w-3.5 h-3.5 rounded-full bg-synth-cyan transition-all"
<span class="w-10 h-5 rounded-full bg-slate-900/50 border border-cyan-500/30 relative transition-all"
[class.bg-cyan-500/30]="isHtml" [class.border-cyan-500]="isHtml">
<span class="absolute top-0.5 left-0.5 w-3.5 h-3.5 rounded-full bg-cyan-500 transition-all"
[class.translate-x-5]="isHtml"></span>
</span>
</label>
</div>
<textarea id="body" [(ngModel)]="body" name="body" rows="10" required
class="w-full bg-synth-darker/80 border border-synth-cyan/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-synth-pink focus:border-synth-cyan transition-all resize-none"
class="w-full bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-500 transition-all resize-none"
[placeholder]="isHtml ? 'Write HTML or Markdown...' : 'Write your message...'"></textarea>
@if (isHtml) {
<p class="text-xs text-slate-500 font-mono mt-1">💡 Supports Markdown: **bold**, *italic*, - lists, \`\`\`code\`\`\`</p>
@@ -1,48 +1,48 @@
<div class="space-y-4">
<h2 class="text-lg font-orbitron font-bold text-synth-cyan flex items-center gap-2">
<span class="text-synth-pink">👥</span> Contacts
<h2 class="text-lg font-orbitron font-bold text-cyan-300 flex items-center gap-2">
<span class="text-pink-500">👥</span> CONTACTS
<span class="text-sm font-mono text-slate-400 ml-auto">{{ contacts().length }} contacts</span>
</h2>
<!-- Форма добавления -->
<div class="glass-panel p-4">
<h3 class="text-sm font-orbitron font-bold text-synth-yellow mb-3">
{{ editingContact() ? 'Edit Contact' : 'Add New Contact' }}
<div class="glass-panel p-5">
<h3 class="text-sm font-orbitron font-bold text-yellow-400 mb-3 flex items-center gap-2">
<span class="text-pink-500">✏️</span> {{ editingContact() ? 'EDIT CONTACT' : 'ADD NEW CONTACT' }}
</h3>
<form (ngSubmit)="saveContact()" class="grid grid-cols-1 md:grid-cols-2 gap-3">
<div>
<label class="block text-xs font-mono text-slate-400 mb-1">Name</label>
<label class="block text-xs font-mono text-pink-400 mb-1">NAME</label>
<input type="text" [(ngModel)]="formName" name="name"
class="w-full bg-synth-dark/50 border border-synth-cyan/20 rounded-lg px-3 py-2 text-sm font-mono text-white focus:border-synth-cyan focus:outline-none focus:ring-1 focus:ring-synth-cyan/50"
class="w-full bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-2.5 text-sm font-mono text-slate-200 focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-400 transition-all"
placeholder="John Doe">
</div>
<div>
<label class="block text-xs font-mono text-slate-400 mb-1">Email *</label>
<label class="block text-xs font-mono text-pink-400 mb-1">EMAIL *</label>
<input type="email" [(ngModel)]="formEmail" name="email" required
class="w-full bg-synth-dark/50 border border-synth-cyan/20 rounded-lg px-3 py-2 text-sm font-mono text-white focus:border-synth-cyan focus:outline-none focus:ring-1 focus:ring-synth-cyan/50"
class="w-full bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-2.5 text-sm font-mono text-slate-200 focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-400 transition-all"
placeholder="john@example.com">
</div>
<div>
<label class="block text-xs font-mono text-slate-400 mb-1">Phone</label>
<label class="block text-xs font-mono text-pink-400 mb-1">PHONE</label>
<input type="text" [(ngModel)]="formPhone" name="phone"
class="w-full bg-synth-dark/50 border border-synth-cyan/20 rounded-lg px-3 py-2 text-sm font-mono text-white focus:border-synth-cyan focus:outline-none focus:ring-1 focus:ring-synth-cyan/50"
class="w-full bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-2.5 text-sm font-mono text-slate-200 focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-400 transition-all"
placeholder="+1 234 567 8900">
</div>
<div>
<label class="block text-xs font-mono text-slate-400 mb-1">Company</label>
<label class="block text-xs font-mono text-pink-400 mb-1">COMPANY</label>
<input type="text" [(ngModel)]="formCompany" name="company"
class="w-full bg-synth-dark/50 border border-synth-cyan/20 rounded-lg px-3 py-2 text-sm font-mono text-white focus:border-synth-cyan focus:outline-none focus:ring-1 focus:ring-synth-cyan/50"
class="w-full bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-2.5 text-sm font-mono text-slate-200 focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-400 transition-all"
placeholder="Acme Corp">
</div>
<div class="md:col-span-2 flex gap-2">
<button type="submit"
class="px-4 py-2 bg-synth-pink/20 border border-synth-pink/40 rounded-lg text-sm font-mono text-synth-pink hover:bg-synth-pink/30 transition-all">
{{ editingContact() ? '💾 Update' : '➕ Add' }}
class="cyber-btn px-6 py-2 rounded-xl text-xs font-bold">
{{ editingContact() ? '💾 UPDATE' : '➕ ADD' }}
</button>
@if (editingContact()) {
<button type="button" (click)="cancelEdit()"
class="px-4 py-2 bg-slate-700/50 border border-slate-600/40 rounded-lg text-sm font-mono text-slate-300 hover:bg-slate-600/50 transition-all">
✖ Cancel
class="px-4 py-2 rounded-xl text-xs font-mono text-slate-400 border border-slate-600/40 hover:text-slate-200 hover:border-slate-400 transition-all">
✖ CANCEL
</button>
}
</div>
@@ -50,7 +50,7 @@
</div>
<!-- Список контактов -->
<div class="glass-panel p-4">
<div class="glass-panel p-5">
@if (contacts().length === 0) {
<div class="text-center py-12 text-slate-400 font-mono">
<p class="text-4xl mb-4">📇</p>
@@ -59,28 +59,34 @@
} @else {
<div class="space-y-2">
@for (contact of contacts(); track contact.id) {
<div class="flex items-center gap-3 p-3 rounded-xl border border-synth-cyan/10 hover:border-synth-cyan/40 hover:bg-synth-cyan/5 transition-all">
<div class="w-10 h-10 rounded-full bg-synth-purple/30 flex items-center justify-center text-synth-cyan font-bold text-lg">
<div class="flex items-center gap-3 p-3 rounded-xl border border-cyan-500/10 hover:border-cyan-500/40 hover:bg-cyan-500/5 transition-all">
<div class="w-10 h-10 rounded-full bg-purple-500/30 flex items-center justify-center text-cyan-400 font-bold text-lg">
{{ (contact.name || contact.email).charAt(0).toUpperCase() }}
</div>
<div class="flex-1 min-w-0">
<div class="font-mono text-white truncate">{{ contact.name || '(No name)' }}</div>
<div class="font-mono text-slate-200 truncate">{{ contact.name || '(No name)' }}</div>
<div class="text-xs text-slate-400 font-mono truncate">{{ contact.email }}</div>
@if (contact.phone || contact.company) {
<div class="text-xs text-slate-500 font-mono">
<span *ngIf="contact.company">{{ contact.company }}</span>
<span *ngIf="contact.company && contact.phone"> • </span>
<span *ngIf="contact.phone">{{ contact.phone }}</span>
@if (contact.company) {
{{ contact.company }}
}
@if (contact.company && contact.phone) {
&nbsp;•&nbsp;
}
@if (contact.phone) {
{{ contact.phone }}
}
</div>
}
</div>
<div class="flex gap-1">
<button (click)="editContact(contact)"
class="px-2 py-1 text-xs font-mono text-synth-cyan hover:bg-synth-cyan/10 rounded transition-all">
class="px-2 py-1 text-xs font-mono text-cyan-400 hover:bg-cyan-500/10 rounded-lg transition-all">
✏️
</button>
<button (click)="deleteContact(contact.id!)"
class="px-2 py-1 text-xs font-mono text-synth-pink hover:bg-synth-pink/10 rounded transition-all">
class="px-2 py-1 text-xs font-mono text-pink-400 hover:bg-pink-500/10 rounded-lg transition-all">
🗑️
</button>
</div>
@@ -1,13 +1,12 @@
import { Component, inject, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { MailService } from '../../services/mail.service';
import { Contact } from '../../models/mail.models';
@Component({
selector: 'app-contacts',
standalone: true,
imports: [FormsModule, CommonModule],
imports: [FormsModule],
providers: [MailService],
templateUrl: './contacts.component.html',
styles: ``
@@ -18,7 +18,7 @@ import { of } from 'rxjs';
<div class="glass-panel p-4">
<div class="flex gap-3">
<input [(ngModel)]="searchQuery" (keyup.enter)="loadMessages()"
class="flex-1 bg-synth-darker/80 border border-synth-cyan/40 rounded-xl px-4 py-2 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-synth-pink focus:border-synth-cyan transition-all"
class="flex-1 bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-2 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-400 transition-all"
placeholder="🔍 Search messages...">
<button (click)="loadMessages()" class="cyber-btn px-6 py-2 rounded-xl text-xs font-bold">
SEARCH
@@ -28,8 +28,8 @@ import { of } from 'rxjs';
<!-- Список писем -->
<div class="glass-panel p-4">
<h2 class="text-lg font-orbitron font-bold text-synth-cyan mb-4 flex items-center gap-2">
<span class="text-synth-pink">📬</span> {{ folderName() }}
<h2 class="text-lg font-orbitron font-bold text-cyan-300 mb-4 flex items-center gap-2">
<span class="text-pink-500">📬</span> {{ folderName() }}
<span class="text-sm font-mono text-slate-400 ml-auto">{{ messages()?.length || 0 }} messages</span>
</h2>
@@ -42,12 +42,12 @@ import { of } from 'rxjs';
<div class="space-y-2">
@for (msg of messages() || []; track msg.id) {
<a routerLink="/message/{{ msg.id }}"
class="block p-4 rounded-xl border border-synth-cyan/10 hover:border-synth-cyan/40 hover:bg-synth-cyan/5 transition-all cursor-pointer group">
class="block p-4 rounded-xl border border-cyan-500/10 hover:border-cyan-500/40 hover:bg-cyan-500/5 transition-all cursor-pointer group">
<div class="flex items-start gap-3">
<!-- Индикатор прочитанности -->
<div class="mt-1">
@if (!msg.read) {
<span class="w-3 h-3 rounded-full bg-synth-pink inline-block shadow-[0_0_8px_rgba(255,0,127,0.8)]"></span>
<span class="w-3 h-3 rounded-full bg-pink-500 inline-block shadow-[0_0_8px_rgba(255,0,127,0.8)]"></span>
} @else {
<span class="w-3 h-3 rounded-full border border-slate-500 inline-block"></span>
}
@@ -59,15 +59,15 @@ import { of } from 'rxjs';
{{ msg.senderName || msg.sender }}
</span>
@if (msg.spam) {
<span class="px-2 py-0.5 text-xs rounded-full bg-synth-yellow/20 text-synth-yellow font-mono">
<span class="px-2 py-0.5 text-xs rounded-full bg-yellow-500/20 text-yellow-400 font-mono">
SPAM
</span>
}
@if (msg.flagged) {
<span class="text-synth-yellow text-xs">★</span>
<span class="text-yellow-400 text-xs">★</span>
}
</div>
<div class="text-sm text-synth-cyan font-rajdhani truncate group-hover:text-synth-pink transition-colors">
<div class="text-sm text-cyan-400 font-rajdhani truncate group-hover:text-pink-400 transition-colors">
{{ msg.subject || '(No subject)' }}
</div>
<div class="text-xs text-slate-400 font-mono truncate">
@@ -80,7 +80,7 @@ import { of } from 'rxjs';
{{ msg.sentAt | date:'MMM d, HH:mm' }}
</div>
@if (msg.hasAttachments) {
<span class="text-xs text-synth-purple font-mono">📎 {{ msg.attachmentCount }}</span>
<span class="text-xs text-purple-400 font-mono">📎 {{ msg.attachmentCount }}</span>
}
</div>
</div>
@@ -94,19 +94,19 @@ import { of } from 'rxjs';
@if (totalPages() > 1) {
<div class="glass-panel p-4 flex items-center justify-center gap-2">
<button (click)="goToPage(page() - 1)" [disabled]="page() === 0"
class="px-3 py-1 rounded-lg text-xs font-mono text-synth-cyan border border-synth-cyan/30 hover:bg-synth-cyan/10 disabled:opacity-30 disabled:cursor-not-allowed transition-all">
class="px-3 py-1 rounded-lg text-xs font-mono text-cyan-400 border border-cyan-500/30 hover:bg-cyan-500/10 disabled:opacity-30 disabled:cursor-not-allowed transition-all">
← Prev
</button>
@for (p of pageNumbers; track p) {
<button (click)="goToPage(p)"
[class.bg-synth-pink/20]="p === page()" [class.text-synth-pink]="p === page()"
[class.border-synth-pink]="p === page()"
class="px-3 py-1 rounded-lg text-xs font-mono text-slate-400 border border-synth-cyan/20 hover:bg-synth-cyan/10 hover:text-synth-cyan transition-all">
[class.bg-pink-500/20]="p === page()" [class.text-pink-400]="p === page()"
[class.border-pink-500]="p === page()"
class="px-3 py-1 rounded-lg text-xs font-mono text-slate-400 border border-cyan-500/20 hover:bg-cyan-500/10 hover:text-cyan-300 transition-all">
{{ p + 1 }}
</button>
}
<button (click)="goToPage(page() + 1)" [disabled]="page() >= totalPages() - 1"
class="px-3 py-1 rounded-lg text-xs font-mono text-synth-cyan border border-synth-cyan/30 hover:bg-synth-cyan/10 disabled:opacity-30 disabled:cursor-not-allowed transition-all">
class="px-3 py-1 rounded-lg text-xs font-mono text-cyan-400 border border-cyan-500/30 hover:bg-cyan-500/10 disabled:opacity-30 disabled:cursor-not-allowed transition-all">
Next →
</button>
<span class="text-xs font-mono text-slate-500 ml-2">
@@ -1,49 +1,93 @@
import { Component, inject, signal } from '@angular/core';
import { Component, inject, signal, effect } from '@angular/core';
import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
import { AuthService } from '../../services/auth.service';
import { MailService } from '../../services/mail.service';
import { Folder } from '../../models/mail.models';
const THEMES = [
{ value: 'theme-cyber-magenta', label: '💖 Cyber Magenta' },
{ value: 'theme-neon-cyan', label: '🩵 Neon Cyan' },
{ value: 'theme-sunset-orange', label: '🧡 Sunset Orange' },
{ value: 'theme-matrix-green', label: '💚 Matrix Green' },
];
@Component({
selector: 'app-layout',
standalone: true,
imports: [RouterOutlet, RouterLink, RouterLinkActive],
template: `
<div class="min-h-screen relative" [class.light-theme]="isLightTheme()">
<!-- Synthwave фон -->
<div class="synth-bg" aria-hidden="true">
<div #root class="min-h-screen relative theme-cyber-magenta" [class.light-theme]="isLightTheme()" [class.high-contrast]="isHighContrast()">
<!-- Synthwave анимированный фон -->
<div class="synth-stage" aria-hidden="true">
<div class="synth-sun"></div>
<div class="synth-grid">
<div class="synth-grid-plane"></div>
<div class="synth-mountains"></div>
<div class="grid-container">
<div class="grid-plane"></div>
</div>
</div>
<!-- Контент -->
<div class="relative z-10 flex flex-col min-h-screen">
<!-- Шапка -->
<header class="glass-panel rounded-none border-t-0 border-x-0 p-4 sticky top-0 z-40 backdrop-blur-xl">
<div class="max-w-7xl mx-auto flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-synth-pink to-synth-cyan flex items-center justify-center text-synth-dark font-black text-xl font-orbitron shadow-[0_0_15px_rgba(255,0,127,0.8)]">
<header class="p-4 lg:p-6 border-b border-cyan-500/20 glass-panel rounded-none border-t-0 border-x-0 sticky top-0 z-40 backdrop-blur-xl">
<div class="max-w-7xl mx-auto flex flex-col md:flex-row items-center justify-between gap-4">
<!-- Логотип -->
<div class="flex items-center space-x-3">
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-pink-500 to-cyan-400 flex items-center justify-center text-slate-950 font-black text-xl shadow-[0_0_15px_rgba(255,0,127,0.8)] font-orbitron">
M
</div>
<div>
<h1 class="text-2xl font-orbitron font-extrabold tracking-wider neon-text-primary">
MAIL<span class="text-synth-cyan font-normal">SERVER</span>
<h1 class="text-2xl font-extrabold font-orbitron tracking-wider neon-text-primary">
MAIL<span class="text-cyan-400 font-normal">SERVER</span>
</h1>
<p class="text-xs text-synth-cyan/70 font-mono tracking-widest uppercase">Synthwave Edition</p>
<p class="text-xs text-cyan-300/70 font-mono tracking-widest uppercase">Synthwave Edition</p>
</div>
</div>
<div class="flex items-center gap-4">
<!-- Переключатель темы -->
<button (click)="toggleTheme()" class="cyber-btn px-3 py-2 rounded-lg text-xs font-bold" [title]="isLightTheme() ? 'Переключить на тёмную тему' : 'Переключить на светлую тему'">
<!-- Статистика -->
<div class="flex items-center space-x-4 bg-slate-950/60 px-4 py-2 rounded-full border border-pink-500/30 text-xs font-mono">
<div class="flex items-center space-x-2">
<span class="w-2 h-2 rounded-full bg-emerald-400 animate-ping"></span>
<span class="text-slate-300">SMTP: <span class="text-emerald-400 font-bold">ONLINE</span></span>
</div>
<span class="text-slate-600">|</span>
<div class="text-slate-300">User: <span class="text-cyan-400 font-bold">{{ email }}</span></div>
<span class="text-slate-600">|</span>
<div class="text-slate-300">Folders: <span class="text-pink-400 font-bold">{{ folders().length }}</span></div>
</div>
<!-- Контроли -->
<div class="flex items-center space-x-3">
<!-- Выбор цветовой темы -->
<div class="relative">
<select #themeSelect (change)="onThemeChange(themeSelect.value)"
class="bg-slate-900/90 text-cyan-300 border border-cyan-500/40 rounded-lg px-3 py-1.5 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-pink-500 cursor-pointer">
@for (theme of themes; track theme.value) {
<option [value]="theme.value" [selected]="currentTheme() === theme.value">
{{ theme.label }}
</option>
}
</select>
</div>
<!-- High Contrast -->
<button (click)="toggleHighContrast()"
[attr.aria-pressed]="isHighContrast()"
title="Toggle High Contrast Mode"
class="px-3 py-1.5 rounded-lg border border-cyan-500/40 bg-slate-900/80 text-xs font-mono hover:bg-cyan-500/20 focus:outline-none focus:ring-2 focus:ring-pink-500 transition-all flex items-center gap-1.5">
<span class="text-yellow-400">◐</span>
<span>Contrast</span>
</button>
<!-- Light/Dark -->
<button (click)="toggleTheme()" class="cyber-btn px-3 py-1.5 rounded-lg text-xs font-bold">
{{ isLightTheme() ? '🌙 Dark' : '☀️ Light' }}
</button>
<span class="text-sm font-mono text-slate-400">{{ email }}</span>
<button (click)="onLogout()" class="cyber-btn px-4 py-2 rounded-lg text-xs font-bold">
<i class="mr-1">⏻</i> Logout
<!-- Logout -->
<button (click)="onLogout()" class="cyber-btn px-4 py-1.5 rounded-lg text-xs font-bold">
⏻ Logout
</button>
</div>
</div>
@@ -51,21 +95,21 @@ import { Folder } from '../../models/mail.models';
<!-- Основная область -->
<div class="flex flex-1 max-w-7xl mx-auto w-full p-4 gap-4">
<!-- Боковая панель с папками -->
<!-- Боковая панель -->
<aside class="w-64 flex-shrink-0">
<div class="glass-panel p-4">
<h2 class="text-sm font-orbitron font-bold text-synth-cyan mb-3 flex items-center gap-2">
<span class="text-synth-pink">📁</span> FOLDERS
<h2 class="text-sm font-orbitron font-bold text-cyan-300 mb-3 flex items-center gap-2">
<span class="text-pink-500">📁</span> FOLDERS
</h2>
<nav class="space-y-1">
@for (folder of folders(); track folder.id) {
<a routerLink="/inbox" [queryParams]="{folderId: folder.id}"
routerLinkActive="bg-synth-pink/20 text-synth-pink border-synth-pink"
[queryParamsHandling]=" 'merge'"
class="block px-3 py-2 rounded-lg text-sm font-rajdhani text-slate-300 hover:bg-synth-cyan/10 hover:text-synth-cyan border border-transparent transition-all">
routerLinkActive="bg-pink-500/20 text-pink-400 border-pink-500"
[queryParamsHandling]="'merge'"
class="block px-3 py-2 rounded-lg text-sm font-rajdhani text-slate-300 hover:bg-cyan-500/10 hover:text-cyan-300 border border-transparent transition-all">
{{ folder.name }}
@if (folder.unreadCount > 0) {
<span class="ml-2 px-2 py-0.5 text-xs rounded-full bg-synth-pink/30 text-synth-pink font-mono">
<span class="ml-2 px-2 py-0.5 text-xs rounded-full bg-pink-500/30 text-pink-400 font-mono">
{{ folder.unreadCount }}
</span>
}
@@ -77,8 +121,8 @@ import { Folder } from '../../models/mail.models';
<!-- Контакты -->
<div class="glass-panel p-4 mt-4">
<a routerLink="/contacts"
routerLinkActive="bg-synth-cyan/20 text-synth-cyan border-synth-cyan"
class="block px-3 py-2 rounded-lg text-sm font-rajdhani text-slate-300 hover:bg-synth-cyan/10 hover:text-synth-cyan border border-transparent transition-all">
routerLinkActive="bg-cyan-500/20 text-cyan-300 border-cyan-500"
class="block px-3 py-2 rounded-lg text-sm font-rajdhani text-slate-300 hover:bg-cyan-500/10 hover:text-cyan-300 border border-transparent transition-all">
👥 Contacts
</a>
</div>
@@ -96,6 +140,17 @@ import { Folder } from '../../models/mail.models';
<router-outlet />
</main>
</div>
<!-- Футер -->
<footer class="p-4 border-t border-cyan-500/20 glass-panel rounded-none border-b-0 border-x-0 mt-6 text-center text-xs font-mono text-slate-400">
<div class="max-w-7xl mx-auto flex flex-col sm:flex-row items-center justify-between gap-2">
<div>MAILSERVER SYNTHWAVE &copy; 2026</div>
<div class="flex items-center space-x-4">
<span class="text-cyan-400">◉ A11Y READY</span>
<span class="text-pink-400">⚡ HIGH PERF CSS3D</span>
</div>
</div>
</footer>
</div>
</div>
`,
@@ -108,15 +163,38 @@ export default class LayoutComponent {
email = this.authService.getEmail() || '';
folders = signal<Folder[]>([]);
isLightTheme = signal(false);
isHighContrast = signal(false);
currentTheme = signal('theme-cyber-magenta');
themes = THEMES;
constructor() {
this.loadFolders();
// Загружаем сохранённую тему
const saved = localStorage.getItem('theme');
if (saved === 'light') {
// Загружаем сохранённые настройки
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'light') {
this.isLightTheme.set(true);
document.documentElement.classList.add('light-theme');
}
const savedPalette = localStorage.getItem('palette');
if (savedPalette) {
this.currentTheme.set(savedPalette);
}
const savedContrast = localStorage.getItem('highContrast');
if (savedContrast === 'true') {
this.isHighContrast.set(true);
}
// Применяем тему при изменении
effect(() => {
const root = document.documentElement;
// Удаляем все theme классы
THEMES.forEach(t => root.classList.remove(t.value));
root.classList.add(this.currentTheme());
});
}
onThemeChange(theme: string): void {
this.currentTheme.set(theme);
localStorage.setItem('palette', theme);
}
toggleTheme(): void {
@@ -131,6 +209,18 @@ export default class LayoutComponent {
}
}
toggleHighContrast(): void {
const hc = !this.isHighContrast();
this.isHighContrast.set(hc);
if (hc) {
document.documentElement.classList.add('high-contrast');
localStorage.setItem('highContrast', 'true');
} else {
document.documentElement.classList.remove('high-contrast');
localStorage.setItem('highContrast', 'false');
}
}
loadFolders(): void {
this.mailService.getFolders().subscribe({
next: (folders) => this.folders.set(folders),
@@ -9,58 +9,59 @@ import { Router } from '@angular/router';
standalone: true,
imports: [RouterLink, FormsModule],
template: `
<div class="min-h-screen relative flex items-center justify-center">
<!-- Synthwave фон -->
<div class="synth-bg" aria-hidden="true">
<div class="min-h-screen relative flex items-center justify-center theme-cyber-magenta">
<!-- Synthwave анимированный фон -->
<div class="synth-stage" aria-hidden="true">
<div class="synth-sun"></div>
<div class="synth-grid">
<div class="synth-grid-plane"></div>
<div class="synth-mountains"></div>
<div class="grid-container">
<div class="grid-plane"></div>
</div>
</div>
<!-- Форма логина -->
<div class="relative z-10 w-full max-w-md px-4">
<div class="glass-panel p-8">
<div class="glass-panel p-8 border-2 border-cyan-400 shadow-[0_0_50px_rgba(0,240,255,0.4)]">
<div class="text-center mb-8">
<div class="w-16 h-16 mx-auto rounded-xl bg-gradient-to-br from-synth-pink to-synth-cyan flex items-center justify-center text-synth-dark font-black text-2xl font-orbitron shadow-[0_0_20px_rgba(255,0,127,0.8)] mb-4">
<div class="w-16 h-16 mx-auto rounded-xl bg-gradient-to-br from-pink-500 to-cyan-400 flex items-center justify-center text-slate-950 font-black text-2xl font-orbitron shadow-[0_0_20px_rgba(255,0,127,0.8)] mb-4">
M
</div>
<h1 class="text-3xl font-orbitron font-extrabold tracking-wider neon-text-primary mb-2">
MAIL<span class="text-synth-cyan font-normal">SERVER</span>
<h1 class="text-3xl font-extrabold font-orbitron tracking-wider neon-text-primary mb-2">
MAIL<span class="text-cyan-400 font-normal">SERVER</span>
</h1>
<p class="text-sm text-slate-400 font-mono">Sign in to your mailbox</p>
<p class="text-sm text-cyan-300/70 font-mono tracking-widest uppercase">Synthwave Edition</p>
</div>
@if (error) {
<div class="mb-4 p-3 rounded-lg bg-synth-pink/20 border border-synth-pink/40 text-synth-pink text-sm font-mono">
<div class="mb-4 p-3 rounded-lg bg-pink-500/20 border border-pink-500/40 text-pink-400 text-sm font-mono">
{{ error }}
</div>
}
<form (ngSubmit)="onLogin()" class="space-y-4">
<div>
<label for="email" class="block text-xs font-mono text-synth-cyan mb-1">EMAIL</label>
<label for="email" class="block text-xs font-mono text-pink-400 mb-1">EMAIL</label>
<input id="email" type="email" [(ngModel)]="email" name="email" required
class="w-full bg-synth-darker/80 border border-synth-cyan/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-synth-pink focus:border-synth-cyan transition-all"
class="w-full bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-400 transition-all"
placeholder="user@example.com">
</div>
<div>
<label for="password" class="block text-xs font-mono text-synth-cyan mb-1">PASSWORD</label>
<label for="password" class="block text-xs font-mono text-pink-400 mb-1">PASSWORD</label>
<input id="password" type="password" [(ngModel)]="password" name="password" required
class="w-full bg-synth-darker/80 border border-synth-cyan/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-synth-pink focus:border-synth-cyan transition-all"
class="w-full bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-400 transition-all"
placeholder="••••••••">
</div>
<button type="submit" [disabled]="loading"
class="cyber-btn w-full py-3 rounded-xl text-sm font-bold mt-6">
{{ loading ? 'CONNECTING...' : 'LOGIN' }}
{{ loading ? '⏳ CONNECTING...' : '⚡ LOGIN' }}
</button>
</form>
<p class="text-center text-sm text-slate-400 mt-6">
<p class="text-center text-sm text-slate-400 mt-6 font-rajdhani">
No account?
<a routerLink="/register" class="text-synth-cyan hover:text-synth-pink font-mono transition-colors">
<a routerLink="/register" class="text-cyan-400 hover:text-pink-400 font-mono transition-colors">
Register here
</a>
</p>
@@ -13,24 +13,24 @@ import { Message } from '../../models/mail.models';
@if (message) {
<div class="glass-panel p-6">
<!-- Заголовок письма -->
<div class="mb-6 pb-4 border-b border-synth-cyan/20">
<div class="mb-6 pb-4 border-b border-cyan-500/20">
<h1 class="text-xl font-orbitron font-bold neon-text-secondary mb-3">
{{ message.subject || '(No subject)' }}
</h1>
<div class="flex items-center gap-4 text-sm font-mono">
<div class="flex items-center gap-2">
<span class="text-synth-cyan">FROM:</span>
<span class="text-cyan-500">FROM:</span>
<span class="text-slate-200">{{ message.senderName || message.sender }}</span>
</div>
<div class="flex items-center gap-2">
<span class="text-synth-cyan">DATE:</span>
<span class="text-cyan-500">DATE:</span>
<span class="text-slate-200">{{ message.sentAt | date:'MMM d, y, HH:mm' }}</span>
</div>
</div>
@if (message.spam) {
<div class="mt-3 px-3 py-1.5 inline-block rounded-full bg-synth-yellow/20 border border-synth-yellow/40 text-synth-yellow text-xs font-mono">
<div class="mt-3 px-3 py-1.5 inline-block rounded-full bg-yellow-500/20 border border-yellow-500/40 text-yellow-500 text-xs font-mono">
⚠️ MARKED AS SPAM
</div>
}
@@ -45,8 +45,8 @@ import { Message } from '../../models/mail.models';
<!-- Вложения -->
@if (message.hasAttachments) {
<div class="mb-6 p-4 rounded-xl bg-synth-darker/60 border border-synth-purple/30">
<h3 class="text-sm font-orbitron font-bold text-synth-purple mb-3">
<div class="mb-6 p-4 rounded-xl bg-slate-950/60 border border-purple-500/30">
<h3 class="text-sm font-orbitron font-bold text-purple-500 mb-3">
📎 ATTACHMENTS ({{ message.attachmentCount }})
</h3>
<p class="text-xs text-slate-400 font-mono">
@@ -56,19 +56,19 @@ import { Message } from '../../models/mail.models';
}
<!-- Действия -->
<div class="flex gap-3 pt-4 border-t border-synth-cyan/20">
<div class="flex gap-3 pt-4 border-t border-cyan-500/20">
<a routerLink="/inbox" class="cyber-btn px-6 py-2 rounded-xl text-xs font-bold">
← BACK TO INBOX
</a>
<a [routerLink]="['/compose']" [queryParams]="{replyTo: message.sender, subject: 'Re: ' + (message.subject || '')}"
class="px-6 py-2 rounded-xl text-xs font-mono text-synth-cyan border border-synth-cyan/40 hover:bg-synth-cyan/20 transition-all">
class="px-6 py-2 rounded-xl text-xs font-mono text-cyan-500 border border-cyan-500/40 hover:bg-cyan-500/20 transition-all">
↩️ REPLY
</a>
<a [routerLink]="['/compose']" [queryParams]="{forwardTo: '', forwardSubject: 'Fwd: ' + (message.subject || ''), forwardBody: message.body}"
class="px-6 py-2 rounded-xl text-xs font-mono text-synth-purple border border-synth-purple/40 hover:bg-synth-purple/20 transition-all">
class="px-6 py-2 rounded-xl text-xs font-mono text-purple-500 border border-purple-500/40 hover:bg-purple-500/20 transition-all">
➡️ FORWARD
</a>
<button (click)="onDelete()" class="px-6 py-2 rounded-xl text-xs font-mono text-synth-pink border border-synth-pink/40 hover:bg-synth-pink/20 transition-all">
<button (click)="onDelete()" class="px-6 py-2 rounded-xl text-xs font-mono text-pink-500 border border-pink-500/40 hover:bg-pink-500/20 transition-all">
🗑️ DELETE
</button>
</div>
@@ -9,70 +9,71 @@ import { Router } from '@angular/router';
standalone: true,
imports: [RouterLink, FormsModule],
template: `
<div class="min-h-screen relative flex items-center justify-center">
<!-- Synthwave фон -->
<div class="synth-bg" aria-hidden="true">
<div class="min-h-screen relative flex items-center justify-center theme-cyber-magenta">
<!-- Synthwave анимированный фон -->
<div class="synth-stage" aria-hidden="true">
<div class="synth-sun"></div>
<div class="synth-grid">
<div class="synth-grid-plane"></div>
<div class="synth-mountains"></div>
<div class="grid-container">
<div class="grid-plane"></div>
</div>
</div>
<!-- Форма регистрации -->
<div class="relative z-10 w-full max-w-md px-4">
<div class="glass-panel p-8">
<div class="glass-panel p-8 border-2 border-cyan-400 shadow-[0_0_50px_rgba(0,240,255,0.4)]">
<div class="text-center mb-8">
<div class="w-16 h-16 mx-auto rounded-xl bg-gradient-to-br from-synth-pink to-synth-cyan flex items-center justify-center text-synth-dark font-black text-2xl font-orbitron shadow-[0_0_20px_rgba(255,0,127,0.8)] mb-4">
<div class="w-16 h-16 mx-auto rounded-xl bg-gradient-to-br from-pink-500 to-cyan-400 flex items-center justify-center text-slate-950 font-black text-2xl font-orbitron shadow-[0_0_20px_rgba(255,0,127,0.8)] mb-4">
M
</div>
<h1 class="text-3xl font-orbitron font-extrabold tracking-wider neon-text-primary mb-2">
MAIL<span class="text-synth-cyan font-normal">SERVER</span>
<h1 class="text-3xl font-extrabold font-orbitron tracking-wider neon-text-primary mb-2">
MAIL<span class="text-cyan-400 font-normal">SERVER</span>
</h1>
<p class="text-sm text-slate-400 font-mono">Create your mailbox</p>
<p class="text-sm text-cyan-300/70 font-mono tracking-widest uppercase">Create your mailbox</p>
</div>
@if (error) {
<div class="mb-4 p-3 rounded-lg bg-synth-pink/20 border border-synth-pink/40 text-synth-pink text-sm font-mono">
<div class="mb-4 p-3 rounded-lg bg-pink-500/20 border border-pink-500/40 text-pink-400 text-sm font-mono">
{{ error }}
</div>
}
@if (success) {
<div class="mb-4 p-3 rounded-lg bg-synth-cyan/20 border border-synth-cyan/40 text-synth-cyan text-sm font-mono">
Account created! You can now login.
<div class="mb-4 p-3 rounded-lg bg-cyan-500/20 border border-cyan-500/40 text-cyan-400 text-sm font-mono">
Account created! Redirecting to login...
</div>
}
<form (ngSubmit)="onRegister()" class="space-y-4">
<div>
<label for="displayName" class="block text-xs font-mono text-synth-cyan mb-1">DISPLAY NAME</label>
<label for="displayName" class="block text-xs font-mono text-pink-400 mb-1">DISPLAY NAME</label>
<input id="displayName" type="text" [(ngModel)]="displayName" name="displayName" required
class="w-full bg-synth-darker/80 border border-synth-cyan/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-synth-pink focus:border-synth-cyan transition-all"
class="w-full bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-400 transition-all"
placeholder="John Doe">
</div>
<div>
<label for="email" class="block text-xs font-mono text-synth-cyan mb-1">EMAIL</label>
<label for="email" class="block text-xs font-mono text-pink-400 mb-1">EMAIL</label>
<input id="email" type="email" [(ngModel)]="email" name="email" required
class="w-full bg-synth-darker/80 border border-synth-cyan/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-synth-pink focus:border-synth-cyan transition-all"
class="w-full bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-400 transition-all"
placeholder="user@example.com">
</div>
<div>
<label for="password" class="block text-xs font-mono text-synth-cyan mb-1">PASSWORD</label>
<label for="password" class="block text-xs font-mono text-pink-400 mb-1">PASSWORD</label>
<input id="password" type="password" [(ngModel)]="password" name="password" required
class="w-full bg-synth-darker/80 border border-synth-cyan/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-synth-pink focus:border-synth-cyan transition-all"
class="w-full bg-slate-950/80 border border-cyan-500/40 rounded-xl px-4 py-3 text-sm text-slate-200 font-mono focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-cyan-400 transition-all"
placeholder="••••••••">
</div>
<button type="submit" [disabled]="loading"
class="cyber-btn w-full py-3 rounded-xl text-sm font-bold mt-6">
{{ loading ? 'CREATING...' : 'REGISTER' }}
{{ loading ? '⏳ CREATING...' : '⚡ REGISTER' }}
</button>
</form>
<p class="text-center text-sm text-slate-400 mt-6">
<p class="text-center text-sm text-slate-400 mt-6 font-rajdhani">
Already have an account?
<a routerLink="/login" class="text-synth-cyan hover:text-synth-pink font-mono transition-colors">
<a routerLink="/login" class="text-cyan-400 hover:text-pink-400 font-mono transition-colors">
Login here
</a>
</p>
+184 -99
View File
@@ -9,7 +9,7 @@
--color-synth-cyan: #00f0ff;
--color-synth-purple: #7928ca;
--color-synth-yellow: #ffbe0b;
--color-synth-glass: rgba(13, 2, 33, 0.6);
--color-synth-glass: rgba(15, 12, 35, 0.65);
--font-family-orbitron: 'Orbitron', sans-serif;
--font-family-rajdhani: 'Rajdhani', sans-serif;
@@ -17,12 +17,68 @@
}
:root {
--color-synth-dark: #0d0221;
--color-synth-pink: #ff007f;
--color-synth-cyan: #00f0ff;
--color-synth-purple: #7928ca;
--color-synth-yellow: #ffbe0b;
--color-synth-glass: rgba(13, 2, 33, 0.6);
--primary-color: #ff007f;
--secondary-color: #00f0ff;
--accent-color: #7928ca;
--highlight-color: #ffbe0b;
--bg-color: #0d0221;
--grid-color: rgba(255, 0, 127, 0.4);
--glow-color: rgba(0, 240, 255, 0.6);
--blur-quality: 16px;
--synth-glass: rgba(15, 12, 35, 0.65);
}
/* Color Theme Presets */
.theme-cyber-magenta {
--primary-color: #ff007f;
--secondary-color: #00f0ff;
--accent-color: #7928ca;
--highlight-color: #ffbe0b;
--grid-color: rgba(255, 0, 127, 0.4);
--glow-color: rgba(0, 240, 255, 0.6);
}
.theme-neon-cyan {
--primary-color: #00f0ff;
--secondary-color: #00ff88;
--accent-color: #0077ff;
--highlight-color: #e0f7fa;
--grid-color: rgba(0, 240, 255, 0.4);
--glow-color: rgba(0, 255, 136, 0.6);
}
.theme-sunset-orange {
--primary-color: #ff5e00;
--secondary-color: #ffb703;
--accent-color: #d62828;
--highlight-color: #fcbf49;
--grid-color: rgba(255, 94, 0, 0.4);
--glow-color: rgba(255, 183, 3, 0.6);
}
.theme-matrix-green {
--primary-color: #00ff66;
--secondary-color: #39ff14;
--accent-color: #008000;
--highlight-color: #ccff00;
--grid-color: rgba(0, 255, 102, 0.4);
--glow-color: rgba(57, 255, 20, 0.6);
}
/* High Contrast Mode Overrides */
.high-contrast {
--bg-color: #000000;
--primary-color: #ffff00 !important;
--secondary-color: #00ffff !important;
--accent-color: #ffffff !important;
--highlight-color: #ffffff !important;
--grid-color: rgba(0, 255, 255, 0.8) !important;
}
.high-contrast .glass-panel {
background: rgba(0, 0, 0, 0.95) !important;
border: 2px solid var(--secondary-color) !important;
box-shadow: 0 0 15px var(--secondary-color) !important;
}
* {
@@ -32,17 +88,104 @@
}
body {
background-color: var(--color-synth-dark);
background-color: var(--bg-color);
color: #f1f5f9;
font-family: 'Rajdhani', sans-serif;
overflow-x: hidden;
transition: background-color 0.4s ease;
}
/* Glassmorphism панель */
/* Synthwave Background World */
.synth-stage {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 0;
overflow: hidden;
pointer-events: none;
}
/* Sun Graphic */
.synth-sun {
position: absolute;
top: 15%;
left: 50%;
transform: translateX(-50%);
width: 280px;
height: 280px;
border-radius: 50%;
background: linear-gradient(to bottom, #ffbe0b, var(--primary-color), var(--accent-color));
box-shadow: 0 0 80px var(--primary-color), 0 0 120px var(--accent-color);
z-index: 1;
}
/* Sun Bars Cutout Effect */
.synth-sun::after {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
background: repeating-linear-gradient(
to bottom,
transparent 0px,
transparent 6px,
var(--bg-color) 6px,
var(--bg-color) 12px
);
mask-image: linear-gradient(to bottom, transparent 40%, black 100%);
-webkit-mask-image: linear-gradient(to bottom, transparent 40%, black 100%);
}
/* Horizon Silhouette */
.synth-mountains {
position: absolute;
bottom: 45%;
width: 100%;
height: 160px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 120' preserveAspectRatio='none'%3E%3Cpath d='M0,120 L0,80 L150,110 L300,50 L450,95 L600,30 L750,85 L900,40 L1050,90 L1200,60 L1200,120 Z' fill='%23080214'/%3E%3C/svg%3E");
background-size: cover;
z-index: 2;
}
/* Perspective Grid Floor */
.grid-container {
position: absolute;
bottom: 0;
left: -50%;
width: 200%;
height: 50%;
perspective: 450px;
z-index: 3;
mask-image: linear-gradient(to top, rgba(0,0,0,1) 60%, rgba(0,0,0,0) 100%);
-webkit-mask-image: linear-gradient(to top, rgba(0,0,0,1) 60%, rgba(0,0,0,0) 100%);
}
.grid-plane {
position: absolute;
width: 100%;
height: 200%;
top: 0;
left: 0;
transform: rotateX(75deg);
background-size: 50px 50px;
background-image:
linear-gradient(to right, var(--grid-color) 1px, transparent 1px),
linear-gradient(to bottom, var(--grid-color) 1px, transparent 1px);
animation: moveGrid 2s linear infinite;
}
@keyframes moveGrid {
0% { transform: rotateX(75deg) translateY(0); }
100% { transform: rotateX(75deg) translateY(50px); }
}
/* Glassmorphism Cards */
.glass-panel {
background: var(--color-synth-glass);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
background: var(--synth-glass, rgba(15, 12, 35, 0.65));
backdrop-filter: blur(var(--blur-quality));
-webkit-backdrop-filter: blur(var(--blur-quality));
border: 1px solid rgba(0, 240, 255, 0.25);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5), inset 0 0 15px rgba(255, 255, 255, 0.03);
border-radius: 16px;
@@ -50,26 +193,30 @@ body {
}
.glass-panel:hover {
border-color: var(--color-synth-cyan);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.6), 0 0 15px rgba(0, 240, 255, 0.6);
border-color: var(--secondary-color);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.6), 0 0 15px var(--glow-color);
}
/* Неоновый текст */
/* Neon Text & Glow Effects */
.neon-text-primary {
color: #ffffff;
text-shadow: 0 0 5px var(--color-synth-pink), 0 0 15px var(--color-synth-pink);
text-shadow: 0 0 5px var(--primary-color), 0 0 15px var(--primary-color);
}
.neon-text-secondary {
color: #ffffff;
text-shadow: 0 0 5px var(--color-synth-cyan), 0 0 15px var(--color-synth-cyan);
text-shadow: 0 0 5px var(--secondary-color), 0 0 15px var(--secondary-color);
}
/* Cyber кнопка */
.neon-border {
box-shadow: 0 0 10px var(--secondary-color);
}
/* Cyber Button Style */
.cyber-btn {
position: relative;
background: linear-gradient(135deg, rgba(255, 0, 127, 0.2), rgba(0, 240, 255, 0.2));
border: 1px solid var(--color-synth-cyan);
border: 1px solid var(--secondary-color);
color: #ffffff;
font-family: 'Orbitron', sans-serif;
text-transform: uppercase;
@@ -95,80 +242,18 @@ body {
}
.cyber-btn:hover {
background: var(--color-synth-cyan);
color: var(--color-synth-dark);
box-shadow: 0 0 20px var(--color-synth-cyan);
background: var(--secondary-color);
color: #0d0221;
box-shadow: 0 0 20px var(--secondary-color);
transform: translateY(-2px);
}
/* Synthwave фон с сеткой */
.synth-bg {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 0;
overflow: hidden;
pointer-events: none;
.cyber-btn:focus-visible {
outline: 3px solid var(--highlight-color);
outline-offset: 3px;
}
.synth-sun {
position: absolute;
top: 10%;
left: 50%;
transform: translateX(-50%);
width: 200px;
height: 200px;
border-radius: 50%;
background: linear-gradient(to bottom, #ffbe0b, var(--color-synth-pink), var(--color-synth-purple));
box-shadow: 0 0 60px var(--color-synth-pink), 0 0 100px var(--color-synth-purple);
}
.synth-sun::after {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
background: repeating-linear-gradient(
to bottom,
transparent 0px,
transparent 4px,
var(--color-synth-dark) 4px,
var(--color-synth-dark) 8px
);
mask-image: linear-gradient(to bottom, transparent 40%, black 100%);
-webkit-mask-image: linear-gradient(to bottom, transparent 40%, black 100%);
}
.synth-grid {
position: absolute;
bottom: 0;
left: -50%;
width: 200%;
height: 40%;
perspective: 400px;
mask-image: linear-gradient(to top, rgba(0,0,0,1) 60%, rgba(0,0,0,0) 100%);
-webkit-mask-image: linear-gradient(to top, rgba(0,0,0,1) 60%, rgba(0,0,0,0) 100%);
}
.synth-grid-plane {
width: 100%;
height: 200%;
transform: rotateX(75deg);
background-size: 40px 40px;
background-image:
linear-gradient(to right, rgba(255, 0, 127, 0.3) 1px, transparent 1px),
linear-gradient(to bottom, rgba(255, 0, 127, 0.3) 1px, transparent 1px);
animation: gridMove 2s linear infinite;
}
@keyframes gridMove {
0% { transform: rotateX(75deg) translateY(0); }
100% { transform: rotateX(75deg) translateY(40px); }
}
/* Кастомный скроллбар */
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
@@ -177,27 +262,27 @@ body {
background: rgba(13, 2, 33, 0.8);
}
::-webkit-scrollbar-thumb {
background: var(--color-synth-pink);
background: var(--primary-color);
border-radius: 4px;
box-shadow: 0 0 10px var(--color-synth-pink);
box-shadow: 0 0 10px var(--primary-color);
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-synth-cyan);
background: var(--secondary-color);
}
/* Неоновое свечение */
/* Neon glow utilities */
.neon-glow-pink {
box-shadow: 0 0 10px var(--color-synth-pink), 0 0 20px var(--color-synth-pink);
box-shadow: 0 0 10px var(--primary-color), 0 0 20px var(--primary-color);
}
.neon-glow-cyan {
box-shadow: 0 0 10px var(--color-synth-cyan), 0 0 20px var(--color-synth-cyan);
box-shadow: 0 0 10px var(--secondary-color), 0 0 20px var(--secondary-color);
}
/* Светлая тема */
/* Light theme overrides */
.light-theme {
--color-synth-dark: #f0f0f5;
--color-synth-glass: rgba(255, 255, 255, 0.7);
--bg-color: #f0f0f5;
--synth-glass: rgba(255, 255, 255, 0.7);
}
.light-theme body,
@@ -212,7 +297,7 @@ body {
color: #1a1a2e !important;
}
.light-theme .synth-bg {
.light-theme .synth-stage {
display: none !important;
}
@@ -230,7 +315,7 @@ body {
}
.light-theme .cyber-btn:hover {
background: var(--color-synth-purple) !important;
background: var(--accent-color) !important;
color: #fff !important;
}