/* 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; }