Files
gowebmail/web/templates/app.html
T

960 lines
69 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{template "base" .}}
{{define "title"}}GoWebMail{{end}}
{{define "body_class"}}app-page{{end}}
{{define "body"}}
<div class="app" id="app-root" data-mob-view="list">
<!-- Mobile top bar (hidden on desktop) -->
<div class="mob-topbar" id="mob-topbar">
<button class="mob-nav-btn" id="mob-nav-btn" onclick="mobShowNav()" title="Menu" aria-label="Open navigation menu">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>
</button>
<button class="mob-back-btn" id="mob-back-btn" onclick="mobBack()" title="Back" aria-label="Back" style="display:none">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
</button>
<span class="mob-title" id="mob-title">GoWebMail</span>
<button class="compose-btn" onclick="openCompose()" style="margin-left:auto;padding:5px 10px;font-size:11px">+ New</button>
<button class="compose-btn" onclick="window.open('/compose','_blank')" style="padding:5px 8px;font-size:11px" title="Compose in new tab" aria-label="Compose in new tab"></button>
</div>
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<div style="display:flex;align-items:center;justify-content:space-between">
<div style="display:flex;align-items:center;gap:8px;min-width:0">
<button class="icon-btn sidebar-collapse-btn" onclick="toggleSidebarCollapse()" title="Collapse sidebar" aria-label="Collapse sidebar" style="flex-shrink:0">
<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></svg>
</button>
<div class="logo">
<div class="logo-icon"><svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg></div>
<span class="logo-text"><a href="/">GoWebMail</a></span>
</div>
</div>
</div>
<div style="display:flex;margin-top:8px">
<button class="compose-btn" onclick="openCompose()" style="flex:1;border-radius:6px 0 0 6px">+ New</button>
<button class="compose-btn" onclick="toggleComposeDropdown(event)" style="border-radius:0 6px 6px 0;border-left:1px solid rgba(255,255,255,.25);padding:6px 7px" title="More options" aria-label="More compose options" aria-haspopup="true">
<svg viewBox="0 0 24 24" width="10" height="10" fill="white"><path d="M7 10l5 5 5-5z"/></svg>
</button>
</div>
</div>
<div class="nav-section">
<div class="nav-item active" id="nav-unified" onclick="selectFolder('unified','Unified Inbox')">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
Unified Inbox
<span class="unread-badge" id="unread-total" style="display:none"></span>
</div>
<div class="nav-item" id="nav-starred" onclick="selectFolder('starred','Starred')">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>
Starred
</div>
<div class="nav-item" id="nav-snoozed" onclick="selectFolder('snoozed','Snoozed')">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 20c4.42 0 8-3.58 8-8s-3.58-8-8-8-8 3.58-8 8 3.58 8 8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67V7z"/></svg>
Snoozed
</div>
<div class="nav-item" id="nav-scheduled" onclick="showScheduledSends()">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 3h-1V1h-2v2H7V1H5v2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 18H4V8h16v13zm-8-9h5v5h-5z"/></svg>
Scheduled
</div>
<div class="nav-item" id="nav-contacts" onclick="showContacts()">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 0H4v2h16V0zM0 4v18h24V4H0zm22 16H2V6h20v14zM12 11c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3zm-6 6c0-2.21 2.69-4 6-4s6 1.79 6 4H6z"/></svg>
Contacts
</div>
<div class="nav-item" id="nav-calendar" onclick="showCalendar()">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 3h-1V1h-2v2H7V1H5v2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 18H4V8h16v13z"/></svg>
Calendar
</div>
<div id="folders-by-account"></div>
</div>
<div class="sidebar-footer">
<div class="user-info">
<span class="user-name" id="user-display">...</span>
<a href="/admin" id="admin-link" style="display:none;font-size:11px;color:var(--accent);text-decoration:none">Server Administration</a>
</div>
<div class="footer-actions">
<button class="icon-btn" onclick="openSettings()" title="Settings" aria-label="Settings">
<svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
</button>
<button class="icon-btn" onclick="doLogout()" title="Sign out" aria-label="Sign out">
<svg viewBox="0 0 24 24"><path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"/></svg>
</button>
</div>
</div>
</aside>
<!-- Mobile sidebar backdrop -->
<div class="mob-sidebar-backdrop" id="mob-sidebar-backdrop" onclick="mobCloseNav()"></div>
<!-- Mail view: message list + reading pane (position/density configurable via View menu) -->
<div class="mail-view" id="mail-view">
<div class="message-list-panel">
<div class="panel-header">
<div style="display:flex;align-items:center;gap:6px;min-width:0">
<button class="icon-btn" id="sidebar-expand-btn" onclick="toggleSidebarCollapse()" title="Show sidebar" aria-label="Show sidebar" style="flex-shrink:0">
<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6z"/></svg>
</button>
<span class="panel-title" id="panel-title">Unified Inbox</span>
</div>
<div style="display:flex;align-items:center;gap:6px">
<span class="panel-count" id="panel-count"></span>
<div class="filter-dropdown" id="view-dropdown">
<button class="filter-dropdown-btn" id="view-dropdown-btn" title="View settings" onclick="toggleViewDropdown(event)" aria-haspopup="true" aria-expanded="false">
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>
<span>View</span>
</button>
<div class="filter-dropdown-menu" id="view-dropdown-menu" style="display:none;min-width:190px">
<div style="padding:6px 12px 2px;font-size:10px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted)">Reading pane</div>
<div class="filter-opt" id="vopt-pane-right" onclick="setViewPref('readingPane','right');event.stopPropagation()">✓ Right</div>
<div class="filter-opt" id="vopt-pane-bottom" onclick="setViewPref('readingPane','bottom');event.stopPropagation()">○ Bottom</div>
<div class="filter-sep-line"></div>
<div style="padding:6px 12px 2px;font-size:10px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted)">Density</div>
<div class="filter-opt" id="vopt-density-compact" onclick="setViewPref('density','compact');event.stopPropagation()">✓ Compact</div>
<div class="filter-opt" id="vopt-density-comfortable" onclick="setViewPref('density','comfortable');event.stopPropagation()">○ Comfortable</div>
<div class="filter-sep-line"></div>
<div style="padding:6px 12px 2px;font-size:10px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted)">Sidebar</div>
<div class="filter-opt" id="vopt-sidebar-expanded" onclick="setViewPref('sidebarMode','expanded');event.stopPropagation()">✓ Pinned (always visible)</div>
<div class="filter-opt" id="vopt-sidebar-collapsed" onclick="setViewPref('sidebarMode','collapsed');event.stopPropagation()">○ Minimized</div>
<div class="filter-opt" id="vopt-sidebar-auto" onclick="setViewPref('sidebarMode','auto');event.stopPropagation()">○ Auto-hide (peek on hover)</div>
</div>
</div>
<div class="filter-dropdown" id="labels-dropdown">
<button class="filter-dropdown-btn" id="labels-dropdown-btn" title="Labels" onclick="toggleLabelsDropdown(event)" aria-haspopup="true" aria-expanded="false">
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg>
<span>Labels</span>
</button>
<div class="filter-dropdown-menu" id="labels-dropdown-menu" style="display:none;min-width:210px"></div>
</div>
<div class="filter-dropdown" id="filter-dropdown">
<button class="filter-dropdown-btn" id="filter-dropdown-btn" title="Filter &amp; sort" aria-haspopup="true" aria-expanded="false" onclick="var m=document.getElementById('filter-dropdown-menu');var exp=m.style.display==='block';m.style.display=exp?'none':'block';this.setAttribute('aria-expanded',String(!exp));event.stopPropagation()">
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/></svg>
<span id="filter-label">Filter</span>
</button>
<div class="filter-dropdown-menu" id="filter-dropdown-menu" style="display:none">
<div class="filter-opt" id="fopt-default" onclick="goMailSetFilter('default');event.stopPropagation()">✓ Default order</div>
<div class="filter-sep-line"></div>
<div class="filter-opt" id="fopt-unread" onclick="goMailSetFilter('unread');event.stopPropagation()">○ Unread only</div>
<div class="filter-opt" id="fopt-attachment" onclick="goMailSetFilter('attachment');event.stopPropagation()">○ 📎 Has attachment</div>
<div class="filter-sep-line"></div>
<div class="filter-opt" id="fopt-date-desc" onclick="goMailSetFilter('date-desc');event.stopPropagation()">○ Newest first</div>
<div class="filter-opt" id="fopt-date-asc" onclick="goMailSetFilter('date-asc');event.stopPropagation()">○ Oldest first</div>
<div class="filter-opt" id="fopt-size-desc" onclick="goMailSetFilter('size-desc');event.stopPropagation()">○ Largest first</div>
</div>
</div>
</div>
</div>
<div class="search-bar">
<div style="display:flex;gap:6px;align-items:center">
<div class="search-wrap" style="flex:1">
<svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
<input class="search-input" type="text" id="search-input" aria-label="Search emails" placeholder="Search emails..." oninput="handleSearch(this.value)" onkeydown="if(event.key==='Enter')applySearchFilters()">
</div>
<button class="filter-dropdown-btn" id="search-filters-btn" title="Search filters" aria-label="Search filters" aria-haspopup="true" onclick="toggleSearchFilters(event)" style="flex-shrink:0">
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/></svg>
</button>
</div>
</div>
<div class="message-list" id="message-list">
<div class="spinner" style="margin-top:60px"></div>
</div>
</div>
<div class="panel-resize-handle" id="panel-resize-handle" title="Drag to resize"></div>
<!-- Message detail -->
<main class="message-detail" id="message-detail">
<div class="no-message">
<svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
<h3>Select a message</h3>
<p>Choose a message from the list to read it</p>
</div>
</main>
</div>
<!-- ── Contacts panel ──────────────────────────────────────────────────── -->
<div id="contacts-panel" style="display:none;flex:1;flex-direction:column;overflow:hidden;background:var(--bg)">
<div class="panel-header" style="padding:14px 18px 10px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;flex-shrink:0">
<span style="font-family:'DM Serif Display',serif;font-size:17px;flex:1">Contacts</span>
<input id="contacts-search" type="search" placeholder="Search contacts…" oninput="filterContacts(this.value)"
style="padding:5px 10px;border:1px solid var(--border2);border-radius:6px;background:var(--surface3);color:var(--text);font-size:13px;width:200px">
<button class="btn-secondary" onclick="openContactForm()" style="font-size:12px">+ New Contact</button>
</div>
<div id="contacts-list" style="flex:1;overflow-y:auto;padding:12px"></div>
</div>
<!-- ── Calendar panel ──────────────────────────────────────────────────── -->
<div id="calendar-panel" style="display:none;flex:1;flex-direction:column;overflow:hidden;background:var(--bg)">
<div style="padding:12px 18px 10px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;flex-shrink:0">
<button class="icon-btn" onclick="calNav(-1)" title="Previous" aria-label="Previous period">&#8249;</button>
<span id="cal-title" style="font-family:'DM Serif Display',serif;font-size:17px;min-width:200px;text-align:center"></span>
<button class="icon-btn" onclick="calNav(1)" title="Next" aria-label="Next period">&#8250;</button>
<button class="btn-secondary" onclick="calGoToday()" style="font-size:12px;margin-left:4px">Today</button>
<div style="margin-left:auto;display:flex;gap:4px">
<button class="btn-secondary" id="cal-btn-month" onclick="calSetView('month')" style="font-size:12px">Month</button>
<button class="btn-secondary" id="cal-btn-week" onclick="calSetView('week')" style="font-size:12px">Week</button>
<button class="btn-secondary" onclick="openEventForm()" style="font-size:12px;background:var(--accent);color:white;border-color:var(--accent)">+ Event</button>
<button class="icon-btn" onclick="showCalDAVSettings()" title="CalDAV / sharing" aria-label="CalDAV and sharing settings">
<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/></svg>
</button>
</div>
</div>
<div id="cal-grid" style="flex:1;overflow-y:auto"></div>
</div>
</div>
<!-- ── Contact form modal ──────────────────────────────────────────────────── -->
<div class="modal-overlay" id="contact-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="contact-modal-title">
<div class="modal" style="max-width:480px">
<h2 id="contact-modal-title">New Contact</h2>
<div class="modal-field"><label>Name</label><input id="cf-name" type="text" placeholder="Full name"></div>
<div class="modal-field"><label>Email</label><input id="cf-email" type="email" placeholder="email@example.com"></div>
<div class="modal-field"><label>Phone</label><input id="cf-phone" type="tel" placeholder="+1 555 000 0000"></div>
<div class="modal-field"><label>Company</label><input id="cf-company" type="text" placeholder="Company name"></div>
<div class="modal-field"><label>Notes</label><textarea id="cf-notes" rows="3" style="width:100%;resize:vertical;padding:8px;background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px"></textarea></div>
<div class="modal-actions">
<button class="modal-cancel" onclick="closeModal('contact-modal')">Cancel</button>
<button id="cf-delete-btn" class="btn-secondary" style="color:var(--danger);display:none" onclick="deleteContact()">Delete</button>
<button class="modal-submit" onclick="saveContact()">Save</button>
</div>
</div>
</div>
<!-- ── Event form modal ──────────────────────────────────────────────────── -->
<div class="modal-overlay" id="event-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="event-modal-title">
<div class="modal" style="max-width:520px">
<h2 id="event-modal-title">New Event</h2>
<div class="modal-field"><label>Title</label><input id="ev-title" type="text" placeholder="Event title"></div>
<div class="modal-row">
<div class="modal-field"><label>Start</label><input id="ev-start" type="datetime-local"></div>
<div class="modal-field"><label>End</label><input id="ev-end" type="datetime-local"></div>
</div>
<div class="modal-field" style="flex-direction:row;align-items:center;gap:8px">
<input id="ev-allday" type="checkbox" style="width:auto">
<label for="ev-allday" style="font-weight:normal;color:var(--text2)">All day</label>
</div>
<div class="modal-field"><label>Location</label><input id="ev-location" type="text" placeholder="Location or video link"></div>
<div class="modal-field"><label>Description</label><textarea id="ev-desc" rows="3" style="width:100%;resize:vertical;padding:8px;background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px"></textarea></div>
<div class="modal-field"><label>Color</label>
<div style="display:flex;gap:6px" id="ev-colors">
<span data-color="#0078D4" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#0078D4;cursor:pointer;border:2px solid transparent"></span>
<span data-color="#EA4335" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#EA4335;cursor:pointer;border:2px solid transparent"></span>
<span data-color="#34A853" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#34A853;cursor:pointer;border:2px solid transparent"></span>
<span data-color="#FBBC04" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#FBBC04;cursor:pointer;border:2px solid transparent"></span>
<span data-color="#9C27B0" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#9C27B0;cursor:pointer;border:2px solid transparent"></span>
<span data-color="#FF6D00" onclick="selectEvColor(this)" style="width:22px;height:22px;border-radius:50%;background:#FF6D00;cursor:pointer;border:2px solid transparent"></span>
</div>
</div>
<div class="modal-actions">
<button class="modal-cancel" onclick="closeModal('event-modal')">Cancel</button>
<button id="ev-delete-btn" class="btn-secondary" style="color:var(--danger);display:none" onclick="deleteEvent()">Delete</button>
<button class="modal-submit" onclick="saveEvent()">Save</button>
</div>
</div>
</div>
<!-- ── CalDAV settings modal ──────────────────────────────────────────────── -->
<div class="modal-overlay" id="caldav-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="caldav-modal-title">
<div class="modal" style="max-width:560px">
<h2 id="caldav-modal-title">CalDAV / Calendar Sharing</h2>
<p style="font-size:13px;color:var(--text2);margin-bottom:14px">
Subscribe to your GoWebMail calendar from any CalDAV client (Apple Calendar, Thunderbird, etc.) using a token URL. Tokens give read-only calendar access — no password needed.
</p>
<div id="caldav-tokens-list" style="margin-bottom:14px"></div>
<div style="display:flex;gap:8px;align-items:center">
<input id="caldav-label" type="text" placeholder="Token label (e.g. iPhone)" style="flex:1;padding:7px 10px;background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--text);font-size:13px">
<button class="btn-secondary" onclick="createCalDAVToken()" style="white-space:nowrap">Generate Token</button>
</div>
<div class="modal-actions" style="margin-top:16px">
<button class="modal-cancel" onclick="closeModal('caldav-modal')">Close</button>
</div>
</div>
</div>
<!-- ── Draggable Compose dialog ───────────────────────────────────────────── -->
<div class="compose-dialog" id="compose-dialog">
<div class="compose-dialog-header" id="compose-drag-handle">
<span class="compose-title" id="compose-title">New Message</span>
<div style="display:flex;align-items:center;gap:2px">
<button class="compose-close" onclick="minimizeCompose()" title="Minimise" aria-label="Minimise compose window">&#8211;</button>
<button class="compose-close" onclick="closeCompose()" title="Close" aria-label="Close compose window">&#215;</button>
</div>
</div>
<div class="compose-body-wrap" id="compose-body-wrap">
<div class="compose-field"><label for="compose-from">From</label><select id="compose-from" onchange="onComposeFromChange()"></select></div>
<div class="compose-field compose-tag-field"><label id="compose-to-label">To</label><div id="compose-to" class="tag-container" role="group" aria-labelledby="compose-to-label"></div></div>
<div class="compose-field compose-tag-field" id="cc-row" style="display:none"><label id="compose-cc-label">CC</label><div id="compose-cc-tags" class="tag-container" role="group" aria-labelledby="compose-cc-label"></div></div>
<div class="compose-field compose-tag-field" id="bcc-row" style="display:none"><label id="compose-bcc-label">BCC</label><div id="compose-bcc-tags" class="tag-container" role="group" aria-labelledby="compose-bcc-label"></div></div>
<div class="compose-field"><label for="compose-subject">Subject</label><input type="text" id="compose-subject" oninput="S.draftDirty=true"></div>
<div class="compose-toolbar" role="toolbar" aria-label="Formatting">
<button class="fmt-btn" title="Bold" aria-label="Bold" onclick="execFmt('bold')"><b>B</b></button>
<button class="fmt-btn" title="Italic" aria-label="Italic" onclick="execFmt('italic')"><i>I</i></button>
<button class="fmt-btn" title="Underline" aria-label="Underline" onclick="execFmt('underline')"><u>U</u></button>
<span class="fmt-sep"></span>
<button class="fmt-btn" title="Bullets" aria-label="Bulleted list" onclick="execFmt('insertUnorderedList')">&#8226;&#8212;</button>
<button class="fmt-btn" title="Numbers" aria-label="Numbered list" onclick="execFmt('insertOrderedList')">1&#8212;</button>
<span class="fmt-sep"></span>
<button class="fmt-btn" title="Link" aria-label="Insert link" onclick="insertLink()">&#128279;</button>
<button class="fmt-btn" title="Clear format" aria-label="Clear formatting" onclick="execFmt('removeFormat')">T&#x20D7;</button>
</div>
<div id="compose-editor" contenteditable="true" role="textbox" aria-multiline="true" aria-label="Message body" class="compose-editor" placeholder="Write your message..."></div>
<div id="compose-attach-list" class="compose-attach-list"></div>
<div class="compose-footer">
<button class="send-btn" id="send-btn" onclick="sendMessage()">Send</button>
<div style="display:flex;gap:6px;margin-left:4px">
<button class="btn-secondary" style="font-size:12px" onclick="showCCRow()">+CC</button>
<button class="btn-secondary" style="font-size:12px" onclick="showBCCRow()">+BCC</button>
<button class="btn-secondary" style="font-size:12px" onclick="triggerAttach()">&#128206; Attach</button>
<button class="btn-secondary" style="font-size:12px" onclick="saveDraft()">&#9998; Draft</button>
<button class="btn-secondary" style="font-size:12px" onclick="openSendLater()">&#128339; Send later</button>
</div>
<input type="file" id="compose-attach-input" multiple style="display:none" onchange="handleAttachFiles(this)">
</div>
</div>
<div class="compose-resize" data-dir="e"></div>
<div class="compose-resize" data-dir="s"></div>
<div class="compose-resize" data-dir="se"></div>
<div class="compose-resize" data-dir="w"></div>
<div class="compose-resize" data-dir="sw"></div>
<div class="compose-resize" data-dir="n"></div>
<div class="compose-resize" data-dir="ne"></div>
<div class="compose-resize" data-dir="nw"></div>
</div>
<!-- Minimised pill (shown when user clicks on header) -->
<div class="compose-minimised" id="compose-minimised" onclick="restoreCompose()">
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
<span id="compose-minimised-label">New Message</span>
</div>
<!-- ── Inline confirm (replaces browser confirm()) ───────────────────────── -->
<div class="inline-confirm" id="inline-confirm" role="alertdialog" aria-modal="true" aria-describedby="inline-confirm-msg">
<p id="inline-confirm-msg" style="margin:0 0 14px;font-size:13px;line-height:1.5"></p>
<div style="display:flex;gap:8px;justify-content:flex-end">
<button class="btn-secondary" style="font-size:12px" id="inline-confirm-cancel">Cancel</button>
<button class="btn-danger" style="font-size:12px" id="inline-confirm-ok">Confirm</button>
</div>
</div>
<!-- ── Inline prompt (replaces browser prompt()) ─────────────────────────── -->
<div class="inline-confirm" id="inline-prompt" role="dialog" aria-modal="true" aria-describedby="inline-prompt-msg">
<p id="inline-prompt-msg" style="margin:0 0 10px;font-size:13px;line-height:1.5"></p>
<div class="modal-field">
<input type="text" id="inline-prompt-input" aria-labelledby="inline-prompt-msg"
onkeydown="if(event.key==='Enter'){document.getElementById('inline-prompt-ok').click();}else if(event.key==='Escape'){document.getElementById('inline-prompt-cancel').click();}">
</div>
<div style="display:flex;gap:8px;justify-content:flex-end">
<button class="btn-secondary" style="font-size:12px" id="inline-prompt-cancel">Cancel</button>
<button class="btn-primary" style="font-size:12px" id="inline-prompt-ok">Create</button>
</div>
</div>
<!-- ── Inline date/time prompt (snooze / send later) ──────────────────────── -->
<div class="inline-confirm" id="inline-datetime" role="dialog" aria-modal="true" aria-describedby="inline-datetime-msg">
<p id="inline-datetime-msg" style="margin:0 0 10px;font-size:13px;line-height:1.5"></p>
<div class="datetime-presets" id="inline-datetime-presets" role="group" aria-label="Quick presets"></div>
<div class="modal-field">
<input type="datetime-local" id="inline-datetime-input" aria-labelledby="inline-datetime-msg">
</div>
<div style="display:flex;gap:8px;justify-content:flex-end">
<button class="btn-secondary" style="font-size:12px" id="inline-datetime-cancel">Cancel</button>
<button class="btn-primary" style="font-size:12px" id="inline-datetime-ok">Set</button>
</div>
</div>
<!-- ── Draft close confirm (save / delete / keep editing) ─────────────────── -->
<div class="inline-confirm" id="draft-close-confirm" role="alertdialog" aria-modal="true" aria-describedby="draft-close-confirm-msg">
<p id="draft-close-confirm-msg" style="margin:0 0 14px;font-size:13px;line-height:1.5">Save this message as a draft before closing?</p>
<div style="display:flex;gap:8px;justify-content:flex-end;flex-wrap:wrap">
<button class="btn-secondary" style="font-size:12px" id="draft-close-cancel">Keep editing</button>
<button class="btn-danger" style="font-size:12px" id="draft-close-delete">Delete draft</button>
<button class="btn-primary" style="font-size:12px" id="draft-close-save">Save draft</button>
</div>
</div>
<!-- ── Scheduled Sends Modal ───────────────────────────────────────────────── -->
<div class="modal-overlay" id="scheduled-sends-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="scheduled-sends-modal-title">
<div class="modal" style="max-width:520px">
<h2 id="scheduled-sends-modal-title">Scheduled sends</h2>
<div id="scheduled-sends-list"></div>
<div class="modal-actions">
<button class="modal-cancel" onclick="closeModal('scheduled-sends-modal')">Close</button>
</div>
</div>
</div>
<!-- ── Login History modal ────────────────────────────────────────────────── -->
<div class="modal-overlay" id="login-history-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="login-history-modal-title">
<div class="modal" style="width:min(1000px,92vw);max-width:none;max-height:90vh;display:flex;flex-direction:column">
<h2 id="login-history-modal-title">Login History</h2>
<p>Login attempts for your account only.</p>
<div style="display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;margin-bottom:14px">
<div class="modal-field" style="margin-bottom:0">
<label for="lh-date-from">From</label>
<input type="date" id="lh-date-from" onchange="loadLoginHistory(1)">
</div>
<div class="modal-field" style="margin-bottom:0">
<label for="lh-date-to">To</label>
<input type="date" id="lh-date-to" onchange="loadLoginHistory(1)">
</div>
<div class="modal-field" style="margin-bottom:0">
<label for="lh-status">Status</label>
<select id="lh-status" onchange="loadLoginHistory(1)">
<option value="">All</option>
<option value="true">Success</option>
<option value="false">Failed</option>
</select>
</div>
<div class="modal-field" style="margin-bottom:0;flex:1;min-width:160px">
<label for="lh-ip">IP contains</label>
<input type="text" id="lh-ip" placeholder="e.g. 192.168" oninput="debouncedLoadLoginHistory()">
</div>
<div class="modal-field" style="margin-bottom:0">
<label for="lh-sort">Sort by date</label>
<select id="lh-sort" onchange="loadLoginHistory(1)">
<option value="desc">Newest first</option>
<option value="asc">Oldest first</option>
</select>
</div>
</div>
<div style="flex:1;overflow-y:auto;border:1px solid var(--border);border-radius:8px;min-height:200px">
<table class="data-table">
<thead><tr><th>Time</th><th>Status</th><th>IP Address</th><th>Detail</th></tr></thead>
<tbody id="lh-table-body"></tbody>
</table>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:12px">
<span id="lh-page-info" style="font-size:12px;color:var(--muted)"></span>
<div style="display:flex;gap:8px">
<button class="btn-secondary" id="lh-prev-btn" onclick="loginHistoryPrevPage()">Previous</button>
<button class="btn-secondary" id="lh-next-btn" onclick="loginHistoryNextPage()">Next</button>
</div>
</div>
<div class="modal-actions">
<button class="modal-cancel" onclick="closeModal('login-history-modal')">Close</button>
</div>
</div>
</div>
<!-- ── Spam Block modal ───────────────────────────────────────────────────── -->
<div class="modal-overlay" id="spam-block-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="spam-block-modal-title">
<div class="modal" style="width:min(700px,92vw);max-width:none;max-height:90vh;display:flex;flex-direction:column">
<h2 id="spam-block-modal-title">Spam Block</h2>
<p>Mail from these senders is automatically moved to Spam when it arrives — no notification is shown for it. Enter either a full email address, or just a domain (e.g. "example.com") to block every address at that domain and its subdomains.</p>
<div style="display:flex;gap:8px;margin-bottom:14px">
<input type="text" id="sb-add-input" placeholder="Email address or domain (e.g. example.com)…" style="flex:1;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none">
<button class="btn-primary" onclick="addSpamBlockEntry()">Block</button>
</div>
<div style="flex:1;overflow-y:auto;border:1px solid var(--border);border-radius:8px;min-height:200px">
<table class="data-table">
<thead><tr><th>Sender</th><th>Blocked since</th><th></th></tr></thead>
<tbody id="sb-table-body"></tbody>
</table>
</div>
<div class="modal-actions">
<button class="modal-cancel" onclick="closeModal('spam-block-modal')">Close</button>
</div>
</div>
</div>
<!-- ── Add Account Modal ──────────────────────────────────────────────────── -->
<div class="modal-overlay" id="add-account-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="add-account-modal-title">
<div class="modal">
<h2 id="add-account-modal-title">Connect an account</h2>
<p>Connect Gmail or Outlook via OAuth, or any email via IMAP/SMTP.</p>
<div class="provider-btns">
<button class="provider-btn" id="btn-gmail" onclick="connectOAuth('gmail')">
<svg viewBox="0 0 24 24" width="20" height="20"><path fill="#EA4335" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path fill="#4285F4" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>
Gmail
</button>
<button class="provider-btn" id="btn-outlook" onclick="connectOAuth('outlook')">
<!-- Microsoft 365 icon -->
<svg viewBox="0 0 24 24" width="20" height="20" xmlns="http://www.w3.org/2000/svg">
<path fill="#EA3E23" d="M11.4 4H4v7.4h7.4V4z"/>
<path fill="#0364B8" d="M11.4 12.6H4V20h7.4v-7.4z"/>
<path fill="#0078D4" d="M20 4h-7.4v7.4H20V4z"/>
<path fill="#28A8E8" d="M20 12.6h-7.4V20H20v-7.4z"/>
</svg>
Microsoft 365
</button>
<button class="provider-btn" id="btn-outlook-personal" onclick="connectOAuth('outlook_personal')">
<!-- Outlook icon (blue envelope) -->
<svg viewBox="0 0 24 24" width="20" height="20" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" rx="3" fill="#0078D4"/>
<path fill="white" d="M6 7h12v10H6z" opacity=".2"/>
<path fill="white" d="M6 7l6 5 6-5H6zm0 1.5V17h12V8.5l-6 5-6-5z"/>
</svg>
Outlook Personal
</button>
</div>
<div class="modal-divider"><span>or add IMAP account</span></div>
<div class="modal-field"><label>Email Address</label>
<div style="display:flex;gap:8px;flex:1">
<input type="email" id="imap-email" placeholder="you@example.com" style="flex:1">
<button class="btn-secondary" id="detect-btn" onclick="detectMailSettings()" style="white-space:nowrap;font-size:12px">Auto-detect</button>
</div>
</div>
<div class="modal-field"><label>Display Name</label><input type="text" id="imap-name" placeholder="Your Name"></div>
<div class="modal-field"><label>Password / App Password</label><input type="password" id="imap-password"></div>
<div class="modal-field" style="display:flex;align-items:center;gap:8px;flex-direction:row">
<input type="checkbox" id="use-jmap" onchange="toggleJMAPFields()" style="width:auto;flex:none">
<label for="use-jmap" style="margin:0;font-weight:400">Connect via JMAP instead of IMAP/SMTP</label>
</div>
<div id="imap-hint" style="font-size:11px;color:var(--muted);padding:0 0 8px;line-height:1.6">
Common ports — IMAP: <strong>993</strong> TLS/SSL, <strong>143</strong> STARTTLS/Plain &nbsp;·&nbsp;
SMTP: <strong>587</strong> STARTTLS, <strong>465</strong> TLS/SSL, <strong>25</strong> Plain
</div>
<div class="modal-row">
<div class="modal-field"><label id="imap-host-label">IMAP Host</label><input type="text" id="imap-host" placeholder="imap.example.com"></div>
<div class="modal-field" id="imap-port-field"><label>IMAP Port</label><input type="number" id="imap-port" value="993"></div>
</div>
<div class="modal-row" id="smtp-fields">
<div class="modal-field"><label>SMTP Host</label><input type="text" id="smtp-host" placeholder="smtp.example.com"></div>
<div class="modal-field"><label>SMTP Port</label><input type="number" id="smtp-port" value="587"></div>
</div>
<div class="modal-divider"><span>optional — sync calendar &amp; contacts</span></div>
<div class="modal-field"><label>CalDAV URL</label><input type="text" id="imap-caldav-url" placeholder="https://mail.example.com/dav/calendars/user@example.com/default"></div>
<div class="modal-field"><label>CardDAV URL</label><input type="text" id="imap-carddav-url" placeholder="https://mail.example.com/dav/addressbooks/user@example.com/default"></div>
<div class="test-result" id="test-result"></div>
<div class="modal-actions">
<button class="modal-cancel" onclick="closeModal('add-account-modal')">Cancel</button>
<button class="btn-secondary" onclick="testNewConnection()" id="test-btn">Test Connection</button>
<button class="modal-submit" onclick="addIMAPAccount()" id="save-acct-btn">Connect</button>
</div>
</div>
</div>
<!-- ── Label Editor Modal (create/rename/recolor) ────────────────────────────── -->
<div class="modal-overlay" id="label-editor-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="label-editor-title">
<div class="modal" style="max-width:360px">
<h2 id="label-editor-title">New Label</h2>
<input type="hidden" id="label-editor-id">
<div class="modal-field"><label>Name</label><input type="text" id="label-editor-name" maxlength="40"></div>
<div class="modal-field">
<label>Color</label>
<div class="label-swatches" id="label-editor-swatches"></div>
<input type="color" id="label-editor-custom-color" onchange="pickLabelColor(this.value)" style="width:40px;height:28px;padding:0;border:1px solid var(--border);border-radius:6px;background:none;cursor:pointer">
</div>
<div class="modal-actions">
<button class="modal-cancel" onclick="closeModal('label-editor-modal')">Cancel</button>
<button class="modal-submit" onclick="saveLabelEditor()">Save</button>
</div>
</div>
</div>
<!-- ── Edit Account Modal ─────────────────────────────────────────────────── -->
<div class="modal-overlay" id="edit-account-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="edit-account-modal-title">
<div class="modal">
<h2 id="edit-account-modal-title">Account Settings</h2>
<p id="edit-account-email" style="font-weight:500;color:var(--text);margin-bottom:16px"></p>
<input type="hidden" id="edit-account-id">
<div class="modal-field"><label>Display Name</label><input type="text" id="edit-name"></div>
<!-- OAuth reconnect — shown only for gmail/outlook accounts -->
<div id="edit-oauth-section" style="display:none">
<div id="edit-oauth-expired-warning" style="display:none;background:rgba(239,68,68,.12);border:1px solid rgba(239,68,68,.35);border-radius:8px;padding:10px 14px;margin-bottom:10px;font-size:13px;color:#f87171">
⚠️ Access token has expired — sync and send will fail until you reconnect.
</div>
<div style="background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:14px 16px;margin-bottom:4px">
<div style="font-size:13px;color:var(--muted);margin-bottom:10px">This account connects via <strong id="edit-oauth-provider-label"></strong> OAuth. To update permissions or fix an expired token, reconnect below.</div>
<button class="btn-secondary" id="edit-oauth-reconnect-btn" style="width:100%">🔗 Reconnect with <span id="edit-oauth-provider-label-btn"></span></button>
</div>
</div>
<!-- IMAP/SMTP credentials (hidden for OAuth accounts) -->
<div id="edit-creds-section">
<div class="modal-field"><label>New Password (leave blank to keep current)</label><input type="password" id="edit-password"></div>
<div class="modal-row">
<div class="modal-field"><label id="edit-imap-host-label">IMAP Host</label><input type="text" id="edit-imap-host"></div>
<div class="modal-field" id="edit-imap-port-field"><label>IMAP Port</label><input type="number" id="edit-imap-port"></div>
</div>
<div class="modal-row" id="edit-smtp-fields">
<div class="modal-field"><label>SMTP Host</label><input type="text" id="edit-smtp-host"></div>
<div class="modal-field"><label>SMTP Port</label><input type="number" id="edit-smtp-port"></div>
</div>
</div>
<div class="settings-group-title" style="margin:16px 0 8px">Calendar &amp; Contacts (optional)</div>
<div class="modal-field"><label>CalDAV URL</label><input type="text" id="edit-caldav-url" placeholder="leave blank to disable"></div>
<div class="modal-field"><label>CardDAV URL</label><input type="text" id="edit-carddav-url" placeholder="leave blank to disable"></div>
<div class="settings-group-title" style="margin:16px 0 8px">Sync Settings</div>
<div class="modal-field">
<label>Email history to sync</label>
<select id="edit-sync-mode" onchange="toggleSyncDaysField()" style="padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px;outline:none">
<option value="preset-30">Last 1 month</option>
<option value="preset-90">Last 3 months</option>
<option value="preset-180">Last 6 months</option>
<option value="preset-365">Last 1 year</option>
<option value="preset-730">Last 2 years</option>
<option value="preset-1825">Last 5 years</option>
<option value="all" selected>All emails (full mailbox)</option>
<option value="days">Custom (days)</option>
</select>
</div>
<div class="modal-row" id="edit-sync-days-row" style="display:none">
<div class="modal-field"><label>Custom days to fetch</label><input type="number" id="edit-sync-days" value="30" min="1" max="36500"></div>
</div>
<div id="edit-conn-result" class="test-result" style="display:none"></div>
<div id="edit-last-error" style="display:none" class="alert error"></div>
<div class="settings-group-title" style="margin:16px 0 8px">Hidden Folders</div>
<div id="edit-hidden-folders" style="font-size:12px;color:var(--muted)">Loading…</div>
<div class="modal-actions">
<button class="modal-cancel" onclick="closeModal('edit-account-modal')">Cancel</button>
<button class="btn-secondary" id="edit-test-btn" onclick="testEditConnection()">Test Connection</button>
<button class="modal-submit" onclick="saveAccountEdit()">Save</button>
</div>
</div>
</div>
<!-- ── Settings Modal ─────────────────────────────────────────────────────── -->
<div class="modal-overlay" id="settings-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="settings-modal-title">
<div class="modal" style="width:820px;max-width:95vw;height:640px;max-height:90vh;padding:0;display:flex;flex-direction:column">
<div style="display:flex;align-items:center;justify-content:space-between;padding:22px 24px 16px">
<h2 id="settings-modal-title" style="margin-bottom:0">Settings</h2>
<button onclick="closeModal('settings-modal')" class="icon-btn" aria-label="Close settings"><svg viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></button>
</div>
<div style="display:flex;align-items:stretch;min-height:0;flex:1;border-top:1px solid var(--border)">
<div class="settings-nav" role="tablist" aria-label="Settings sections">
<button data-tab="accounts" class="active" role="tab" aria-selected="true" onclick="showSettingsTab('accounts')">Accounts</button>
<button data-tab="general" role="tab" aria-selected="false" onclick="showSettingsTab('general')">General</button>
<button data-tab="security" role="tab" aria-selected="false" onclick="showSettingsTab('security')">Security</button>
<button data-tab="account" role="tab" aria-selected="false" onclick="showSettingsTab('account')">Profile</button>
<button data-tab="rules" role="tab" aria-selected="false" onclick="showSettingsTab('rules')">Rules</button>
<button data-tab="signatures" role="tab" aria-selected="false" onclick="showSettingsTab('signatures')">Signatures</button>
<button data-tab="certs" role="tab" aria-selected="false" onclick="showSettingsTab('certs')">Certificates</button>
</div>
<div style="flex:1;min-width:0;overflow-y:auto;padding:20px 24px">
<div class="settings-panel active" data-tab="accounts" role="tabpanel">
<div class="settings-group">
<div class="settings-group-title">Connected mailboxes</div>
<div style="font-size:12px;color:var(--muted);margin-bottom:10px">Manage sync, credentials, CalDAV/CardDAV and per-account settings for each connected mailbox.</div>
<div id="settings-accounts-list"></div>
<button class="accounts-add-btn" onclick="openAddAccountModal()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
Connect new account
</button>
</div>
</div>
<div class="settings-panel" data-tab="general" role="tabpanel">
<div class="settings-group">
<div class="settings-group-title">Email Sync</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:12px">How often to automatically check all your accounts for new mail.</div>
<div style="display:flex;gap:10px;align-items:center">
<select id="sync-interval-select" style="flex:1;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none">
<option value="0">Manual only</option>
<option value="1">Every 1 minute</option>
<option value="5">Every 5 minutes</option>
<option value="10">Every 10 minutes</option>
<option value="15">Every 15 minutes (default)</option>
<option value="30">Every 30 minutes</option>
<option value="60">Every 60 minutes</option>
</select>
<button class="btn-primary" onclick="saveSyncInterval()">Save</button>
</div>
</div>
<div class="settings-group">
<div class="settings-group-title">Remote Images</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:12px">Control when images and other remote content in emails load automatically. Blocking prevents senders from using tracking pixels to detect that you've opened a message.</div>
<div class="modal-field">
<label for="remote-image-policy-select">Policy</label>
<select id="remote-image-policy-select" onchange="saveRemoteImagePolicy()" style="width:100%;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none">
<option value="always">Always render images</option>
<option value="contacts">Only from Contacts</option>
<option value="never">Never</option>
<option value="manual">Manually (needs allowing)</option>
</select>
</div>
<div class="modal-field">
<label>Allowed senders</label>
<div id="remote-whitelist-list" style="font-size:12px;color:var(--muted)">Loading…</div>
</div>
</div>
<div class="settings-group">
<div class="settings-group-title">Notifications</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:12px">Show a browser notification when new mail arrives, even while GoWebMail is in a background tab.</div>
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;color:var(--text)">
<input type="checkbox" id="notifications-toggle" onchange="toggleNotifications(this.checked)" style="width:auto">
Enable desktop notifications
</label>
<div id="notifications-status" style="font-size:12px;color:var(--muted);margin-top:8px"></div>
</div>
</div>
<div class="settings-panel" data-tab="security" role="tabpanel">
<div class="settings-group">
<div class="settings-group-title">IP Access Rules</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:14px">
Control which IP addresses can access your account. This overrides global brute-force settings for your account only.
</div>
<div class="modal-field">
<label>Mode</label>
<select id="ip-rule-mode" onchange="toggleIPRuleHelp()" style="width:100%;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none">
<option value="disabled">Disabled — use global settings</option>
<option value="brute_skip">Skip brute-force check — listed IPs bypass lockout</option>
<option value="allow_only">Allow only — only listed IPs can log in</option>
</select>
</div>
<div id="ip-rule-help" style="font-size:12px;color:var(--muted);margin-bottom:10px;display:none"></div>
<div class="modal-field" id="ip-rule-list-field">
<label>Allowed IPs <span style="color:var(--muted);font-size:11px">(comma-separated)</span></label>
<input type="text" id="ip-rule-list" placeholder="e.g. 192.168.1.10, 10.0.0.5">
</div>
<button class="btn-primary" onclick="saveIPRules()">Save IP Rules</button>
</div>
<div class="settings-group">
<div class="settings-group-title">Login History</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:12px">View login attempts for your account only — successful and failed, with timestamps and source IPs.</div>
<button class="btn-secondary" onclick="openLoginHistory()">View Login History</button>
</div>
<div class="settings-group">
<div class="settings-group-title">Spam Block</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:12px">Senders blocked here are automatically moved to Spam as soon as new mail from them arrives, across all your connected accounts — no notification is shown for it.</div>
<button class="btn-secondary" onclick="openSpamBlock()">Manage Spam Block List</button>
</div>
</div>
<div class="settings-panel" data-tab="account" role="tabpanel">
<div class="settings-group">
<div class="settings-group-title">Profile</div>
<div class="modal-field">
<label>Username</label>
<div style="display:flex;gap:8px">
<input type="text" id="profile-username" placeholder="New username" style="flex:1">
<button class="btn-primary" onclick="updateProfile('username')">Save</button>
</div>
</div>
<div class="modal-field">
<label>Email Address</label>
<div style="display:flex;gap:8px">
<input type="email" id="profile-email" placeholder="New email address" style="flex:1">
<button class="btn-primary" onclick="updateProfile('email')">Save</button>
</div>
</div>
<div class="modal-field">
<label>Current Password <span style="color:var(--muted);font-size:11px">(required to confirm changes)</span></label>
<input type="password" id="profile-confirm-pw" placeholder="Enter your current password">
</div>
</div>
<div class="settings-group">
<div class="settings-group-title">Change Password</div>
<div class="modal-field"><label>Current Password</label><input type="password" id="cur-pw"></div>
<div class="modal-field"><label>New Password</label><input type="password" id="new-pw" placeholder="Min. 8 characters"></div>
<button class="btn-primary" onclick="changePassword()">Update Password</button>
</div>
<div class="settings-group">
<div class="settings-group-title" style="display:flex;align-items:center;gap:10px">
Two-Factor Authentication <span id="mfa-badge"></span>
</div>
<div id="mfa-panel">Loading...</div>
</div>
</div>
<div class="settings-panel" data-tab="rules" role="tabpanel">
<div class="settings-group">
<div class="settings-group-title">Rules apply to</div>
<select id="rules-account-select" onchange="loadRules()" style="width:100%;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none"></select>
</div>
<div class="settings-group">
<div class="settings-group-title">Add Rule</div>
<div style="font-size:12px;color:var(--muted);margin-bottom:12px">Rules run in priority order (lowest first) against newly-synced mail; the first match wins.</div>
<div class="modal-field"><label>Rule name</label><input type="text" id="rule-name" placeholder="e.g. Invoices to Accounting"></div>
<div style="display:flex;gap:8px">
<div class="modal-field" style="flex:1"><label>Priority</label><input type="number" id="rule-priority" value="0"></div>
<div class="modal-field" style="flex:1"><label>Match</label>
<select id="rule-match-type"><option value="all">ALL of the following (AND)</option><option value="any">ANY of the following (OR)</option></select>
</div>
</div>
<div id="rule-conditions"></div>
<button class="btn-secondary" style="margin-bottom:14px" onclick="addRuleConditionRow()">+ Add condition</button>
<div style="display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap">
<div class="modal-field" style="flex:1;min-width:140px"><label>Then</label>
<select id="rule-action" onchange="updateRuleActionFields()">
<option value="move_to_folder">Move to folder</option>
<option value="mark_as_spam">Mark as Junk</option>
<option value="delete">Delete</option>
<option value="mark_read">Mark as read</option>
<option value="forward">Forward to...</option>
<option value="auto_reply">Send auto-reply</option>
</select>
</div>
<div class="modal-field" style="flex:1;min-width:160px" id="rule-action-value-field"><label>Folder name</label><input type="text" id="rule-action-value" placeholder="folder name"></div>
</div>
<div class="modal-field" id="rule-autoreply-body-field" style="display:none"><label>Auto-reply body</label><textarea id="rule-autoreply-body" rows="3" style="width:100%"></textarea></div>
<button class="btn-primary" onclick="saveRule()">Add Rule</button>
</div>
<div class="settings-group">
<div class="settings-group-title">Existing Rules</div>
<div id="rules-list"><p style="color:var(--muted);font-size:13px">No rules yet.</p></div>
</div>
</div>
<div class="settings-panel" data-tab="signatures" role="tabpanel">
<div class="settings-group">
<div class="settings-group-title" id="sig-form-title">Add Signature</div>
<div class="modal-field"><label for="sig-name">Name</label><input type="text" id="sig-name" placeholder="e.g. Work"></div>
<div class="modal-field">
<label for="sig-content">Content</label>
<div class="compose-toolbar" role="toolbar" aria-label="Signature formatting" style="margin-bottom:6px">
<button type="button" class="fmt-btn" title="Bold" aria-label="Bold" onclick="execSigFmt('bold')"><b>B</b></button>
<button type="button" class="fmt-btn" title="Italic" aria-label="Italic" onclick="execSigFmt('italic')"><i>I</i></button>
<button type="button" class="fmt-btn" title="Underline" aria-label="Underline" onclick="execSigFmt('underline')"><u>U</u></button>
<span class="fmt-sep"></span>
<label class="fmt-btn" title="Text color" aria-label="Text color" style="cursor:pointer;position:relative">
🎨<input type="color" id="sig-color-input" style="position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer" onchange="execSigFmt('foreColor', this.value)">
</label>
<span class="fmt-sep"></span>
<button type="button" class="fmt-btn" title="Link" aria-label="Insert link" onclick="insertLink('sig-content')">&#128279;</button>
<button type="button" class="fmt-btn" title="Image" aria-label="Insert image" onclick="document.getElementById('sig-image-input').click()">&#128247;</button>
<button type="button" class="fmt-btn" title="Clear format" aria-label="Clear formatting" onclick="execSigFmt('removeFormat')">T&#x20D7;</button>
<input type="file" id="sig-image-input" accept="image/*" style="display:none" onchange="insertSigImage(this)">
</div>
<div id="sig-content" contenteditable="true" role="textbox" aria-multiline="true" aria-label="Signature content"
style="width:100%;min-height:110px;padding:10px;background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;line-height:1.5;overflow-y:auto"></div>
</div>
<div style="display:flex;gap:8px">
<button class="btn-primary" id="sig-save-btn" onclick="saveSignature()">Add Signature</button>
<button class="btn-secondary" id="sig-cancel-btn" onclick="cancelSignatureEdit()" style="display:none">Cancel</button>
</div>
</div>
<div class="settings-group">
<div class="settings-group-title">Your Signatures</div>
<div id="signatures-list"><p style="color:var(--muted);font-size:13px">No signatures yet.</p></div>
</div>
<div class="settings-group">
<div class="settings-group-title">Defaults per account</div>
<select id="sig-defaults-account-select" onchange="renderSignatureDefaultsForm()" style="width:100%;margin-bottom:10px;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none"></select>
<div id="sig-defaults-form"></div>
</div>
</div>
<div class="settings-panel" data-tab="certs" role="tabpanel">
<div class="settings-group">
<div class="settings-group-title">Certificates apply to</div>
<select id="certs-account-select" onchange="loadCerts()" style="width:100%;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none"></select>
<div style="font-size:12px;color:var(--muted);margin-top:10px">S/MIME certificates <b>sign</b> outgoing mail. PGP keys <b>encrypt</b> it. A message can use either, both, or neither.</div>
</div>
<div class="settings-group">
<div class="settings-group-title">S/MIME — for signing</div>
<div id="smime-identity-list"></div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin:10px 0">
<button class="btn-primary" onclick="smimeGenerate()">Generate Self-Signed Certificate</button>
</div>
<div class="modal-field"><label>Import existing (.p12/.pfx)</label>
<input type="file" id="smime-import-file" accept=".p12,.pfx">
<input type="password" id="smime-import-password" placeholder=".p12 export password (if any)" style="margin-top:6px">
<button class="btn-secondary" style="margin-top:6px" onclick="smimeImport()">Import</button>
</div>
<div class="settings-group-title" style="margin-top:16px;font-size:13px">S/MIME contact certificates</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px">
<input type="email" id="smime-contact-email" placeholder="someone@example.com" style="flex:1;min-width:160px">
<input type="file" id="smime-contact-file" accept=".pem,.crt,.cer">
<button class="btn-secondary" onclick="smimeAddContact()">Add Contact</button>
</div>
<div id="smime-contacts-list"></div>
</div>
<div class="settings-group">
<div class="settings-group-title">PGP — for encryption</div>
<div id="pgp-identity-list"></div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin:10px 0">
<input type="text" id="pgp-gen-label" placeholder="Label (optional)" style="flex:1;min-width:120px">
<input type="password" id="pgp-gen-pass" placeholder="Passphrase (min 8 chars)" style="flex:1;min-width:140px">
<input type="password" id="pgp-gen-pass2" placeholder="Confirm passphrase" style="flex:1;min-width:140px">
<button class="btn-primary" onclick="pgpGenerate()">Generate PGP Key</button>
</div>
<div class="modal-field"><label>Import existing (.asc)</label>
<input type="file" id="pgp-import-file" accept=".asc">
<input type="password" id="pgp-import-pass" placeholder="The key's passphrase" style="margin-top:6px">
<button class="btn-secondary" style="margin-top:6px" onclick="pgpImport()">Import</button>
</div>
<div class="settings-group-title" style="margin-top:16px;font-size:13px">PGP contact keys</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px">
<input type="email" id="pgp-contact-email" placeholder="someone@example.com" style="flex:1;min-width:160px">
<input type="text" id="pgp-contact-label" placeholder="Label (optional)" style="flex:1;min-width:100px">
<input type="file" id="pgp-contact-file" accept=".asc">
<button class="btn-secondary" onclick="pgpAddContact()">Add Contact</button>
</div>
<div id="pgp-contacts-list"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Compose split-button dropdown — fixed-position, JS-placed (see toggleComposeDropdown);
lives outside .sidebar so its overflow:hidden can't clip it -->
<div id="compose-dropdown" style="display:none;position:fixed;background:var(--surface);border:1px solid var(--border2);border-radius:7px;box-shadow:0 4px 16px rgba(0,0,0,.2);z-index:210;min-width:200px;overflow:hidden">
<div class="ctx-item" onclick="openCompose();closeComposeDropdown()">✉ New message</div>
<div class="ctx-item" onclick="window.open('/compose','_blank');closeComposeDropdown()">↗ New message in new tab</div>
</div>
<!-- Search filters popover — fixed-position, JS-placed under the search bar (see
toggleSearchFilters); same reasoning as #compose-dropdown above. -->
<div id="search-filters-panel" style="display:none;position:fixed;padding:10px;background:var(--surface2);border:1px solid var(--border2);border-radius:8px;box-shadow:0 8px 28px rgba(0,0,0,.5);z-index:210;max-height:80vh;overflow-y:auto">
<div class="modal-field" style="margin-bottom:8px">
<label>Search in mailbox</label>
<select id="sf-mailbox-scope" style="width:100%;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
<option value="">All mailboxes</option>
</select>
</div>
<div class="modal-field" style="margin-bottom:8px">
<label>Search in</label>
<select id="sf-scope" style="width:100%;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
<option value="all">All fields</option>
<option value="subject">Subject only</option>
<option value="body">Body only</option>
<option value="subject_body">Subject + Body</option>
</select>
</div>
<div class="modal-field" style="margin-bottom:8px">
<label>Attachment</label>
<select id="sf-attachment" style="width:100%;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
<option value="">Any</option>
<option value="1">Has attachment</option>
<option value="0">No attachment</option>
</select>
</div>
<div class="modal-row" style="margin-bottom:8px;gap:8px">
<div class="modal-field" style="flex:1;margin-bottom:0"><label>From date</label>
<input type="date" id="sf-date-from" style="width:100%;padding:5px 6px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
</div>
<div class="modal-field" style="flex:1;margin-bottom:0"><label>To date</label>
<input type="date" id="sf-date-to" style="width:100%;padding:5px 6px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
</div>
</div>
<div class="modal-field" style="margin-bottom:8px">
<label>Older than (days) <span style="color:var(--muted);font-size:10px">— fills in "To date"</span></label>
<input type="number" id="sf-older-days" min="0" placeholder="e.g. 30" style="width:100%;padding:5px 6px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
</div>
<div class="modal-row" style="margin-bottom:10px;gap:8px">
<div class="modal-field" style="flex:1;margin-bottom:0"><label>Min size (KB)</label>
<input type="number" id="sf-min-size" min="0" style="width:100%;padding:5px 6px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
</div>
<div class="modal-field" style="flex:1;margin-bottom:0"><label>Max size (KB)</label>
<input type="number" id="sf-max-size" min="0" style="width:100%;padding:5px 6px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px">
</div>
</div>
<div style="display:flex;gap:6px;justify-content:flex-end">
<button class="btn-secondary" style="font-size:12px" onclick="clearSearchFilters()">Clear</button>
<button class="btn-primary" style="font-size:12px" onclick="applySearchFilters()">Apply</button>
</div>
</div>
<!-- Context menu -->
<div class="ctx-menu" id="ctx-menu"></div>
<div class="toast-container" id="toast-container" role="status" aria-live="polite" aria-atomic="true"></div>
{{end}}
{{define "scripts"}}
<script src="/static/js/app.js?v=91"></script>
<script src="/static/js/contacts_calendar.js?v=79"></script>
{{end}}