:root { --bg: #f4f6f8; --panel: #ffffff; --border: #e2e5e9; --text: #1f2430; --text-muted: #6b7280; --primary: #3860f4; --primary-dark: #2a48c9; --danger: #e5484d; --accent-bg: #eef2ff; --shadow: 0 1px 3px rgba(20, 25, 40, 0.08); --radius: 10px; } * { box-sizing: border-box; } html, body { margin: 0; padding: 0; height: 100%; background: var(--bg); color: var(--text); font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 15px; /* Safety net so nothing (the topbar's own flex row not shrinking, an overlong subject line, whatever) can force the whole page wider than the viewport on mobile - anything that still doesn't fit scrolls inside its own container instead (see .topbar's mobile rule below). */ overflow-x: hidden; } a { color: var(--primary); text-decoration: none; } a:hover { text-decoration: underline; } /* fetch-based navigation loading indicator (only shown once a request takes > 500ms) */ body.nav-loading::after { content: ""; position: fixed; inset: 0; background: rgba(15, 18, 25, 0.15); cursor: wait; z-index: 9999; } #navLoadingCancel { display: none; position: fixed; top: 16px; right: 16px; z-index: 10000; } body.nav-loading #navLoadingCancel { display: block; } .toast { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); max-width: min(90vw, 480px); background: var(--text); color: #fff; padding: 10px 16px; border-radius: var(--radius); box-shadow: var(--shadow); font-size: 13px; z-index: 9000; } .toast[hidden] { display: none; } button, .btn { font-family: inherit; font-size: 14px; padding: 9px 16px; border-radius: 8px; border: 1px solid var(--border); background: var(--panel); color: var(--text); cursor: pointer; white-space: nowrap; flex-shrink: 0; } button:hover, .btn:hover { background: var(--accent-bg); } .btn-primary { background: var(--primary); border-color: var(--primary); color: #fff; } .btn-primary:hover { background: var(--primary-dark); } .btn-text { border: none; background: transparent; color: var(--primary); padding: 6px 8px; } input[type=text], input[type=email], input[type=password], input[type=number], select, textarea { width: 100%; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; font-size: 14px; font-family: inherit; background: #fff; color: var(--text); } input:focus, select:focus, textarea:focus { outline: 2px solid var(--primary); outline-offset: -1px; } label { font-size: 13px; color: var(--text-muted); display: block; margin-bottom: 4px; margin-top: 12px; } .field-error { color: var(--danger); font-size: 13px; margin-top: 4px; } .alert { padding: 10px 14px; border-radius: 8px; margin-bottom: 12px; font-size: 14px; } .alert-error { background: #fdeceb; color: var(--danger); border: 1px solid #f6c2c2; } .alert-info { background: var(--accent-bg); color: var(--primary-dark); border: 1px solid #c7d3fb; } .sensitivity-banner { background: #fff8e1; color: #8a6d1a; border: 1px solid #f0e0a6; } /* ---------- Auth pages ---------- */ .auth-wrap { min-height: 100%; display: flex; align-items: center; justify-content: center; padding: 24px; } .auth-card { width: 100%; max-width: 420px; background: var(--panel); border-radius: var(--radius); box-shadow: var(--shadow); padding: 28px 26px; } .auth-card h1 { font-size: 22px; margin: 0 0 4px; } .auth-card p.sub { color: var(--text-muted); margin: 0 0 18px; font-size: 14px; } .auth-actions { margin-top: 20px; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; } .advanced-toggle { margin-top: 16px; font-size: 13px; } .advanced-box { display: none; border-top: 1px dashed var(--border); margin-top: 16px; padding-top: 8px; } .advanced-box.show { display: block; } .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; } /* ---------- App shell ---------- */ .app { display: flex; flex-direction: column; height: 100vh; } .topbar { position: relative; display: flex; align-items: center; gap: 10px; padding: 10px 14px; background: var(--panel); border-bottom: 1px solid var(--border); flex-shrink: 0; } .topbar .brand { font-weight: 700; font-size: 17px; margin-right: 8px; } .topbar .spacer { flex: 1; } .account-email { color: var(--text-muted); font-size: 14px; text-decoration: none; } .account-email:hover { color: var(--text); text-decoration: underline; } .lang-select { width: auto; flex-shrink: 0; } /* Desktop: just another row of inline items, same as before. Mobile (see the media query below) turns this into a collapsible dropdown panel instead - see _Topbar.cshtml for why it's grouped into its own container at all. */ .topbar-links { display: flex; align-items: center; gap: 10px; } .topbar-menu-toggle { display: none; border: none; background: transparent; font-size: 20px; padding: 4px 10px; cursor: pointer; color: var(--text); border-radius: 6px; } .topbar-menu-toggle:hover { background: var(--accent-bg); } .hamburger { display: none; border: none; background: transparent; font-size: 20px; padding: 4px 8px; } .subheader { display: flex; gap: 4px; padding: 0 14px; background: var(--panel); border-bottom: 1px solid var(--border); flex-shrink: 0; } .subheader-tab { padding: 10px 14px; color: var(--text-muted); border-bottom: 2px solid transparent; font-size: 14px; } .subheader-tab:hover { color: var(--text); text-decoration: none; } .subheader-tab.active { color: var(--primary-dark); font-weight: 600; border-bottom-color: var(--primary); } .app-body { flex: 1; display: flex; min-height: 0; } .sidebar { width: 230px; flex-shrink: 0; background: var(--panel); border-right: 1px solid var(--border); overflow-y: auto; padding: 14px 10px; } .sidebar .compose-btn { display: block; text-align: center; margin-bottom: 12px; } .folder-list { list-style: none; margin: 0; padding: 0; } .folder-list li a { display: flex; justify-content: space-between; padding: 9px 10px; border-radius: 8px; color: var(--text); } .folder-list li a:hover { background: var(--accent-bg); text-decoration: none; } .folder-list li a.active { background: var(--accent-bg); color: var(--primary-dark); font-weight: 600; } .folder-list .badge { color: var(--text-muted); font-size: 12px; } .folder-list li a.drop-target { background: var(--primary); color: #fff; outline: 2px dashed var(--primary-dark); outline-offset: -2px; } .folder-row-right { display: flex; align-items: center; gap: 6px; } .folder-refresh { color: var(--text-muted); font-size: 14px; line-height: 1; padding: 2px; border-radius: 4px; } .folder-refresh:hover { color: var(--primary-dark); background: var(--panel); } .message-row { cursor: grab; } .message-row:active { cursor: grabbing; } .content { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; } /* message list */ .list-toolbar { padding: 10px 0; border-bottom: 1px solid var(--border); background: var(--panel); } /* Horizontal padding lives on the row, not the container - every .list-toolbar-row and .message-row (see further down) uses this exact same 14px, so whichever one sets the checkbox column's left edge, they all agree. */ .list-toolbar-row { display: flex; align-items: center; gap: 10px; flex-wrap: nowrap; overflow-x: auto; padding: 0 14px 2px; } .list-toolbar-row + .list-toolbar-row { margin-top: 8px; } /* The Acties/Verplaatsen/Verwijderen selects are the one row allowed to actually wrap onto a second line on a narrow pane, rather than scrolling sideways - there's no checkbox-column alignment to preserve here, unlike the bottom row. */ .list-toolbar-row-wrap { flex-wrap: wrap; overflow-x: visible; } /* Single shared source of truth for the checkbox column's geometry, used by BOTH this row and .message-row below (they carry this exact same class) - grid-template-columns/column-gap can only be defined once, here, so the two can never drift apart the way matching flex gaps by hand kept doing. Only the fixed-width leading columns (flag icon, checkbox) need to line up. Deliberately flat - every child is a direct grid item, none of them wrapped in their own nested flex container, so align-items:center on this one grid is the only thing controlling vertical position for all of them; nesting a flex row inside a grid item left its contents centered relative to their own (different) box instead of the shared row. */ .checkbox-grid-row { display: grid; grid-template-columns: 20px 20px auto auto; column-gap: 10px; align-items: center; justify-content: start; } /* Overrides the base 4-column template with 5 - unreadOnlyToggle's checkbox and its label are now two separate flat grid items (see the markup) instead of one nested inside the other, so there's nothing left that could center relative to its own sub-container instead of this row. */ .toolbar-checkbox-row { grid-template-columns: 20px 20px 20px auto auto; } .list-toolbar .sort-links { display: flex; align-items: center; gap: 4px; font-size: 13px; } .list-toolbar .sort-links a { padding: 6px 8px; border-radius: 6px; color: var(--text-muted); } .list-toolbar .sort-links a.active { color: var(--primary-dark); font-weight: 600; background: var(--accent-bg); } .list-toolbar select { width: auto; padding: 8px 10px; font-size: 13px; flex-shrink: 0; } /* The global `label { display:block; margin-top:12px; margin-bottom:4px; }` rule (for form field labels elsewhere) still applies to any property this rule doesn't explicitly override - a class selector beats an element selector on specificity, but only for the properties it actually sets. This was silently inheriting that 12px top margin the whole time: every earlier fix (grid columns, checkbox sizing, line-height) was correct, but got undone by a form label style bleeding into an unrelated toolbar checkbox label. display/margin here override it explicitly. */ .unread-only-label { display: inline; margin: 0; font-size: 13px; color: var(--text-muted); cursor: pointer; white-space: nowrap; } /* One rule for every checkbox in both the toolbar and the message list (not two separate rules with two separate sizes) - a native, unstyled checkbox's rendered size varies by browser/OS, so the toolbar's select-all checkbox and a message row's own checkbox only ever end up the same visible size, and therefore actually centered on the same line, if they share this one declaration instead of one being explicitly sized and the other left to its native default. */ .list-toolbar-row input[type=checkbox], .message-row input[type=checkbox] { width: 15px; height: 15px; margin: 0; flex-shrink: 0; } .detail-header-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 12px; } .detail-header-main { flex: 1; min-width: 0; } .detail-toolbar { display: flex; flex-direction: column; align-items: stretch; gap: 6px; flex-shrink: 0; } .detail-toolbar select, .detail-toolbar .btn { width: 100%; padding: 8px 10px; font-size: 13px; } #messageListBody, #contactMailsBody { display: flex; flex-direction: column; flex: 1; min-height: 0; } .message-list { list-style: none; margin: 0; padding: 0; overflow-y: auto; flex: 1; background: var(--panel); } /* Extends .checkbox-grid-row's shared 20px/20px leading columns with a 4th (the right-aligned meta block) - a more specific selector than .checkbox-grid-row alone, so it only overrides grid-template-columns, not the shared display/column-gap/align-items. */ .message-row.checkbox-grid-row { grid-template-columns: 20px 20px 1fr auto; } .message-row { padding: 12px 14px; border-bottom: 1px solid var(--border); cursor: pointer; } .message-row:hover { background: var(--accent-bg); } .message-row.unread { background: #fbfcff; } .message-row.unread .subject { font-weight: 700; } .message-row.selected { background: var(--accent-bg); box-shadow: inset 3px 0 0 var(--primary); } .message-row .main { flex: 1; min-width: 0; } .message-row .subject { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .message-row .from { color: var(--text-muted); font-size: 13px; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .message-row .meta { flex-shrink: 0; text-align: right; font-size: 12px; color: var(--text-muted); min-width: 90px; } .message-row .meta .date { white-space: nowrap; } .message-row .meta .size { margin-top: 2px; } .priority-high { color: var(--danger); font-weight: 700; margin-right: 4px; } .flag-toggle { text-align: center; color: var(--border); font-size: 16px; line-height: 1; cursor: pointer; } .flag-toggle:hover { color: var(--text-muted); } .flag-toggle.flagged { color: var(--danger); } .pager { display: flex; align-items: center; justify-content: center; flex-wrap: nowrap; gap: 14px; padding: 10px 14px; border-top: 1px solid var(--border); background: var(--panel); flex-shrink: 0; } .pager .pager-info { font-size: 12px; color: var(--text-muted); } .pager .btn.disabled { pointer-events: none; opacity: 0.4; } .empty-state { padding: 40px 20px; text-align: center; color: var(--text-muted); } .btn-text.active { color: var(--primary-dark); font-weight: 600; } .settings-page { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; } .settings-wrap { max-width: 640px; margin: 0 auto; padding: 24px; width: 100%; } .settings-wrap-wide { max-width: 1000px; } .settings-section { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px 24px; margin-bottom: 20px; } .settings-section h2 { font-size: 16px; margin: 0 0 14px; } .settings-section select { width: auto; } .settings-readonly { padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg); color: var(--text-muted); } .folder-manage-toolbar { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; } .folder-manage-table-wrap { overflow-x: auto; } .folder-manage-table { width: 100%; border-collapse: collapse; font-size: 14px; } .folder-manage-table th, .folder-manage-table td { padding: 8px 10px; border-bottom: 1px solid var(--border); text-align: left; white-space: nowrap; } .folder-manage-table th:nth-child(4), .folder-manage-table td:nth-child(4), .folder-manage-table th:nth-child(5), .folder-manage-table td:nth-child(5) { text-align: right; } .folder-manage-table select { width: auto; padding: 6px 8px; font-size: 13px; } .folder-manage-table tfoot td { border-top: 2px solid var(--border); border-bottom: none; } /* Log viewer: the message is the thing being read, so it gets full width and readable size; time/ level/category/account are just orientation and stay small and out of the way. A table forces every column to the same importance - this is a plain list instead, one entry per log line. */ .logs-list { display: flex; flex-direction: column; gap: 2px; } .log-entry { padding: 8px 10px; border-bottom: 1px solid var(--border); } .log-entry-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 3px; } .log-entry-level { display: inline-block; min-width: 62px; font-weight: 600; } .log-entry-message { font-size: 14px; white-space: pre-wrap; word-break: break-word; } .log-level-Warning .log-entry-level, .log-level-Warning .log-entry-message { color: #b8860b; } .log-level-Error .log-entry-level, .log-level-Error .log-entry-message, .log-level-Critical .log-entry-level, .log-level-Critical .log-entry-message { color: #d9534f; } .log-level-Warning { background: rgba(184, 134, 11, 0.06); } .log-level-Error, .log-level-Critical { background: rgba(217, 83, 79, 0.06); } .settings-hint { color: var(--text-muted); font-size: 13px; margin-top: 12px; } .folder-manage-table .move-handle { cursor: grab; color: var(--text-muted); display: flex; gap: 2px; align-items: center; } .folder-manage-table .move-handle button { border: none; background: transparent; padding: 2px 4px; font-size: 11px; cursor: pointer; color: var(--text-muted); line-height: 1; } .folder-manage-table .move-handle button:hover { color: var(--text); } .folder-manage-table .move-handle button:disabled { opacity: 0.25; cursor: default; } .folder-manage-table tr.dragging { opacity: 0.4; } .folder-manage-table tr.drag-over { box-shadow: inset 0 2px 0 var(--primary); } .folder-manage-table tr.nest-target { background: var(--accent-bg); outline: 2px dashed var(--primary); outline-offset: -2px; } .folder-manage-table tr[data-protected="true"] .folder-row-select { visibility: hidden; } /* three-column mail layout: sidebar (folders) | message-list-pane | detail-pane */ .content { flex-direction: row; } .message-list-pane { width: 380px; flex-shrink: 0; display: flex; flex-direction: column; min-height: 0; border-right: 1px solid var(--border); background: var(--panel); } .detail-pane { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; background: var(--bg); } .detail-pane .empty-state { margin: auto; } .col-resizer { width: 6px; flex-shrink: 0; cursor: col-resize; background: transparent; position: relative; } .col-resizer::after { content: ""; position: absolute; top: 0; bottom: 0; left: 2px; width: 2px; background: transparent; } .col-resizer:hover::after, .col-resizer.resizing::after { background: var(--primary); } body.col-resizing { cursor: col-resize; user-select: none; } /* message detail */ .message-detail { padding: 20px 24px 0; flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--panel); } .message-detail .subject-line { font-size: 20px; font-weight: 700; margin-bottom: 10px; flex-shrink: 0; } .message-detail .headers { font-size: 13px; color: var(--text-muted); margin-bottom: 16px; line-height: 1.8; flex-shrink: 0; } .message-detail .headers b { color: var(--text); } .message-detail .body-html { border-top: 1px solid var(--border); padding-top: 16px; padding-bottom: 20px; line-height: 1.5; flex: 1; min-height: 0; } .message-detail .body-html iframe { width: 100%; height: 100%; border: none; display: block; } .message-detail .body-html pre { height: 100%; overflow-y: auto; margin: 0; } #contactHeader { flex-wrap: wrap; align-items: flex-start; } .contact-header-info { display: flex; flex-direction: column; gap: 2px; margin-right: auto; } .contact-header-info .name { font-weight: 600; } .contact-header-info .email { color: var(--text-muted); font-size: 13px; } .attachments { margin-top: 18px; border-top: 1px dashed var(--border); padding-top: 12px; flex-shrink: 0; } .attachment-row { display: flex; align-items: center; gap: 8px; margin: 4px 0; font-size: 13px; } .attachment-row .btn { padding: 4px 10px; font-size: 12px; } .attachments-list { margin-top: 6px; } /* compose */ .compose-wrap { padding: 18px 22px; overflow-y: auto; flex: 1; background: var(--panel); } .compose-wrap .row-inline { display: flex; gap: 14px; flex-wrap: wrap; } .compose-wrap .row-inline > div { flex: 1; min-width: 140px; max-width: 420px; } #composeForm > input[type=text] { max-width: 420px; } .editor-toolbar { display: flex; flex-wrap: wrap; gap: 4px; padding: 6px; border: 1px solid var(--border); border-bottom: none; border-radius: 8px 8px 0 0; background: #fafbfc; } .editor-toolbar button { padding: 6px 9px; border: 1px solid transparent; background: transparent; font-size: 13px; border-radius: 6px; } .editor-toolbar button:hover { background: var(--accent-bg); border-color: var(--border); } .editor-toolbar select, .editor-toolbar input[type=color] { padding: 4px 6px; width: auto; font-size: 13px; } .editor-toolbar .sep { width: 1px; background: var(--border); margin: 3px 4px; } #editor { min-height: 260px; border: 1px solid var(--border); border-radius: 0 0 8px 8px; padding: 14px; background: #fff; line-height: 1.5; } #editor:focus { outline: none; } .compose-options { display: flex; gap: 20px; flex-wrap: wrap; margin-top: 16px; align-items: center; } .checkbox-line { display: flex; align-items: center; gap: 6px; font-size: 14px; margin-top: 16px; } .checkbox-line input { width: auto; } .attach-list { margin-top: 10px; font-size: 13px; color: var(--text-muted); } .form-actions { margin-top: 20px; display: flex; gap: 10px; } /* ---------- Mobile ---------- */ @media (max-width: 860px) { .hamburger { display: inline-block; } .col-resizer { display: none; } /* Contacten/taal/Beheer/Instellingen/Uitloggen move into a dropdown behind the "⋮" button instead of trying to fit inline - the topbar itself only ever has to fit brand + hamburger + this one toggle button, so it no longer needs to scroll or shrink anything illegibly. Account email still drops entirely - redundant (the user knows who they are) and there's no room for it even in the collapsed bar. */ .account-email { display: none; } .topbar-menu-toggle { display: inline-block; } .topbar-links { display: none; position: absolute; top: 52px; right: 10px; flex-direction: column; align-items: stretch; gap: 4px; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); padding: 8px; min-width: 190px; z-index: 30; } .topbar-links.open { display: flex; } .topbar-links .lang-select { width: 100%; } .sidebar { position: fixed; top: 53px; left: 0; bottom: 0; z-index: 20; transform: translateX(-100%); transition: transform .2s ease; box-shadow: 2px 0 8px rgba(0,0,0,0.15); } .sidebar.open { transform: translateX(0); } .app-body { position: relative; } .message-row .meta { min-width: 60px; } .message-row .from { display: none; } .grid-2 { grid-template-columns: 1fr; } .compose-wrap .row-inline { flex-direction: column; } } @media (max-width: 700px) { .message-list-pane { width: 100%; border-right: none; } .detail-pane { display: none; position: absolute; inset: 0; z-index: 5; } .content { position: relative; } .content.has-detail .message-list-pane { display: none; } .content.has-detail .detail-pane { display: flex; } .mobile-back { display: inline-block !important; } } .mobile-back { display: none; }