Backend: * Migration 0009_networks: network_interfaces (ethernet|vlan|bond| bridge|wireguard, role wan|lan|dmz|mgmt|cluster, parent + vlan_id für VLANs) + ip_addresses (interface_id FK, address+prefix, is_vip + vip_priority für Cluster-Failover-VIPs). * Repos services/networkifs + services/ipaddresses + Models + Handler /api/v1/network-interfaces (CRUD + /:id/ip-addresses) und /api/v1/ip-addresses (CRUD). * /api/v1/system/interfaces refactored auf Go-natives net.Interfaces() statt `ip -j addr show` shell-out — die systemd-Sandbox blockt AF_NETLINK auch für Go's runtime, deswegen edgeguard-api.service RestrictAddressFamilies um AF_NETLINK ergänzt. Output-Shape bleibt identisch (ifindex, ifname, flags[], mtu, link_type, address, addr_info[]) — Frontend muss nicht angepasst werden. Frontend: * Networks-Page (/networks): "System-discovered Interfaces" read-only Tags-Card oben, deklarierte Interfaces unten als Tabelle mit Modal-CRUD; Type-Switch zeigt parent+vlan_id-Felder bei type=vlan; Role-Tags farbig (wan blau, lan grün, dmz orange, mgmt purple, cluster magenta). * IPAddresses-Page (/ip-addresses): Tabelle pro Interface, VIP- Toggle blendet vip_priority-Eingabe ein. Goldenes VIP-Tag in der Liste. * Sidebar erweitert um Networks + IP-Adressen + section-grouping. Design 1:1 von mail-gateway/management-ui/ übernommen: * enterprise.css verbatim (Inter-Font via Google CDN statt local woff2), Sidebar 240px dunkler Gradient #0B1426→#101D33→#0D1829, branding-accent #1677ff für Active-State, abgerundete Cards mit shadow-Token, Header weiß mit subtilem backdrop-filter. * AntD-Theme-Tokens: colorPrimary #0EA5E9, fontSize 13, fontFamily 'Inter', controlHeight 34, borderRadius 6. * Layout-Komponenten neu strukturiert: AppLayout/Sidebar/Header matchen mailguard-Klassen-Naming (.app-layout, .main-content, .sidebar-section, .sidebar-menu-item.active, .header-left, …). * Sidebar mit 4 Sektionen (Übersicht / Routing / Netzwerk / System) + Logo-Header + Versions-Footer. Live-deployed auf 89.163.205.6: Networks-Endpoint listet eth0 (89.163.205.6/24, MAC bc:24:11:64:29:e8) + lo, frontend zeigt sie als System-Tags in der Networks-Page. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2711 lines
75 KiB
CSS
2711 lines
75 KiB
CSS
/* EdgeGuard — Enterprise Light Theme (übernommen 1:1 aus mail-gateway). */
|
||
|
||
/* Inter via Google Fonts CDN. mail-gateway hostet woff2 lokal in
|
||
* public/fonts/; EdgeGuard nutzt CDN um keine Font-Binaries zu shippen. */
|
||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
||
|
||
/* ── Design tokens ──────────────────────────────────────────────────────────── */
|
||
:root {
|
||
--branding-primary: #1677ff;
|
||
--branding-accent: #1677ff;
|
||
--radius: 6px;
|
||
--radius-md: 8px;
|
||
--radius-lg: 10px;
|
||
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
|
||
--shadow: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
|
||
--shadow-md: 0 4px 12px rgba(0,0,0,0.08);
|
||
--shadow-lg: 0 8px 32px rgba(0,0,0,0.10), 0 2px 8px rgba(0,0,0,0.05);
|
||
}
|
||
|
||
* {
|
||
box-sizing: border-box;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
font-weight: 400;
|
||
background: #F8FAFC;
|
||
color: #334155;
|
||
font-size: 13px;
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
letter-spacing: -0.01em;
|
||
}
|
||
|
||
/* Global typography */
|
||
h1, h2, h3, h4, h5, h6 {
|
||
font-weight: 600 !important;
|
||
color: #0F172A !important;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
|
||
.ant-typography { color: #334155; }
|
||
.ant-modal-title { font-weight: 600 !important; }
|
||
.ant-card-head-title { font-weight: 600 !important; }
|
||
.ant-tabs-tab { font-weight: 500 !important; }
|
||
.ant-btn { font-weight: 500 !important; letter-spacing: -0.01em; }
|
||
|
||
/* === LAYOUT === */
|
||
.app-layout {
|
||
display: flex;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* === SIDEBAR === */
|
||
.sidebar {
|
||
width: 240px;
|
||
min-height: 100vh;
|
||
background: linear-gradient(180deg, #0B1426 0%, #101D33 50%, #0D1829 100%);
|
||
display: flex;
|
||
flex-direction: column;
|
||
position: fixed;
|
||
left: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
overflow-y: auto;
|
||
scrollbar-width: thin;
|
||
scrollbar-color: rgba(255,255,255,0.1) transparent;
|
||
z-index: 100;
|
||
}
|
||
|
||
.sidebar::-webkit-scrollbar { width: 4px; }
|
||
.sidebar::-webkit-scrollbar-track { background: transparent; }
|
||
.sidebar::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 2px; }
|
||
|
||
.sidebar-logo {
|
||
padding: 20px 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||
}
|
||
|
||
.sidebar-logo-icon {
|
||
width: 32px;
|
||
height: 32px;
|
||
background: linear-gradient(135deg, #1677ff, #1677ff);
|
||
border-radius: 8px;
|
||
box-shadow: 0 0 12px rgba(0, 212, 170, 0.4);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: white;
|
||
}
|
||
|
||
.sidebar-logo-text {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.sidebar-section {
|
||
padding: 16px 16px 4px;
|
||
}
|
||
|
||
.sidebar-section-label {
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1.5px;
|
||
color: #475569;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.sidebar-menu {
|
||
list-style: none;
|
||
padding: 4px 8px;
|
||
}
|
||
|
||
.sidebar-menu-item {
|
||
position: relative;
|
||
border-radius: 6px;
|
||
margin-bottom: 1px;
|
||
}
|
||
|
||
.sidebar-menu-item a,
|
||
.sidebar-menu-item button {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 8px 10px;
|
||
font-size: 14px;
|
||
/* CSS-Variable --nmg-sidebar-text wird vom Sidebar-Komponente
|
||
gesetzt wenn das Branding eine eigene Textfarbe hat. Fallback:
|
||
#94A3B8 (Light-Slate, ursprünglicher Hardcode). */
|
||
color: var(--nmg-sidebar-text, #94A3B8);
|
||
text-decoration: none;
|
||
border-radius: 6px;
|
||
transition: all 0.15s ease;
|
||
background: none;
|
||
border: none;
|
||
cursor: pointer;
|
||
width: 100%;
|
||
}
|
||
|
||
.sidebar-menu-item a:hover,
|
||
.sidebar-menu-item button:hover {
|
||
background: rgba(255,255,255,0.05);
|
||
color: #CBD5E1;
|
||
}
|
||
|
||
.sidebar-menu-item.active::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 0;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 3px;
|
||
height: 20px;
|
||
background: var(--branding-accent, #1677ff);
|
||
border-radius: 0 2px 2px 0;
|
||
box-shadow: 0 0 8px color-mix(in srgb, var(--branding-accent, #1677ff) 40%, transparent);
|
||
}
|
||
|
||
.sidebar-menu-item.active a,
|
||
.sidebar-menu-item.active button {
|
||
background: color-mix(in srgb, var(--branding-accent, #1677ff) 10%, transparent);
|
||
color: var(--branding-accent, #1677ff);
|
||
}
|
||
|
||
.sidebar-version {
|
||
margin-top: auto;
|
||
padding: 16px;
|
||
font-size: 11px;
|
||
color: #475569;
|
||
text-align: center;
|
||
border-top: 1px solid rgba(255,255,255,0.06);
|
||
}
|
||
|
||
.sidebar-logo-img {
|
||
height: 28px;
|
||
width: auto;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* Mobile: sidebar slides in, overlay dims the main content. */
|
||
.sidebar-overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(15, 23, 42, 0.5);
|
||
z-index: 99;
|
||
}
|
||
|
||
.header-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.header-menu-toggle {
|
||
display: none;
|
||
}
|
||
|
||
.header-user {
|
||
color: #334155;
|
||
font-size: 13px;
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.sidebar {
|
||
transform: translateX(-100%);
|
||
transition: transform 0.2s ease;
|
||
}
|
||
.sidebar.open {
|
||
transform: translateX(0);
|
||
}
|
||
.main-content {
|
||
margin-left: 0;
|
||
}
|
||
.header-menu-toggle {
|
||
display: inline-flex;
|
||
}
|
||
.header-user {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
/* === MAIN CONTENT === */
|
||
.main-content {
|
||
margin-left: 240px;
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 100vh;
|
||
overflow-x: hidden;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* === HEADER === */
|
||
.header {
|
||
height: 56px;
|
||
background: #ffffff;
|
||
border-bottom: 1px solid #E5E7EB;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 20px;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 50;
|
||
backdrop-filter: blur(8px);
|
||
background: rgba(255,255,255,0.95);
|
||
}
|
||
|
||
.header-title {
|
||
font-size: 15px;
|
||
font-weight: 500;
|
||
color: #1E293B;
|
||
}
|
||
|
||
.header-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
/* === CONTENT AREA === */
|
||
.content-area {
|
||
padding: 16px;
|
||
flex: 1;
|
||
overflow-x: hidden;
|
||
min-width: 0;
|
||
}
|
||
|
||
.content-card {
|
||
background: #ffffff;
|
||
border: 1px solid #E2E8F0;
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
box-shadow: 0 1px 3px rgba(0,0,0,0.02), 0 1px 2px rgba(0,0,0,0.03);
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
/* === TABS (Pill-Style) === */
|
||
.tab-bar {
|
||
display: flex;
|
||
gap: 8px;
|
||
margin-bottom: 20px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.tab-item {
|
||
padding: 6px 14px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
border: 1.5px solid #E2E8F0;
|
||
border-radius: 8px;
|
||
color: #64748B;
|
||
background: #ffffff;
|
||
cursor: pointer;
|
||
transition: all 0.15s ease;
|
||
}
|
||
|
||
.tab-item:hover {
|
||
border-color: #CBD5E1;
|
||
color: #475569;
|
||
}
|
||
|
||
.tab-item.active {
|
||
border-color: #1677ff;
|
||
background: #F0F9FF;
|
||
color: #1d4ed8;
|
||
}
|
||
|
||
/* === STATUS DOTS === */
|
||
.status-dot {
|
||
width: 7px;
|
||
height: 7px;
|
||
border-radius: 50%;
|
||
display: inline-block;
|
||
}
|
||
|
||
.status-dot.online {
|
||
background: #1677ff;
|
||
box-shadow: 0 0 8px rgba(0, 212, 170, 0.5);
|
||
}
|
||
|
||
.status-dot.offline {
|
||
background: #EF4444;
|
||
box-shadow: 0 0 8px rgba(239, 68, 68, 0.5);
|
||
}
|
||
|
||
/* === BADGES === */
|
||
.badge {
|
||
padding: 2px 8px;
|
||
border-radius: 20px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.badge-success { background: rgba(0, 212, 170, 0.1); color: #1677ff; }
|
||
.badge-danger { background: rgba(239, 68, 68, 0.1); color: #EF4444; }
|
||
.badge-info { background: rgba(14, 165, 233, 0.1); color: #1677ff; }
|
||
.badge-warning { background: rgba(245, 158, 11, 0.1); color: #D97706; }
|
||
|
||
/* ═══════════════════════════════════════════════════════════════════════════════
|
||
PREMIUM ANT DESIGN OVERRIDES — High-End Enterprise Look
|
||
═══════════════════════════════════════════════════════════════════════════════ */
|
||
|
||
/* ── Tables — shadcn-clean ───────────────────────────────────────────────────── */
|
||
.ant-table {
|
||
border-radius: 8px !important;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.ant-table table {
|
||
table-layout: auto !important;
|
||
}
|
||
|
||
.ant-table-thead > tr > th {
|
||
white-space: nowrap !important;
|
||
}
|
||
|
||
.ant-table-tbody > tr > td {
|
||
/* overflow-wrap statt word-break: nur LANGE Strings (URLs,
|
||
Mailadressen ohne Spaces) brechen, kurze formatierte Strings
|
||
wie Timestamps bleiben in einer Zeile. word-break:break-word hat
|
||
22.12.2025 12:30 in „22.1 2.20 25 12: 30" gehackt — Beobachtung
|
||
1.5.61 auf der Sandbox-Page. */
|
||
overflow-wrap: anywhere;
|
||
word-break: normal;
|
||
}
|
||
|
||
.ant-table-thead > tr > th,
|
||
.ant-table-thead > tr > td {
|
||
background: #F8FAFC !important;
|
||
color: #64748B !important;
|
||
font-weight: 500 !important;
|
||
font-size: 12px !important;
|
||
text-transform: uppercase !important;
|
||
letter-spacing: 0.4px !important;
|
||
border-bottom: 1px solid #E2E8F0 !important;
|
||
padding: 10px 16px !important;
|
||
}
|
||
|
||
.ant-table-tbody > tr > td {
|
||
padding: 12px 16px !important;
|
||
border-bottom: 1px solid #F1F5F9 !important;
|
||
font-size: 13px !important;
|
||
color: #334155 !important;
|
||
transition: background 0.1s ease !important;
|
||
}
|
||
|
||
.ant-table-tbody > tr:hover > td {
|
||
background: #F8FAFC !important;
|
||
}
|
||
|
||
.ant-table-tbody > tr:last-child > td {
|
||
border-bottom: none !important;
|
||
}
|
||
|
||
/* Scrollable tables on small screens */
|
||
.ant-table-wrapper {
|
||
border-radius: 8px !important;
|
||
border: 1px solid #E2E8F0 !important;
|
||
overflow: hidden !important;
|
||
}
|
||
|
||
/* ── Modals ──────────────────────────────────────────────────────────────────── */
|
||
.ant-modal {
|
||
max-width: calc(100vw - 32px) !important;
|
||
}
|
||
|
||
.ant-modal .ant-modal-content {
|
||
border-radius: 10px !important;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.10), 0 2px 8px rgba(0,0,0,0.05) !important;
|
||
overflow: hidden !important;
|
||
border: 1px solid #E2E8F0 !important;
|
||
}
|
||
|
||
.ant-modal .ant-modal-header {
|
||
background: #FFFFFF !important;
|
||
border-bottom: 1px solid #E2E8F0 !important;
|
||
padding: 16px 24px !important;
|
||
margin: 0 !important;
|
||
}
|
||
|
||
.ant-modal .ant-modal-title {
|
||
color: #0F172A !important;
|
||
font-weight: 600 !important;
|
||
font-size: 15px !important;
|
||
}
|
||
|
||
.ant-modal .ant-modal-close {
|
||
color: #64748B !important;
|
||
top: 12px !important;
|
||
}
|
||
|
||
.ant-modal .ant-modal-close:hover {
|
||
color: #0F172A !important;
|
||
background: #F1F5F9 !important;
|
||
border-radius: 6px !important;
|
||
}
|
||
|
||
.ant-modal .ant-modal-body {
|
||
padding: 20px 24px !important;
|
||
}
|
||
|
||
.ant-modal .ant-modal-footer {
|
||
border-top: 1px solid #F1F5F9 !important;
|
||
padding: 12px 24px !important;
|
||
background: #FAFAFA !important;
|
||
}
|
||
|
||
/* ── Drawers ─────────────────────────────────────────────────────────────────── */
|
||
.ant-drawer .ant-drawer-header {
|
||
background: #FFFFFF !important;
|
||
border-bottom: 1px solid #E2E8F0 !important;
|
||
padding: 16px 24px !important;
|
||
}
|
||
|
||
.ant-drawer .ant-drawer-title {
|
||
color: #0F172A !important;
|
||
font-weight: 600 !important;
|
||
font-size: 15px !important;
|
||
}
|
||
|
||
.ant-drawer .ant-drawer-close {
|
||
color: #64748B !important;
|
||
}
|
||
|
||
.ant-drawer .ant-drawer-close:hover {
|
||
color: #0F172A !important;
|
||
background: #F1F5F9 !important;
|
||
border-radius: 6px !important;
|
||
}
|
||
|
||
.ant-drawer .ant-drawer-body {
|
||
padding: 20px 24px !important;
|
||
background: #FFFFFF !important;
|
||
}
|
||
|
||
.ant-drawer .ant-drawer-extra .ant-btn-primary {
|
||
background: var(--branding-primary, #1677ff) !important;
|
||
border: none !important;
|
||
}
|
||
|
||
/* ── Buttons — flat, clean, shadcn-style ─────────────────────────────────────── */
|
||
.ant-btn-primary {
|
||
background: var(--branding-primary, #1677ff) !important;
|
||
border: none !important;
|
||
border-radius: 6px !important;
|
||
font-weight: 500 !important;
|
||
color: #fff !important;
|
||
box-shadow: none !important;
|
||
transition: opacity 0.15s ease, background 0.15s ease !important;
|
||
}
|
||
|
||
.ant-btn-primary:hover {
|
||
background: color-mix(in srgb, var(--branding-primary, #1677ff) 85%, #000) !important;
|
||
box-shadow: none !important;
|
||
color: #fff !important;
|
||
}
|
||
|
||
.ant-btn-primary:active {
|
||
background: color-mix(in srgb, var(--branding-primary, #1677ff) 75%, #000) !important;
|
||
}
|
||
|
||
/* Ghost buttons */
|
||
.ant-btn-primary.ant-btn-background-ghost,
|
||
.ant-btn.ant-btn-primary[class*="ghost"] {
|
||
background: transparent !important;
|
||
color: var(--branding-primary, #1677ff) !important;
|
||
border: 1px solid var(--branding-primary, #1677ff) !important;
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
.ant-btn-primary.ant-btn-background-ghost:hover,
|
||
.ant-btn.ant-btn-primary[class*="ghost"]:hover {
|
||
background: rgba(14,165,233,0.05) !important;
|
||
color: var(--branding-primary, #1677ff) !important;
|
||
}
|
||
|
||
/* Danger buttons */
|
||
.ant-btn-primary.ant-btn-dangerous {
|
||
background: #EF4444 !important;
|
||
color: #fff !important;
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
.ant-btn-primary.ant-btn-dangerous:hover {
|
||
background: #DC2626 !important;
|
||
color: #fff !important;
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
/* Default / secondary buttons */
|
||
.ant-btn-default {
|
||
border-radius: 6px !important;
|
||
border-color: #E2E8F0 !important;
|
||
color: #334155 !important;
|
||
font-weight: 500 !important;
|
||
background: #FFFFFF !important;
|
||
box-shadow: none !important;
|
||
transition: background 0.15s ease, border-color 0.15s ease !important;
|
||
}
|
||
|
||
.ant-btn-default:hover {
|
||
border-color: #CBD5E1 !important;
|
||
background: #F8FAFC !important;
|
||
color: #0F172A !important;
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
.ant-btn-default.ant-btn-dangerous {
|
||
color: #EF4444 !important;
|
||
border-color: #FCA5A5 !important;
|
||
}
|
||
|
||
.ant-btn-default.ant-btn-dangerous:hover {
|
||
border-color: #EF4444 !important;
|
||
background: #FFF5F5 !important;
|
||
}
|
||
|
||
/* Link buttons */
|
||
.ant-btn-link {
|
||
color: var(--branding-primary, #1677ff) !important;
|
||
font-weight: 500 !important;
|
||
}
|
||
|
||
.ant-btn-link:hover {
|
||
color: color-mix(in srgb, var(--branding-primary, #1677ff) 75%, #000) !important;
|
||
background: rgba(14,165,233,0.05) !important;
|
||
}
|
||
|
||
/* Text buttons */
|
||
.ant-btn-text {
|
||
color: #64748B !important;
|
||
}
|
||
|
||
.ant-btn-text:hover {
|
||
background: #F1F5F9 !important;
|
||
color: #334155 !important;
|
||
}
|
||
|
||
/* Ensure button content color */
|
||
.ant-btn-primary span,
|
||
.ant-btn-primary .anticon {
|
||
color: inherit !important;
|
||
}
|
||
|
||
/* ── Tags ────────────────────────────────────────────────────────────────────── */
|
||
.ant-tag {
|
||
border-radius: 4px !important;
|
||
font-weight: 500 !important;
|
||
font-size: 11px !important;
|
||
padding: 2px 8px !important;
|
||
line-height: 18px !important;
|
||
margin-inline-end: 4px !important;
|
||
}
|
||
|
||
/* ── Cards (Ant Design) ──────────────────────────────────────────────────────── */
|
||
.ant-card {
|
||
border-radius: 10px !important;
|
||
border-color: #E2E8F0 !important;
|
||
box-shadow: 0 1px 2px rgba(0,0,0,0.04) !important;
|
||
transition: border-color 0.15s ease, box-shadow 0.15s ease !important;
|
||
}
|
||
|
||
.ant-card:hover {
|
||
border-color: #CBD5E1 !important;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.06) !important;
|
||
}
|
||
|
||
.ant-card .ant-card-head {
|
||
border-bottom: 1px solid #F1F5F9 !important;
|
||
padding: 14px 20px !important;
|
||
min-height: auto !important;
|
||
background: #FFFFFF !important;
|
||
}
|
||
|
||
.ant-card .ant-card-head-title {
|
||
font-size: 14px !important;
|
||
font-weight: 600 !important;
|
||
color: #0F172A !important;
|
||
padding: 0 !important;
|
||
}
|
||
|
||
.ant-card .ant-card-body {
|
||
padding: 16px 20px !important;
|
||
}
|
||
|
||
/* ── Form Inputs — shadcn style ──────────────────────────────────────────────── */
|
||
.ant-input,
|
||
.ant-input-affix-wrapper,
|
||
.ant-input-number,
|
||
.ant-input-number-affix-wrapper,
|
||
.ant-select-selector,
|
||
.ant-picker {
|
||
border-radius: 6px !important;
|
||
border-color: #E2E8F0 !important;
|
||
background: #FFFFFF !important;
|
||
transition: border-color 0.15s ease, box-shadow 0.15s ease !important;
|
||
}
|
||
|
||
.ant-input::placeholder,
|
||
.ant-input-number-input::placeholder {
|
||
color: #94A3B8 !important;
|
||
}
|
||
|
||
.ant-input:hover,
|
||
.ant-input-affix-wrapper:hover,
|
||
.ant-input-number:hover,
|
||
.ant-select:not(.ant-select-disabled):hover .ant-select-selector,
|
||
.ant-picker:hover {
|
||
border-color: #CBD5E1 !important;
|
||
}
|
||
|
||
.ant-input:focus,
|
||
.ant-input-focused,
|
||
.ant-input-affix-wrapper-focused,
|
||
.ant-input-number-focused,
|
||
.ant-select-focused .ant-select-selector,
|
||
.ant-picker-focused {
|
||
border-color: var(--branding-primary, #1677ff) !important;
|
||
box-shadow: 0 0 0 2px rgba(14,165,233,0.12) !important;
|
||
}
|
||
|
||
.ant-form-item-label > label {
|
||
font-weight: 500 !important;
|
||
font-size: 13px !important;
|
||
color: #374151 !important;
|
||
}
|
||
|
||
.ant-form-item-explain-error {
|
||
font-size: 12px !important;
|
||
margin-top: 4px !important;
|
||
}
|
||
|
||
/* ── Tabs (Ant Design) ───────────────────────────────────────────────────────── */
|
||
.ant-tabs .ant-tabs-tab {
|
||
font-weight: 500 !important;
|
||
color: #64748B !important;
|
||
transition: color 0.15s ease !important;
|
||
padding: 10px 16px !important;
|
||
}
|
||
|
||
.ant-tabs .ant-tabs-tab:hover {
|
||
color: #334155 !important;
|
||
}
|
||
|
||
.ant-tabs .ant-tabs-tab-active .ant-tabs-tab-btn {
|
||
color: var(--branding-primary, #1677ff) !important;
|
||
font-weight: 600 !important;
|
||
}
|
||
|
||
.ant-tabs .ant-tabs-ink-bar {
|
||
background: var(--branding-primary, #1677ff) !important;
|
||
height: 2px !important;
|
||
border-radius: 2px !important;
|
||
}
|
||
|
||
.ant-tabs-content-holder {
|
||
padding-top: 16px !important;
|
||
}
|
||
|
||
/* ── Pagination ──────────────────────────────────────────────────────────────── */
|
||
.ant-pagination .ant-pagination-item {
|
||
border-radius: 6px !important;
|
||
border-color: #E2E8F0 !important;
|
||
transition: border-color 0.15s, background 0.15s !important;
|
||
}
|
||
|
||
.ant-pagination .ant-pagination-item:hover {
|
||
border-color: var(--branding-primary, #1677ff) !important;
|
||
background: #F0F9FF !important;
|
||
}
|
||
|
||
.ant-pagination .ant-pagination-item-active {
|
||
background: var(--branding-primary, #1677ff) !important;
|
||
border-color: var(--branding-primary, #1677ff) !important;
|
||
}
|
||
|
||
.ant-pagination .ant-pagination-item-active a {
|
||
color: #fff !important;
|
||
}
|
||
|
||
/* ── Switch ──────────────────────────────────────────────────────────────────── */
|
||
.ant-switch-checked {
|
||
background: #10B981 !important;
|
||
}
|
||
|
||
.ant-switch-checked:hover {
|
||
background: #059669 !important;
|
||
}
|
||
|
||
/* ── Progress Bars ───────────────────────────────────────────────────────────── */
|
||
.ant-progress-bg {
|
||
border-radius: 4px !important;
|
||
}
|
||
|
||
.ant-progress-inner {
|
||
border-radius: 4px !important;
|
||
}
|
||
|
||
/* ── Popconfirm ──────────────────────────────────────────────────────────────── */
|
||
.ant-popconfirm .ant-btn-primary {
|
||
border-radius: 6px !important;
|
||
}
|
||
|
||
/* ── Select Dropdown ─────────────────────────────────────────────────────────── */
|
||
.ant-select-dropdown {
|
||
border-radius: 10px !important;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.12) !important;
|
||
border: 1px solid #E2E8F0 !important;
|
||
padding: 4px !important;
|
||
}
|
||
|
||
.ant-select-item-option {
|
||
border-radius: 6px !important;
|
||
margin: 1px 0 !important;
|
||
}
|
||
|
||
.ant-select-item-option-active {
|
||
background: #F0F9FF !important;
|
||
}
|
||
|
||
.ant-select-item-option-selected {
|
||
background: #F0F9FF !important;
|
||
font-weight: 600 !important;
|
||
}
|
||
|
||
/* ── Tooltip ─────────────────────────────────────────────────────────────────── */
|
||
.ant-tooltip-inner {
|
||
border-radius: 8px !important;
|
||
font-size: 12px !important;
|
||
font-weight: 500 !important;
|
||
}
|
||
|
||
/* ── Message ─────────────────────────────────────────────────────────────────── */
|
||
.ant-message-notice-content {
|
||
border-radius: 10px !important;
|
||
box-shadow: 0 8px 24px rgba(0,0,0,0.1) !important;
|
||
padding: 10px 16px !important;
|
||
font-weight: 500 !important;
|
||
}
|
||
|
||
/* ── Divider ─────────────────────────────────────────────────────────────────── */
|
||
.ant-divider-inner-text {
|
||
font-size: 12px !important;
|
||
font-weight: 600 !important;
|
||
color: #64748B !important;
|
||
text-transform: uppercase !important;
|
||
letter-spacing: 0.5px !important;
|
||
}
|
||
|
||
/* ── Badge ───────────────────────────────────────────────────────────────────── */
|
||
.ant-badge-status-dot {
|
||
width: 8px !important;
|
||
height: 8px !important;
|
||
}
|
||
|
||
/* ── Alert ───────────────────────────────────────────────────────────────────── */
|
||
.ant-alert {
|
||
border-radius: 10px !important;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════════════════════════════
|
||
LAYOUT UTILITIES — replace inline styles in page components
|
||
═══════════════════════════════════════════════════════════════════════════════ */
|
||
|
||
/* Flex helpers */
|
||
.flex { display: flex; }
|
||
.flex-center { display: flex; align-items: center; }
|
||
.flex-between { display: flex; align-items: center; justify-content: space-between; }
|
||
.flex-col { display: flex; flex-direction: column; }
|
||
.flex-wrap { flex-wrap: wrap; }
|
||
.gap-4 { gap: 4px; }
|
||
.gap-8 { gap: 8px; }
|
||
.gap-12 { gap: 12px; }
|
||
.gap-16 { gap: 16px; }
|
||
|
||
/* Spacing */
|
||
.mb-0 { margin-bottom: 0; }
|
||
.mb-8 { margin-bottom: 8px; }
|
||
.mb-12 { margin-bottom: 12px; }
|
||
.mb-16 { margin-bottom: 16px; }
|
||
.mb-20 { margin-bottom: 20px; }
|
||
.mt-8 { margin-top: 8px; }
|
||
.mt-12 { margin-top: 12px; }
|
||
.mt-16 { margin-top: 16px; }
|
||
|
||
/* Text utilities */
|
||
.text-muted { color: #64748B; }
|
||
.text-subtle { color: #94A3B8; }
|
||
.text-dark { color: #0F172A; }
|
||
.text-xxs { font-size: 10px; }
|
||
.text-xs { font-size: 11px; }
|
||
.text-sm { font-size: 12px; }
|
||
.text-base { font-size: 13px; }
|
||
.font-medium { font-weight: 500; }
|
||
.font-semibold { font-weight: 600; }
|
||
.font-bold { font-weight: 700; }
|
||
.font-mono { font-variant-numeric: tabular-nums; font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace; }
|
||
.text-upper { text-transform: uppercase; letter-spacing: 0.4px; font-size: 11px; font-weight: 600; color: #64748B; }
|
||
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
.text-nowrap { white-space: nowrap; }
|
||
.text-wrap { white-space: normal; word-break: break-word; }
|
||
.text-right { text-align: right; }
|
||
.text-left { text-align: left; }
|
||
.flex-shrink-0 { flex-shrink: 0; }
|
||
|
||
/* ── Table-Cell utilities (1.5.58) ─────────────────────────────────────────────
|
||
Engere Tabellen-Variante für Live-Listen mit vielen Spalten (Mail-Logs,
|
||
Training, MailQueue). Ant-Default ist 12px×16px, das frisst auf
|
||
Desktop-Breite mit ≥6 Spalten zu viel horizontalen Platz und sprengt
|
||
die Tabelle aus dem Viewport. .tbl-compact reduziert padding auf
|
||
6px×10px und text auf 12px (statt 13px).
|
||
Wrap-Modus (.tbl-wrap-cells) verhindert dass nowrap-Cells die
|
||
Tabellen-Breite ins Unendliche ziehen. */
|
||
.tbl-compact .ant-table-tbody > tr > td,
|
||
.tbl-compact .ant-table-thead > tr > th {
|
||
padding: 6px 10px !important;
|
||
font-size: 12px !important;
|
||
}
|
||
.tbl-compact .ant-table-thead > tr > th {
|
||
font-size: 11px !important;
|
||
}
|
||
.tbl-wrap-cells .ant-table-tbody > tr > td {
|
||
white-space: normal !important;
|
||
word-break: break-word !important;
|
||
}
|
||
.tbl-cell-nowrap {
|
||
white-space: nowrap;
|
||
word-break: normal;
|
||
}
|
||
.tbl-tag-xs .ant-tag {
|
||
font-size: 10px !important;
|
||
line-height: 16px !important;
|
||
padding: 0 6px !important;
|
||
}
|
||
|
||
/* ── Detail-Panel-Utilities ────────────────────────────────────────────────────
|
||
Für die expandable-row + Quarantäne-Preview-Modal: Symbol-Listen,
|
||
Score-Highlights, kleine Mono-Fonts. */
|
||
.detail-row { display: flex; justify-content: space-between; padding: 3px 0; border-bottom: 1px solid #fafafa; gap: 4px; }
|
||
.detail-row .ant-typography { font-size: 11px; }
|
||
.detail-section-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.4px; color: #94A3B8; font-weight: 600; }
|
||
.score-large { font-size: 22px; font-weight: 700; line-height: 1.2; }
|
||
.score-required { font-size: 10px; color: #8c8c8c; }
|
||
.score-pill { font-size: 13px !important; padding: 2px 12px !important; }
|
||
|
||
/* ── Symbol-list rows (rspamd-Detail) ──────────────────────────────────────────
|
||
ellipsis name + tag-with-score, beide im selben flex-row. */
|
||
.symbol-row { display: flex; justify-content: space-between; align-items: center; padding: 3px 0; border-bottom: 1px solid #fafafa; gap: 6px; }
|
||
.symbol-row .symbol-name { font-size: 11px; font-family: 'JetBrains Mono', monospace; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
.symbol-row .ant-tag { font-size: 10px !important; flex-shrink: 0; margin: 0; }
|
||
|
||
/* ── Responsive table fallback ─────────────────────────────────────────────────
|
||
Auf Desktop ≤960 px kommen viel-spaltige Tabellen ins Stocken; das
|
||
built-in `scroll: { x: ... }` der Ant-Table greift dann. Damit der
|
||
horizontale Scroll subtil bleibt: Scrollbar dünner, kein Border-Box. */
|
||
@media (max-width: 960px) {
|
||
.ant-table-wrapper .ant-table-content { overflow-x: auto !important; }
|
||
.tbl-compact .ant-table-tbody > tr > td,
|
||
.tbl-compact .ant-table-thead > tr > th { padding: 5px 8px !important; font-size: 11px !important; }
|
||
}
|
||
|
||
/* Score-Color-Klassen — komplementär zu mailLogsScoreColor() in JS,
|
||
damit datengetriebene-color als style={{ color: ... }} bleiben kann
|
||
ODER alternative semantische Klassen genutzt werden. */
|
||
.score-clean { color: #10B981; }
|
||
.score-warn { color: #F59E0B; }
|
||
.score-spam { color: #EF4444; }
|
||
.score-virus { color: #7C2D12; }
|
||
|
||
/* MailLogs-Detail-Row: action-tag + relay/IP links, host-tag rechts. */
|
||
.maillog-detail { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
||
.maillog-detail-left { display: flex; flex-wrap: nowrap; gap: 6px; align-items: center; overflow: hidden; min-width: 0; }
|
||
.maillog-detail-left > * { flex-shrink: 0; }
|
||
|
||
/* MailLogs Expandable-Detail: Pill-Header + Dark-Terminal-Block */
|
||
.maillog-expand { padding: 8px 0; }
|
||
.maillog-pill-header { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 12px; background: #fafafa; border-radius: 6px; margin-bottom: 8px; }
|
||
.maillog-pill-header .ant-tag { font-size: 10px; }
|
||
.maillog-subject-trunc { max-width: 250px; display: inline-block; vertical-align: bottom; }
|
||
.maillog-score-pill { padding: 2px 8px; border-radius: 12px; font-weight: 600; font-size: 11px; color: #fff; }
|
||
.maillog-terminal { background: #1f1f1f; border: 1px solid #303030; border-radius: 6px; padding: 12px 16px; font-family: 'JetBrains Mono', monospace; font-size: 11px; line-height: 1.7; color: #d9d9d9; max-height: 320px; overflow-y: auto; }
|
||
.maillog-terminal-empty { color: #bfbfbf; }
|
||
.maillog-step { margin-bottom: 4px; white-space: pre-wrap; }
|
||
.maillog-step-ts { color: #888; margin-right: 8px; }
|
||
.maillog-step-action { color: #fa8c16; margin-right: 8px; }
|
||
.maillog-step-relay { color: #73d13d; margin-right: 8px; }
|
||
.maillog-step-source { margin-right: 8px; }
|
||
|
||
/* Training-action utilities */
|
||
.btn-ham { color: #52c41a; }
|
||
.mr-4 { margin-right: 4px; }
|
||
|
||
/* Training-Intro-Block: lead + bullet-list + quarantine-hint footer */
|
||
.training-intro { font-size: 13px; line-height: 1.6; }
|
||
.training-intro p { margin: 0 0 8px; }
|
||
.training-intro ul { margin: 0 0 8px; padding-left: 20px; }
|
||
.training-intro-foot { margin: 0 !important; color: #595959; }
|
||
|
||
/* Symbol-row neutral (informative without score-tag) */
|
||
.symbol-row-neutral { padding: 2px 0; }
|
||
|
||
/* ── Sandbox-Detail (1.5.65) ──────────────────────────────────────────────────
|
||
Aufklappbarer Report-Block in der Sandbox-Tabelle. Zeigt alle 13 Stages
|
||
strukturiert in Cards. Verdict-Hero oben prominent, Sub-Cards Grid darunter. */
|
||
.sandbox-detail { padding: 8px 0; }
|
||
|
||
/* Verdict-Hero: Score-Big-Number + Verdict-Tag + Confidence + Duration */
|
||
.sandbox-verdict-hero {
|
||
display: flex; align-items: center; gap: 24px;
|
||
padding: 16px 20px; margin-bottom: 12px;
|
||
background: linear-gradient(180deg, #F8FAFC 0%, #FFFFFF 100%);
|
||
border: 1px solid #E2E8F0; border-radius: 10px;
|
||
}
|
||
.sandbox-verdict-score { display: flex; align-items: baseline; gap: 4px; }
|
||
.sandbox-verdict-score-num { font-size: 42px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
|
||
.sandbox-verdict-score-suffix { font-size: 14px; color: #94A3B8; font-weight: 500; }
|
||
.sandbox-verdict-meta { display: flex; flex-direction: column; gap: 4px; }
|
||
|
||
/* Reasons-Liste (Bullet-Punkte unter Hero) */
|
||
.sandbox-section { padding: 8px 12px; background: #FFFBEB; border-radius: 6px; border: 1px solid #FDE68A; margin-bottom: 8px; }
|
||
.sandbox-reasons { margin: 4px 0 0 0; padding-left: 18px; font-size: 12px; color: #92400E; }
|
||
.sandbox-reasons li { margin-bottom: 2px; }
|
||
|
||
/* Key-Value-Grid für IP/RDNS/ASN/Country/MIME-Stats etc. */
|
||
.kv-grid {
|
||
display: grid; grid-template-columns: max-content 1fr;
|
||
gap: 4px 12px; align-items: baseline;
|
||
font-size: 12px;
|
||
}
|
||
.kv-key { font-size: 11px; color: #64748B; font-weight: 500; }
|
||
|
||
/* Received-Chain Liste, leicht eingerückt + monospace */
|
||
.sandbox-recv-chain { margin-top: 4px; padding: 6px 10px; background: #F8FAFC; border-radius: 4px; max-height: 200px; overflow-y: auto; }
|
||
|
||
/* Attachment- und URL-Listen */
|
||
.sandbox-attach-list, .sandbox-url-list { display: flex; flex-direction: column; gap: 8px; }
|
||
.sandbox-attach-item, .sandbox-url-item {
|
||
padding: 8px 10px; border: 1px solid #F1F5F9; border-radius: 6px; background: #FAFAFA;
|
||
}
|
||
.sandbox-attach-head, .sandbox-url-head {
|
||
display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
|
||
}
|
||
.sandbox-attach-head strong { font-size: 12px; }
|
||
.sandbox-url-head code { word-break: break-all; min-width: 0; }
|
||
|
||
/* ── Dashboard: Enterprise KPI-Layout (1.5.59) ─────────────────────────────
|
||
Großer, klarer Blick auf die wichtigsten Zahlen. Hierarchie:
|
||
1. Hero-Row mit den 4 kritischen KPIs (Mails, Spam, Rejected, Quarantäne)
|
||
mit großen Zahlen + Trend-Sparkline daneben
|
||
2. Sekundäre KPIs in kleineren Tiles
|
||
3. Listen + Charts darunter
|
||
Ant-Statistic-Komponente wird per .kpi-* übersteuert. */
|
||
.dashboard-hero .ant-card-body {
|
||
padding: 20px 24px !important;
|
||
}
|
||
.kpi-tile {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
padding: 14px 16px;
|
||
background: #FFFFFF;
|
||
border: 1px solid #E2E8F0;
|
||
border-radius: 10px;
|
||
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
||
min-width: 0;
|
||
}
|
||
.kpi-tile:hover {
|
||
border-color: #CBD5E1;
|
||
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.04);
|
||
}
|
||
.kpi-tile.kpi-primary { background: #F8FAFC; border-color: #E2E8F0; }
|
||
.kpi-tile.kpi-success { background: #F0FDF4; border-color: #BBF7D0; }
|
||
.kpi-tile.kpi-warning { background: #FFFBEB; border-color: #FDE68A; }
|
||
.kpi-tile.kpi-danger { background: #FEF2F2; border-color: #FECACA; }
|
||
.kpi-tile.kpi-info { background: #EFF6FF; border-color: #BFDBFE; }
|
||
.kpi-tile-label {
|
||
font-size: 11px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
color: #64748B;
|
||
font-weight: 600;
|
||
}
|
||
.kpi-tile-value {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
line-height: 1.1;
|
||
color: #0F172A;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.kpi-tile-value.kpi-success { color: #10B981; }
|
||
.kpi-tile-value.kpi-warning { color: #F59E0B; }
|
||
.kpi-tile-value.kpi-danger { color: #EF4444; }
|
||
.kpi-tile-value.kpi-info { color: #0EA5E9; }
|
||
|
||
/* Score-Boost-Tags (Lookalike-Watcher etc.) — drei Tier-Farben.
|
||
Wird per Klasse statt inline-style gesetzt damit das Theme global
|
||
tunable bleibt. */
|
||
.tag-score-high.ant-tag {
|
||
background: #FEE2E2;
|
||
border-color: #FCA5A5;
|
||
color: #B91C1C;
|
||
}
|
||
.tag-score-mid.ant-tag {
|
||
background: #FEF3C7;
|
||
border-color: #FCD34D;
|
||
color: #B45309;
|
||
}
|
||
|
||
/* Threat-Radar-Tile-Akzent: bei > 0 Hits farbiger Border-Left + voller
|
||
Wert; bei 0 Hits halb-transparent. Klassen statt inline-style. */
|
||
.threat-tile.threat-tile--hit { opacity: 1; border-left: 3px solid #EF4444; }
|
||
.threat-tile.threat-tile--warn { opacity: 1; border-left: 3px solid #F59E0B; }
|
||
.threat-tile.threat-tile--crit { opacity: 1; border-left: 3px solid #DC2626; }
|
||
.threat-tile.threat-tile--idle { opacity: 0.55; border-left: 3px solid transparent; }
|
||
.threat-tile-value--hit { color: #EF4444; }
|
||
.threat-tile-value--warn { color: #F59E0B; }
|
||
.threat-tile-value--crit { color: #DC2626; }
|
||
|
||
.kpi-tile-link { display: block; text-decoration: none; cursor: pointer; }
|
||
.kpi-tile-link:hover .kpi-tile-compact { background: #F8FAFC; }
|
||
|
||
/* MailTrace eml-Eingabe: monospace fixed-width für raw-mail-Snippets. */
|
||
.eml-textarea textarea {
|
||
font-family: Menlo, Consolas, monospace !important;
|
||
font-size: 12px !important;
|
||
}
|
||
|
||
/* MailTrace Stage-Card-Layout (Status-Tag + Summary + collapsible JSON). */
|
||
.trace-stage-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
.trace-stage-tag.ant-tag {
|
||
min-width: 60px;
|
||
text-align: center;
|
||
}
|
||
.trace-stage-detail {
|
||
margin-top: 8px;
|
||
font-size: 11px;
|
||
background: #F6F8FA;
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
overflow: auto;
|
||
max-height: 240px;
|
||
}
|
||
.full-width-stack { width: 100%; }
|
||
|
||
.stats-window-label {
|
||
display: block;
|
||
margin-bottom: 8px;
|
||
font-size: 12px;
|
||
font-style: italic;
|
||
}
|
||
|
||
/* Inline-Style-Cleanup-Sweep (1.7.14): Utility-Klassen für die
|
||
Patterns die in /pages noch als style={{...}} drinhingen.
|
||
Memory-Regel: style={{}} nur für datengetriebene Werte (Score-
|
||
Color, Progress-Width per pct), Layout = Klasse. */
|
||
.row-flex-end { display: flex; justify-content: flex-end; }
|
||
.list-item-flat { padding: 4px 0; border: 0; }
|
||
.tag-xs.ant-tag { font-size: 10px; }
|
||
.tag-xxs.ant-tag { font-size: 9px; }
|
||
.code-xs { font-size: 12px; }
|
||
.text-xxs { font-size: 11px; }
|
||
.api-key-input { width: calc(100% - 44px); }
|
||
|
||
/* ScoreTuning-Tab — sticky filter header + dashed-row separator. */
|
||
.scoretuning-sticky-header {
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 5;
|
||
padding: 8px 0;
|
||
background: #fff;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
margin-bottom: 16px;
|
||
}
|
||
.scoretuning-row {
|
||
padding: 6px 0;
|
||
border-bottom: 1px dashed #f0f0f0;
|
||
}
|
||
|
||
/* Setup-Wizard: ordered list mit reduziertem padding-left + pre mit
|
||
wrap. */
|
||
.setup-list-tight { padding-left: 18px; }
|
||
.setup-pre-wrap { white-space: pre-wrap; }
|
||
|
||
/* SuspiciousTLDs-Spam-Ratio-Cell: Farbe wird datengetrieben gesetzt
|
||
(color je nach pct), aber das font-weight gehört in eine Klasse. */
|
||
.tld-ratio-cell { font-weight: 500; }
|
||
|
||
/* Generische Icon-Farb-Tokens — drei Tier-Farben für SuspiciousTLDs
|
||
Lock-Cell + ähnliche „operator-locked"/„not-locked"-Indikatoren. */
|
||
.text-warning-icon { color: #F59E0B; }
|
||
.text-muted-icon { color: #94A3B8; }
|
||
|
||
/* MailBodyPreview — sandboxed iframe + pre für Plain-Text. Höhe 60vh
|
||
damit auch lange Newsletter komplett sichtbar sind, scroll im iframe. */
|
||
.mail-body-preview-headers .ant-descriptions-item-label {
|
||
width: 110px;
|
||
font-weight: 500;
|
||
}
|
||
.mail-body-preview-frame {
|
||
width: 100%;
|
||
min-height: 50vh;
|
||
height: 50vh;
|
||
border: 0;
|
||
border-radius: 4px;
|
||
background: #fff;
|
||
}
|
||
.mail-body-preview-pre {
|
||
margin: 0;
|
||
padding: 8px;
|
||
background: #F8FAFC;
|
||
border-radius: 4px;
|
||
font-family: Menlo, Consolas, monospace;
|
||
font-size: 12px;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
max-height: 50vh;
|
||
overflow: auto;
|
||
}
|
||
.kpi-tile-sub {
|
||
font-size: 11px;
|
||
color: #94A3B8;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
.kpi-tile-icon {
|
||
font-size: 16px;
|
||
color: #94A3B8;
|
||
}
|
||
|
||
/* SystemHealthCard — pro Mountpoint eine Zeile mit Pfad + Progress.
|
||
Pfad bleibt in fixer Breite damit die Bars vertikal alignen. */
|
||
.kpi-tile-value-sm {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: #0F172A;
|
||
line-height: 1.3;
|
||
}
|
||
.sys-disk-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
}
|
||
.sys-disk-row {
|
||
font-size: 11px;
|
||
color: #475569;
|
||
}
|
||
.sys-disk-row .ant-progress {
|
||
margin-bottom: 0;
|
||
}
|
||
.sys-disk-path {
|
||
display: inline-block;
|
||
min-width: 96px;
|
||
font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
|
||
color: #64748B;
|
||
font-size: 11px;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
/* Mobile-Layout für SystemHealthCard auf < 768 px Viewports.
|
||
Statt Tabelle ein Card-Stack pro Node — jede Karte hat Header
|
||
(Hostname + Lizenz-Tag) und vertikal gestapelte Werte (Load,
|
||
Uptime, Memory, Disks). 320 px breite Smartphones rendern damit
|
||
ohne Horizontal-Scroll. */
|
||
.sys-mobile-stack {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
.sys-mobile-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
padding: 12px;
|
||
border: 1px solid #E2E8F0;
|
||
border-radius: 8px;
|
||
background: #F8FAFC;
|
||
}
|
||
.sys-mobile-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
gap: 8px;
|
||
}
|
||
.sys-mobile-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.sys-mobile-block {
|
||
margin-top: 4px;
|
||
}
|
||
|
||
/* Branding-Settings Logo-Preview: zentriert das Logo in einer
|
||
80x80-Box mit hellem Border, damit Admin sofort sieht wie das
|
||
Logo skaliert. object-fit:contain erhält das Aspect-Ratio. */
|
||
.branding-logo-preview {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 96px;
|
||
height: 96px;
|
||
padding: 8px;
|
||
border: 1px solid #E2E8F0;
|
||
border-radius: 8px;
|
||
background: #FFFFFF;
|
||
}
|
||
.branding-logo-preview img {
|
||
max-width: 100%;
|
||
max-height: 100%;
|
||
object-fit: contain;
|
||
}
|
||
|
||
/* Login-BG-Preview: 16:9-Box, das Bild wird cover-gerendert wie es
|
||
später im Portal aussehen wird, damit der Operator vor dem Speichern
|
||
sieht ob Crop / Fokus passen. */
|
||
.branding-loginbg-preview {
|
||
display: inline-flex;
|
||
width: 240px;
|
||
height: 135px;
|
||
border: 1px solid #E2E8F0;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
background: #F8FAFC;
|
||
}
|
||
.branding-loginbg-preview img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
/* Section heading inside Card body */
|
||
.section-title {
|
||
font-size: 11px !important;
|
||
text-transform: uppercase !important;
|
||
letter-spacing: 0.5px !important;
|
||
color: #64748B !important;
|
||
font-weight: 600 !important;
|
||
margin: 0 0 12px 0 !important;
|
||
}
|
||
|
||
/* Compact row of secondary KPIs (4-up). Tighter than primary tiles. */
|
||
.kpi-tile-compact {
|
||
padding: 10px 14px;
|
||
background: #FFFFFF;
|
||
border: 1px solid #F1F5F9;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
.kpi-tile-compact .kpi-tile-value {
|
||
font-size: 20px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* Cluster-status pill: shows N/M nodes online in one chip */
|
||
.cluster-pill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 4px 10px;
|
||
border-radius: 999px;
|
||
background: #F0FDF4;
|
||
border: 1px solid #BBF7D0;
|
||
color: #047857;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
}
|
||
.cluster-pill.degraded {
|
||
background: #FEF2F2;
|
||
border-color: #FECACA;
|
||
color: #B91C1C;
|
||
}
|
||
.cluster-pill .anticon {
|
||
font-size: 10px;
|
||
}
|
||
.maillog-symbols-section { margin-top: 12px; }
|
||
.maillog-symbols-list { margin-top: 6px; display: flex; flex-wrap: wrap; gap: 4px; }
|
||
.maillog-postscreen-toggle .ant-typography { font-size: 12px; }
|
||
|
||
/* Quarantäne-Preview-Body (Modal): pre-formatted RFC822-Body. */
|
||
.quarantine-preview-body {
|
||
max-height: 420px; overflow: auto;
|
||
background: #F8FAFC; padding: 12px; border-radius: 6px;
|
||
border: 1px solid #E2E8F0; font-size: 12px; line-height: 1.45;
|
||
white-space: pre-wrap; word-break: break-word;
|
||
}
|
||
.ml-12 { margin-left: 12px; }
|
||
.mt-6 { margin-top: 6px; }
|
||
|
||
/* Margin-zero override für Tags die direkt neben anderen Inhalten stehen
|
||
und keine extra-Lücke zum nächsten Element brauchen sollen. */
|
||
.tag-no-mr { margin-right: 0; }
|
||
.tag-no-margin { margin: 0; }
|
||
|
||
/* Symbol-Lists in Training/Detail (rspamd-Symbol-Block).
|
||
Trennlinie zwischen Symbol-Gruppen. */
|
||
.symbol-divider { margin: 6px 0; }
|
||
|
||
/* Additional utilities for Settings + global use */
|
||
.mb-24 { margin-bottom: 24px; }
|
||
.mt-0 { margin-top: 0; }
|
||
.mt-4 { margin-top: 4px; }
|
||
.mr-6 { margin-right: 6px; }
|
||
.p-24 { padding: 24px; }
|
||
.d-block { display: block; }
|
||
.w-full { width: 100%; }
|
||
.w-200 { width: 200px; }
|
||
.max-w-480 { max-width: 480px; }
|
||
.max-w-520 { max-width: 520px; }
|
||
.max-w-560 { max-width: 560px; }
|
||
.text-center { text-align: center; }
|
||
.text-primary { color: #1677ff; }
|
||
.text-label { font-size: 13px; font-weight: 600; color: #1E293B; display: block; }
|
||
.mono-sm { font-family: monospace; font-size: 12px; }
|
||
.mono-xs { font-family: monospace; font-size: 11px; }
|
||
.mono-base { font-family: monospace; font-size: 13px; }
|
||
.card-bordered { border: 1px solid #E5E7EB; border-radius: 8px; }
|
||
.card-bordered--lg { border: 1px solid #E5E7EB; border-radius: 10px; }
|
||
.btn-primary-blue { background: #1677ff; border-color: #1677ff; }
|
||
.spinner-center { text-align: center; padding: 24px; }
|
||
.loader-center { min-height: 40vh; display: grid; place-items: center; }
|
||
|
||
/* Form-field width helpers — replace inline style={{ width: N }} on
|
||
Form.Item / InputNumber / Input so mail-config, domain-form, filters
|
||
can share a single set of standardised column widths. */
|
||
.field-w-100 { width: 100px; }
|
||
.field-w-120 { width: 120px; }
|
||
.field-w-140 { width: 140px; }
|
||
.field-w-160 { width: 160px; }
|
||
.field-w-180 { width: 180px; }
|
||
.field-w-200 { width: 200px; }
|
||
.field-w-220 { width: 220px; }
|
||
.field-w-240 { width: 240px; }
|
||
.field-w-260 { width: 260px; }
|
||
.max-w-220 { max-width: 220px; }
|
||
.max-w-240 { max-width: 240px; }
|
||
.max-w-260 { max-width: 260px; }
|
||
|
||
/* Inline helpers for small label+value rows. */
|
||
.text-hint-sm { font-size: 12px; margin-top: -4px; }
|
||
.title-suffix { font-size: 16px; font-weight: 400; margin-left: 12px; }
|
||
|
||
/* Semantic status icon colors — shared by Settings (upgrade timeline),
|
||
Dashboard, Cluster status cells and anywhere else a state icon needs
|
||
a consistent palette. */
|
||
.icon-success { color: #52c41a; }
|
||
.icon-info { color: #1677ff; }
|
||
.icon-danger { color: #ff4d4f; }
|
||
.icon-muted { color: #8c8c8c; }
|
||
.icon-warning { color: #faad14; }
|
||
|
||
.inline-elapsed { margin-left: 12px; font-size: 12px; }
|
||
.upgrade-timeline { margin-top: 16px; padding-left: 8px; }
|
||
.auto-update-hint { margin-top: 12px; font-size: 12px; margin-bottom: 0; }
|
||
|
||
.setup-wrapper-done { max-width: 520px; margin: 80px auto; padding: 24px; }
|
||
.setup-wrapper-wizard { max-width: 720px; margin: 60px auto; padding: 24px; }
|
||
|
||
/* Score cell — font-weight shared; color is data-driven so it stays inline. */
|
||
.score-cell { font-weight: 500; }
|
||
.hidden { display: none !important; }
|
||
|
||
.training-dropzone { min-height: 180px; }
|
||
|
||
/* Ham button — green counterpart to AntD's danger (spam). Used in the
|
||
Training / Quarantine / Mail-History pages so "learn as ham" is
|
||
visually consistent with "learn as spam". AntD has no built-in
|
||
success variant for Button size=small, so we overlay our own. */
|
||
.btn-ham.ant-btn,
|
||
.btn-ham.ant-btn:focus {
|
||
color: #10B981 !important;
|
||
border-color: #86EFAC !important;
|
||
background: #FFFFFF !important;
|
||
}
|
||
.btn-ham.ant-btn:hover {
|
||
color: #059669 !important;
|
||
border-color: #10B981 !important;
|
||
background: #F0FDF4 !important;
|
||
}
|
||
.btn-ham.ant-btn:disabled {
|
||
color: #94A3B8 !important;
|
||
border-color: #E2E8F0 !important;
|
||
background: #F8FAFC !important;
|
||
}
|
||
|
||
/* Toolbar row used by page headers that mix title + actions buttons. */
|
||
.page-toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 12px;
|
||
gap: 12px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.page-toolbar h1,
|
||
.page-toolbar h2,
|
||
.page-toolbar h3,
|
||
.page-toolbar h4,
|
||
.page-toolbar h5 {
|
||
margin: 0;
|
||
}
|
||
|
||
/* Inline icon+text combos */
|
||
.icon-label {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
/* Muted icon box (used in section headers, stat cards) */
|
||
.icon-box {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 28px;
|
||
height: 28px;
|
||
background: #F0F9FF;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
color: var(--branding-primary, #1677ff);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.icon-box--sm {
|
||
width: 22px;
|
||
height: 22px;
|
||
font-size: 12px;
|
||
border-radius: 5px;
|
||
}
|
||
|
||
.icon-box--lg {
|
||
width: 36px;
|
||
height: 36px;
|
||
font-size: 17px;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
/* Color variants */
|
||
.icon-box--green { background: #F0FDF4; color: #10B981; }
|
||
.icon-box--red { background: #FFF5F5; color: #EF4444; }
|
||
.icon-box--orange { background: #FFFBEB; color: #F59E0B; }
|
||
.icon-box--purple { background: #F5F3FF; color: #8B5CF6; }
|
||
.icon-box--teal { background: #EFF6FF; color: #1677ff; }
|
||
.icon-box--slate { background: #F8FAFC; color: #64748B; }
|
||
|
||
/* Mobile card items (for ProTable mobile views) */
|
||
.mobile-card {
|
||
background: #FFFFFF;
|
||
border: 1px solid #E2E8F0;
|
||
border-radius: 10px;
|
||
padding: 14px 16px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.mobile-card:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.mobile-card__header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.mobile-card__title {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: #0F172A;
|
||
}
|
||
|
||
.mobile-card__meta {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
font-size: 12px;
|
||
color: #64748B;
|
||
}
|
||
|
||
.mobile-card__row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
}
|
||
|
||
.mobile-card__actions {
|
||
border-top: 1px solid #F1F5F9;
|
||
padding-top: 8px;
|
||
}
|
||
|
||
/* Inline status dot */
|
||
.dot {
|
||
display: inline-block;
|
||
width: 7px;
|
||
height: 7px;
|
||
border-radius: 50%;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.dot--green { background: #10B981; }
|
||
.dot--red { background: #EF4444; }
|
||
.dot--orange { background: #F59E0B; }
|
||
.dot--blue { background: #3B82F6; }
|
||
.dot--gray { background: #94A3B8; }
|
||
|
||
/* Top-list rows on the dashboard. Three-line structure:
|
||
row 1 rank + label (left) total (right, prominent)
|
||
row 2 progress bar (full width)
|
||
row 3 inline counters with .dot- prefixes
|
||
Layout uses CSS-grid so wrapping never collapses the right-side
|
||
total below the label — the previous flex+space-between version
|
||
broke at narrow widths and produced unreadable stacks. */
|
||
.toplist-row {
|
||
width: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
.toplist-head {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
align-items: baseline;
|
||
gap: 12px;
|
||
}
|
||
.toplist-rank {
|
||
display: inline-block;
|
||
min-width: 22px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: #94A3B8;
|
||
letter-spacing: 0.4px;
|
||
}
|
||
.toplist-label {
|
||
font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
color: #0F172A;
|
||
}
|
||
.toplist-sub {
|
||
margin-left: 8px;
|
||
color: #94A3B8;
|
||
font-size: 11px;
|
||
font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace;
|
||
}
|
||
.toplist-total {
|
||
font-variant-numeric: tabular-nums;
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
color: #1E293B;
|
||
}
|
||
.toplist-bar {
|
||
height: 4px;
|
||
background: #F1F5F9;
|
||
border-radius: 2px;
|
||
overflow: hidden;
|
||
}
|
||
.toplist-bar-fill {
|
||
height: 100%;
|
||
border-radius: 2px;
|
||
}
|
||
.toplist-bar-fill--blue { background: #0EA5E9; }
|
||
.toplist-bar-fill--red { background: #EF4444; }
|
||
.toplist-counters {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
font-size: 11px;
|
||
font-variant-numeric: tabular-nums;
|
||
color: #475569;
|
||
align-items: center;
|
||
}
|
||
.toplist-counter {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
}
|
||
|
||
/* Empty state */
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 48px 24px;
|
||
text-align: center;
|
||
gap: 8px;
|
||
color: #94A3B8;
|
||
}
|
||
|
||
.empty-state__icon {
|
||
font-size: 36px;
|
||
margin-bottom: 8px;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.empty-state__title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #64748B;
|
||
}
|
||
|
||
.empty-state__desc {
|
||
font-size: 13px;
|
||
color: #94A3B8;
|
||
max-width: 300px;
|
||
}
|
||
|
||
/* ── AntD Table inside DataCard (flush/no-padding) ───────────────────────────── */
|
||
/* Remove double borders when table is inside data-card */
|
||
.data-card .ant-table-wrapper {
|
||
border: none !important;
|
||
border-radius: 0 !important;
|
||
}
|
||
|
||
.data-card .ant-table {
|
||
border-radius: 0 !important;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════════════════════════════
|
||
RESPONSIVE — Mobile Optimization
|
||
═══════════════════════════════════════════════════════════════════════════════ */
|
||
|
||
@media (max-width: 768px) {
|
||
.ant-drawer-content-wrapper {
|
||
width: 100% !important;
|
||
max-width: 100vw !important;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 992px) {
|
||
.sidebar {
|
||
transform: translateX(-100%);
|
||
transition: transform 0.3s ease;
|
||
}
|
||
|
||
.sidebar.open {
|
||
transform: translateX(0);
|
||
}
|
||
|
||
.main-content {
|
||
margin-left: 0;
|
||
}
|
||
|
||
.header {
|
||
height: 52px;
|
||
padding: 0 12px;
|
||
}
|
||
|
||
.header-menu-toggle {
|
||
display: flex !important;
|
||
}
|
||
|
||
.sidebar-overlay {
|
||
display: block !important;
|
||
}
|
||
|
||
.content-area {
|
||
padding: 8px;
|
||
}
|
||
|
||
.content-card {
|
||
padding: 14px;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.impersonation-banner {
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
padding: 8px 12px !important;
|
||
font-size: 12px !important;
|
||
}
|
||
|
||
.ant-table-wrapper {
|
||
overflow-x: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.ant-modal .ant-modal-body {
|
||
padding: 16px !important;
|
||
}
|
||
|
||
.ant-modal .ant-modal-header {
|
||
padding: 14px 16px !important;
|
||
}
|
||
|
||
.ant-modal .ant-modal-footer {
|
||
padding: 12px 16px !important;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.header-title {
|
||
font-size: 13px;
|
||
max-width: 120px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.header-actions {
|
||
gap: 6px;
|
||
}
|
||
|
||
.header-user-email {
|
||
display: none !important;
|
||
}
|
||
|
||
.header-lang-text {
|
||
display: none !important;
|
||
}
|
||
|
||
.content-area {
|
||
padding: 6px;
|
||
}
|
||
|
||
.content-card {
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.page-header-icon {
|
||
width: 32px !important;
|
||
height: 32px !important;
|
||
font-size: 15px !important;
|
||
}
|
||
|
||
.tab-bar {
|
||
gap: 4px;
|
||
}
|
||
|
||
.tab-item {
|
||
padding: 5px 10px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.stat-card {
|
||
padding: 12px !important;
|
||
}
|
||
|
||
.ant-modal {
|
||
top: 16px !important;
|
||
padding-bottom: 16px !important;
|
||
}
|
||
|
||
.ant-modal .ant-modal-header {
|
||
padding: 12px 16px !important;
|
||
}
|
||
|
||
.ant-modal .ant-modal-title {
|
||
font-size: 14px !important;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.header {
|
||
height: 48px;
|
||
padding: 0 8px;
|
||
}
|
||
|
||
.header-title {
|
||
max-width: 100px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.content-area {
|
||
padding: 4px;
|
||
}
|
||
|
||
.content-card {
|
||
padding: 8px;
|
||
border-radius: 6px;
|
||
margin-bottom: 8px;
|
||
}
|
||
}
|
||
|
||
/* ── Auth pages (Login, ForgotPassword, ResetPassword) ───────────────────────── */
|
||
.auth-page {
|
||
min-height: 100vh;
|
||
background: #F8FAFC;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 16px;
|
||
}
|
||
|
||
.auth-card {
|
||
background: #ffffff;
|
||
border: 1px solid #E5E7EB;
|
||
border-radius: 10px;
|
||
padding: 32px;
|
||
width: 100%;
|
||
max-width: 400px;
|
||
box-shadow: 0 1px 3px rgba(0,0,0,0.03);
|
||
}
|
||
|
||
.auth-logo-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-bottom: 28px;
|
||
}
|
||
|
||
.auth-logo-title {
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: #1E293B;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.auth-logo-subtitle {
|
||
font-size: 12px;
|
||
color: #94A3B8;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.auth-back-link {
|
||
margin-top: 20px;
|
||
text-align: center;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.auth-hint {
|
||
font-size: 13px;
|
||
color: #64748B;
|
||
margin-bottom: 20px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* ─── Update Changelog ──────────────────────────────────────────────────────── */
|
||
|
||
.changelog-collapse {
|
||
margin-top: 8px;
|
||
border: 1px solid #FED7AA !important;
|
||
border-radius: 8px !important;
|
||
background: #FFFBEB !important;
|
||
}
|
||
|
||
.changelog-collapse-label {
|
||
font-weight: 500;
|
||
color: #1677ff;
|
||
}
|
||
|
||
.changelog-body {
|
||
margin: 0;
|
||
font-family: var(--font-mono, 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace);
|
||
font-size: 13px;
|
||
line-height: 1.7;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
color: #1E293B;
|
||
}
|
||
|
||
.changelog-table .ant-table {
|
||
background: transparent;
|
||
}
|
||
|
||
.changelog-table .ant-table-cell {
|
||
padding: 4px 8px !important;
|
||
border-bottom: 1px solid #FDE68A !important;
|
||
vertical-align: top;
|
||
}
|
||
|
||
.changelog-version-tag {
|
||
font-family: var(--font-mono, 'SFMono-Regular', Consolas, monospace);
|
||
font-size: 11px;
|
||
color: #64748B;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.changelog-type-tag {
|
||
font-family: var(--font-mono, 'SFMono-Regular', Consolas, monospace);
|
||
font-size: 11px;
|
||
}
|
||
.flex-1 { flex: 1; }
|
||
.text-red { color: #EF4444; }
|
||
.text-green { color: #10B981; }
|
||
.ml-6 { margin-left: 6px; }
|
||
.ml-8 { margin-left: 8px; }
|
||
|
||
/* ═══════════════════════════════════════════════════════════════════════════════
|
||
SHARED ENTERPRISE COMPONENTS — Reusable across all pages
|
||
═══════════════════════════════════════════════════════════════════════════════ */
|
||
|
||
/* ── Settings Panel (light gray section with title) ────────────────────────── */
|
||
.ent-settings-panel {
|
||
background: #F8FAFC;
|
||
border: 1px solid #E5E7EB;
|
||
border-radius: 8px;
|
||
padding: 16px;
|
||
}
|
||
|
||
.ent-settings-panel__title {
|
||
display: block;
|
||
margin-bottom: 12px;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: #1E293B;
|
||
}
|
||
|
||
.ent-settings-panel__title .anticon {
|
||
margin-right: 6px;
|
||
}
|
||
|
||
/* ── Info Field (monospace value box with label) ───────────────────────────── */
|
||
.ent-field-label {
|
||
display: block;
|
||
margin-bottom: 4px;
|
||
font-size: 12px;
|
||
color: #64748B;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.ent-field-value {
|
||
display: block;
|
||
font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace;
|
||
font-size: 13px;
|
||
background: #F8FAFC;
|
||
padding: 6px 10px;
|
||
border-radius: 6px;
|
||
border: 1px solid #E2E8F0;
|
||
color: #1E293B;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.ent-field-value--success {
|
||
background: #F0FDF4;
|
||
border-color: #BBF7D0;
|
||
color: #15803D;
|
||
}
|
||
|
||
/* ── Warning / Info Alert Box ──────────────────────────────────────────────── */
|
||
.ent-alert-box {
|
||
border-radius: 8px;
|
||
padding: 12px 16px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.ent-alert-box--warning {
|
||
background: #FFF7ED;
|
||
border: 1px solid #FED7AA;
|
||
color: #92400E;
|
||
}
|
||
|
||
.ent-alert-box--info {
|
||
background: #F0F9FF;
|
||
border: 1px solid #BAE6FD;
|
||
color: #1d4ed8;
|
||
}
|
||
|
||
.ent-alert-box--success {
|
||
background: #F0FDF4;
|
||
border: 1px solid #BBF7D0;
|
||
color: #15803D;
|
||
}
|
||
|
||
.ent-alert-box--danger {
|
||
background: #FEF2F2;
|
||
border: 1px solid #FECACA;
|
||
color: #991B1B;
|
||
}
|
||
|
||
/* ── Inline Card (white bordered card used outside DataCard) ───────────────── */
|
||
.ent-card {
|
||
background: #fff;
|
||
border: 1px solid #E5E7EB;
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
|
||
}
|
||
|
||
.ent-card--compact {
|
||
padding: 16px;
|
||
}
|
||
|
||
.ent-card--flush {
|
||
padding: 0;
|
||
}
|
||
|
||
/* ── Selectable Card (for type selectors, e.g. restore type) ───────────────── */
|
||
.ent-select-card {
|
||
flex: 1 1 calc(50% - 4px);
|
||
min-width: 120px;
|
||
border: 2px solid #E5E7EB;
|
||
border-radius: 8px;
|
||
padding: 10px 14px;
|
||
cursor: pointer;
|
||
background: #FAFAFA;
|
||
transition: all 0.15s ease;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 10px;
|
||
}
|
||
|
||
.ent-select-card:hover {
|
||
border-color: #CBD5E1;
|
||
}
|
||
|
||
.ent-select-card--active {
|
||
border-color: var(--branding-primary, #1677ff);
|
||
background: rgba(22, 119, 255, 0.06);
|
||
}
|
||
|
||
.ent-select-card__icon {
|
||
font-size: 20px;
|
||
margin-top: 1px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.ent-select-card__title {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: #1E293B;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
/* ── Code Block (dark terminal-style output) ───────────────────────────────── */
|
||
.ent-code-block {
|
||
background: #0B1426;
|
||
color: #E2E8F0;
|
||
padding: 14px 16px;
|
||
border-radius: 8px;
|
||
font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace;
|
||
font-size: 12px;
|
||
line-height: 1.7;
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
white-space: pre-wrap;
|
||
word-break: break-all;
|
||
}
|
||
|
||
/* ── DS Record / Code Snippet Row ──────────────────────────────────────────── */
|
||
.ent-code-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.ent-code-row:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.ent-code-row__code {
|
||
flex: 1;
|
||
font-size: 11px;
|
||
font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace;
|
||
background: #E5E7EB;
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
word-break: break-all;
|
||
}
|
||
|
||
/* ── Mini Stat Chip (inline stat badge used in active tasks, etc.) ─────────── */
|
||
.ent-stat-chip {
|
||
background: #F0F9FF;
|
||
border: 1px solid #BAE6FD;
|
||
border-radius: 8px;
|
||
padding: 8px 14px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.ent-stat-chip--green {
|
||
background: #F0FDF4;
|
||
border-color: #BBF7D0;
|
||
}
|
||
|
||
.ent-stat-chip--orange {
|
||
background: #FFF7ED;
|
||
border-color: #FED7AA;
|
||
}
|
||
|
||
.ent-stat-chip__icon {
|
||
font-size: 16px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.ent-stat-chip__icon--blue { color: #1677ff; }
|
||
.ent-stat-chip__icon--green { color: #22C55E; }
|
||
.ent-stat-chip__icon--orange { color: #F59E0B; }
|
||
|
||
/* ── Divider thin ──────────────────────────────────────────────────────────── */
|
||
.ent-divider-sm { margin: 12px 0; }
|
||
|
||
/* ── Update Modal — Mission Control ────────────────────────────────────────
|
||
* Full-screen overlay shown during a self-upgrade. Pattern 1:1 from
|
||
* netcell-webpanel/management-ui/src/pages/Dashboard/Dashboard.css.
|
||
* The orbit uses two counter-rotating rings + two dots + a pulsing
|
||
* center icon, plus a four-step progress list and a large seconds
|
||
* timer. Classes are namespaced with `update-modal` so they don't
|
||
* clash with AntD Modal internals. */
|
||
/* Update-Banner Mobile-Layout (1.6.92+). Auf engen Viewports kollidiert
|
||
der „Update verfügbar"-Text mit den beiden Aktion-Buttons (Check +
|
||
Update Now), weil AntD-Alert beide horizontal nebeneinander rendert.
|
||
Unter 640 px kippen wir das Layout in column-flex, action-Bereich
|
||
landet unter der Message + die Buttons stretchen auf 100% Breite. */
|
||
@media (max-width: 640px) {
|
||
.update-banner-alert.ant-alert {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
}
|
||
.update-banner-alert .ant-alert-content {
|
||
margin-right: 0;
|
||
}
|
||
.update-banner-alert .ant-alert-action {
|
||
margin-left: 0;
|
||
margin-top: 8px;
|
||
}
|
||
.update-banner-alert .ant-alert-action .ant-space {
|
||
width: 100%;
|
||
display: flex;
|
||
}
|
||
.update-banner-alert .ant-alert-action .ant-space > .ant-space-item {
|
||
flex: 1;
|
||
}
|
||
.update-banner-alert .ant-alert-action button {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
/* Popconfirm der hinter "Update jetzt installieren?" steckt. Default
|
||
ist sehr breit weil die Description die ganze "Rolling-Upgrade über
|
||
alle N Knoten ..."-Zeile in EINER Zeile rendert. Wir kappen die
|
||
Breite und lassen den Text umbrechen — schmaler aber höher. */
|
||
.update-popconfirm.ant-popover {
|
||
max-width: 380px;
|
||
}
|
||
.update-popconfirm .ant-popconfirm-description {
|
||
white-space: normal;
|
||
line-height: 1.45;
|
||
font-size: 13px;
|
||
}
|
||
.update-popconfirm .ant-popconfirm-message-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.update-modal-overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 9999;
|
||
background: rgba(3, 7, 18, 0.92);
|
||
backdrop-filter: blur(12px);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
animation: updateFadeIn 0.4s ease;
|
||
}
|
||
@keyframes updateFadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
.update-modal {
|
||
text-align: center;
|
||
color: #E2E8F0;
|
||
max-width: 400px;
|
||
padding: 40px;
|
||
}
|
||
.update-modal__orbit {
|
||
position: relative;
|
||
width: 140px;
|
||
height: 140px;
|
||
margin: 0 auto 32px;
|
||
}
|
||
.update-modal__ring {
|
||
position: absolute;
|
||
inset: 0;
|
||
border: 2px solid rgba(22, 119, 255, 0.15);
|
||
border-radius: 50%;
|
||
border-top-color: #1677ff;
|
||
animation: updateSpin 2s linear infinite;
|
||
}
|
||
.update-modal__ring--2 {
|
||
inset: 12px;
|
||
border-color: rgba(22, 119, 255, 0.08);
|
||
border-top-color: rgba(22, 119, 255, 0.4);
|
||
animation-duration: 3s;
|
||
animation-direction: reverse;
|
||
}
|
||
@keyframes updateSpin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
.update-modal__dot {
|
||
position: absolute;
|
||
width: 8px;
|
||
height: 8px;
|
||
background: #1677ff;
|
||
border-radius: 50%;
|
||
top: -4px;
|
||
left: 50%;
|
||
margin-left: -4px;
|
||
box-shadow: 0 0 12px rgba(22, 119, 255, 0.8);
|
||
animation: updateSpin 2s linear infinite;
|
||
transform-origin: 4px 74px;
|
||
}
|
||
.update-modal__dot--2 {
|
||
background: #00D4AA;
|
||
box-shadow: 0 0 12px rgba(0, 212, 170, 0.8);
|
||
animation-duration: 3s;
|
||
animation-direction: reverse;
|
||
transform-origin: 4px 62px;
|
||
top: 8px;
|
||
}
|
||
.update-modal__center {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
width: 56px;
|
||
height: 56px;
|
||
background: linear-gradient(135deg, #0B1426 0%, #1E293B 100%);
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 0 30px rgba(22, 119, 255, 0.2);
|
||
}
|
||
.update-modal__icon {
|
||
font-size: 24px;
|
||
color: #1677ff;
|
||
animation: updatePulse 1.5s ease-in-out infinite;
|
||
}
|
||
@keyframes updatePulse {
|
||
0%, 100% { opacity: 1; transform: scale(1); }
|
||
50% { opacity: 0.7; transform: scale(1.1); }
|
||
}
|
||
.update-modal__title {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: #FFFFFF;
|
||
margin-bottom: 8px;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
.update-modal__version {
|
||
font-size: 14px;
|
||
color: #64748B;
|
||
margin-bottom: 32px;
|
||
font-family: 'JetBrains Mono', monospace;
|
||
}
|
||
.update-modal__steps {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
margin-bottom: 32px;
|
||
text-align: left;
|
||
}
|
||
.update-modal__step {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
font-size: 13px;
|
||
color: #475569;
|
||
transition: color 0.3s;
|
||
}
|
||
.update-modal__step--active { color: #E2E8F0; }
|
||
.update-modal__step--done { color: #10B981; }
|
||
.update-modal__step-dot {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
background: #334155;
|
||
flex-shrink: 0;
|
||
transition: all 0.3s;
|
||
}
|
||
.update-modal__step--active .update-modal__step-dot {
|
||
background: #1677ff;
|
||
box-shadow: 0 0 8px rgba(22, 119, 255, 0.6);
|
||
animation: updateDotPulse 1s ease-in-out infinite;
|
||
}
|
||
.update-modal__step--done .update-modal__step-dot {
|
||
background: #10B981;
|
||
box-shadow: 0 0 8px rgba(16, 185, 129, 0.6);
|
||
animation: none;
|
||
}
|
||
@keyframes updateDotPulse {
|
||
0%, 100% { box-shadow: 0 0 4px rgba(22, 119, 255, 0.3); }
|
||
50% { box-shadow: 0 0 12px rgba(22, 119, 255, 0.8); }
|
||
}
|
||
.update-modal__timer {
|
||
font-size: 36px;
|
||
font-weight: 800;
|
||
color: #1677ff;
|
||
font-family: 'JetBrains Mono', monospace;
|
||
margin-bottom: 8px;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
.update-modal__hint {
|
||
font-size: 12px;
|
||
color: #475569;
|
||
}
|
||
|
||
/* EnvelopeFrom — zwei-zeilige Anzeige in den Mail-Logs/Training/
|
||
Sandbox-Tabellen. Envelope steht oben, Header-From in Klammern
|
||
darunter wenn er sich unterscheidet. Mobile-platzsparend: kompakte
|
||
Schriftgrößen, beide Zeilen mit overflow-wrap:anywhere damit lange
|
||
Adressen umbrechen statt die Spalte zu sprengen. */
|
||
.ef-stack {
|
||
display: flex;
|
||
flex-direction: column;
|
||
line-height: 1.25;
|
||
min-width: 0;
|
||
}
|
||
.ef-line {
|
||
display: block;
|
||
overflow-wrap: anywhere;
|
||
word-break: normal;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.ef-line-envelope {
|
||
font-size: 13px;
|
||
color: #0F172A;
|
||
font-weight: 500;
|
||
}
|
||
.ef-line-header {
|
||
font-size: 11px;
|
||
color: #64748B;
|
||
}
|
||
.ef-tooltip {
|
||
margin: 0;
|
||
font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
|
||
font-size: 11px;
|
||
white-space: pre-wrap;
|
||
}
|
||
@media (max-width: 575px) {
|
||
.ef-line-envelope { font-size: 12px; }
|
||
.ef-line-header { font-size: 10px; }
|
||
}
|
||
|
||
/* GDPR/DSGVO masked rendering — addresses + subjects masked by
|
||
default; the operator clicks to confirm a reveal-reason which is
|
||
audited before the plaintext appears. The clickable variant uses a
|
||
subtle dotted underline so the cell looks like a button without
|
||
adding background colour to a dense table. */
|
||
.masked-address,
|
||
.masked-subject {
|
||
display: inline-block;
|
||
min-width: 0;
|
||
max-width: 100%;
|
||
/* Adressen wie 010f019dd…@mailer.members.netflix.com haben oft kein
|
||
Whitespace und würden ohne Bruch das Layout sprengen — wir lassen
|
||
den Browser an jeder Stelle umbrechen (overflow-wrap:anywhere) und
|
||
nehmen einen zweiten Zeilenumbruch in Kauf statt Ellipsis. Der
|
||
User-Befund 2026-04-29: 'die anadressen sind teilweise noch zu
|
||
breit z.B. die von Amazon, da muss ein Zeilenumbruch rein sonst
|
||
ist das ganze Layout kaputt'. Truncation per ellipsis war hier
|
||
verkehrt — der Operator will den Volltext sehen, auch wenn er
|
||
dafür auf zwei Zeilen muss. */
|
||
white-space: normal;
|
||
overflow-wrap: anywhere;
|
||
word-break: break-word;
|
||
vertical-align: middle;
|
||
}
|
||
/* Im Two-Line-Layout (envelope + Header-From) erbt die ef-stack-Box
|
||
das Wrap-Verhalten — beide Zeilen umbrechen unabhängig, kein
|
||
Ellipsis. */
|
||
.masked-address .ef-stack {
|
||
max-width: 100%;
|
||
}
|
||
.masked-address .ef-line {
|
||
display: block;
|
||
overflow-wrap: anywhere;
|
||
word-break: break-word;
|
||
white-space: normal;
|
||
}
|
||
.masked-clickable {
|
||
cursor: pointer;
|
||
border-bottom: 1px dotted #94A3B8;
|
||
border-radius: 2px;
|
||
}
|
||
.masked-clickable:hover {
|
||
color: #0EA5E9;
|
||
border-bottom-color: #0EA5E9;
|
||
}
|
||
.masked-clickable:focus-visible {
|
||
outline: 2px solid #0EA5E9;
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* Quarantine-Tabelle: Subject-Spalte hart auf eine Zeile limitieren
|
||
damit lange Spam-Betreffe das Layout nicht zerlegen. AntD's
|
||
ellipsis: true greift bei sehr langen ungefluschten Strings nicht
|
||
immer — diese expliziten max-width + Truncation sind robuster. */
|
||
.quar-subject-cell {
|
||
display: inline-block;
|
||
max-width: 100%;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
/* Reporting-Page — Stacked-Bar-Chart pro Tag.
|
||
Pro-Bar Stack-Reihenfolge bottom→top: clean, other, spam, rejected.
|
||
Höhe relativ zum Cluster-weiten Tages-Maximum, damit ruhige Tage
|
||
nicht abflachen. */
|
||
.reporting-chart {
|
||
display: flex;
|
||
align-items: flex-end;
|
||
gap: 2px;
|
||
height: 220px;
|
||
padding: 8px 4px;
|
||
border-bottom: 1px solid #E2E8F0;
|
||
border-left: 1px solid #E2E8F0;
|
||
background: linear-gradient(to top, #F8FAFC 0%, transparent 60%);
|
||
}
|
||
.reporting-bar-col {
|
||
flex: 1 1 0;
|
||
display: flex;
|
||
align-items: flex-end;
|
||
height: 100%;
|
||
min-width: 4px;
|
||
}
|
||
.reporting-bar-stack {
|
||
display: flex;
|
||
flex-direction: column-reverse;
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 2px 2px 0 0;
|
||
overflow: hidden;
|
||
transition: opacity 0.15s ease;
|
||
}
|
||
.reporting-bar-col:hover .reporting-bar-stack { opacity: 0.85; }
|
||
.reporting-bar { width: 100%; }
|
||
.reporting-bar--clean { background: #10B981; }
|
||
.reporting-bar--other { background: #94A3B8; }
|
||
.reporting-bar--spam { background: #F59E0B; }
|
||
.reporting-bar--rej { background: #EF4444; }
|
||
|
||
.reporting-chart-legend {
|
||
display: flex;
|
||
gap: 18px;
|
||
flex-wrap: wrap;
|
||
margin-top: 12px;
|
||
font-size: 12px;
|
||
color: #475569;
|
||
}
|
||
.reporting-chart-legend .legend-item {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
.reporting-chart-legend .legend-dot {
|
||
display: inline-block;
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 2px;
|
||
}
|
||
.legend-dot--clean { background: #10B981; }
|
||
.legend-dot--spam { background: #F59E0B; }
|
||
.legend-dot--rej { background: #EF4444; }
|
||
.legend-dot--other { background: #94A3B8; }
|
||
|
||
|
||
/* ── Dashboard v2 KPI-Tiles ────────────────────────────────────────── */
|
||
.kpi-tile-card { transition: box-shadow 0.15s ease; }
|
||
.kpi-tile-card:hover { box-shadow: 0 2px 8px rgba(15, 23, 42, 0.06); }
|
||
|
||
.kpi-tile-row { display: flex; justify-content: space-between; }
|
||
.kpi-tile-label {
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
letter-spacing: 0.5px;
|
||
text-transform: uppercase;
|
||
}
|
||
.kpi-tile-value-row {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 8px;
|
||
margin-top: 6px;
|
||
}
|
||
.kpi-tile-value { margin: 0 !important; line-height: 1.1 !important; }
|
||
.kpi-tile-suffix { font-size: 13px; color: #64748B; margin-left: 2px; }
|
||
.kpi-tile-delta { font-size: 12px; font-weight: 500; }
|
||
.kpi-tile-subline { font-size: 11px; display: block; margin-top: 4px; }
|
||
.kpi-tile-spark { margin-top: 12px; }
|
||
|
||
/* ── Dashboard v2 DistributionBar ──────────────────────────────────── */
|
||
.distribution-bar {
|
||
display: flex;
|
||
width: 100%;
|
||
border-radius: 3px;
|
||
overflow: hidden;
|
||
background: #F1F5F9;
|
||
}
|
||
.distribution-bar-empty {
|
||
width: 100%;
|
||
border-radius: 3px;
|
||
background: #F1F5F9;
|
||
}
|
||
.distribution-bar-seg {
|
||
display: block;
|
||
height: 100%;
|
||
transition: width 0.2s ease;
|
||
}
|
||
|
||
/* ── Dashboard v2 Sections ─────────────────────────────────────────── */
|
||
.dashboard-section { margin-bottom: 20px; }
|
||
.dashboard-section-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: baseline;
|
||
margin-bottom: 10px;
|
||
gap: 12px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.dashboard-section-title { margin: 0 !important; }
|
||
.dashboard-empty-chart {
|
||
height: 240px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* ── Dashboard v2 ThreatRadar ──────────────────────────────────────── */
|
||
.threat-list-section-label {
|
||
display: block;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.5px;
|
||
text-transform: uppercase;
|
||
margin-bottom: 8px;
|
||
}
|
||
.threat-active-list { display: flex; flex-direction: column; gap: 6px; }
|
||
.threat-active-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 6px 0;
|
||
}
|
||
.threat-active-bar {
|
||
width: 4px;
|
||
height: 30px;
|
||
border-radius: 2px;
|
||
flex-shrink: 0;
|
||
}
|
||
.threat-active-text { display: flex; flex-direction: column; flex: 1 1 auto; }
|
||
.threat-active-count {
|
||
font-size: 18px;
|
||
min-width: 36px;
|
||
text-align: right;
|
||
}
|
||
|
||
/* ── Dashboard v2 TopRecipients ────────────────────────────────────── */
|
||
.topr-toolbar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 12px 16px;
|
||
border-bottom: 1px solid #F1F5F9;
|
||
}
|
||
.topr-domain-cell { display: flex; flex-direction: column; }
|
||
.topr-domain-name { font-size: 13px; font-weight: 500; }
|
||
.topr-domain-subtitle {
|
||
font-size: 11px;
|
||
color: #94A3B8;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
max-width: 320px;
|
||
}
|
||
.topr-footer {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 10px 16px;
|
||
border-top: 1px solid #F1F5F9;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
}
|
||
.topr-legend { font-size: 11px; }
|
||
|
||
/* ── Dashboard v2 Page-Root ────────────────────────────────────────── */
|
||
.dashboard-v2-page { padding: 4px; }
|
||
.dashboard-v2-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 18px;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
}
|
||
.dashboard-v2-title { margin: 0 !important; }
|
||
|
||
/* LearningStats — Enterprise-Card mit Donut + Metric-Bars (1.8.88) */
|
||
.learning-card .ant-card-body { padding: 20px 24px; }
|
||
.learning-donut-wrap {
|
||
display: flex; align-items: center; justify-content: center;
|
||
min-height: 140px;
|
||
position: relative;
|
||
}
|
||
.learning-donut-empty {
|
||
display: flex; flex-direction: column; align-items: center;
|
||
justify-content: center;
|
||
width: 140px; height: 140px;
|
||
border: 2px dashed #E2E8F0; border-radius: 50%;
|
||
text-align: center;
|
||
}
|
||
/* 1.10.5: Donut-Center-Label zog frueher per Pie-annotations bei
|
||
y='62%' rein und schnitt unten in den Ring; der Annotations-Pfad
|
||
ist nicht zuverlaessig zentriert weil chart-padding mitspielt.
|
||
Jetzt absolute Overlay zentriert auf 50/50, bleibt sicher in der
|
||
inneren Hole (innerRadius=0.7 → 70% Hohlraum) und wraps nicht. */
|
||
.learning-donut-center {
|
||
position: absolute; inset: 0;
|
||
display: flex; flex-direction: column;
|
||
align-items: center; justify-content: center;
|
||
pointer-events: none;
|
||
text-align: center;
|
||
line-height: 1.05;
|
||
}
|
||
.learning-donut-center-value {
|
||
font-size: 22px; font-weight: 700; color: #0F172A;
|
||
}
|
||
.learning-donut-center-label {
|
||
font-size: 9px; color: #94A3B8;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
margin-top: 2px;
|
||
}
|
||
.learning-metric-row {
|
||
display: flex; align-items: center; gap: 10px;
|
||
padding: 4px 0;
|
||
}
|
||
.learning-metric-bar {
|
||
width: 4px; height: 28px; border-radius: 2px; flex-shrink: 0;
|
||
}
|
||
.learning-metric-text {
|
||
display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0;
|
||
}
|
||
.learning-metric-text .ant-typography {
|
||
line-height: 1.15;
|
||
}
|
||
.learning-metric-value { font-size: 16px !important; }
|
||
.learning-metric-share {
|
||
font-variant-numeric: tabular-nums;
|
||
min-width: 40px; text-align: right;
|
||
}
|
||
.learning-neural-profile {
|
||
padding: 10px 0;
|
||
border-bottom: 1px solid #F1F5F9;
|
||
}
|
||
.learning-neural-profile:last-child { border-bottom: 0; padding-bottom: 0; }
|
||
.learning-neural-profile:first-child { padding-top: 0; }
|
||
.learning-neural-head {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
margin-bottom: 8px;
|
||
gap: 8px;
|
||
}
|
||
.learning-neural-name { font-family: 'JetBrains Mono', monospace; }
|