/* Copyright 2026 The M8SH Authors. */ /* SPDX-License-Identifier: GPL-3.0 */ /* The mail page fills the viewport below the navbar and above the footer. The body is a flex column: [navbar wrapper .full.height] then [footer]. .full.height has flex-grow:1, so it already fills everything except the footer. We make it a flex container so the mail page can flex:1 within it, sitting below the navbar. */ /* When the mail page is active, make .full.height a column flex container so the navbar stays fixed at top and the mail page fills the remaining height. */ .full.height:has(.mail-page) { display: flex; flex-direction: column; min-height: 0; padding-bottom: 0; /* mail manages its own bottom spacing */ } .page-content.mail-page { flex: 1; min-height: 0; padding: 0; display: flex; flex-direction: column; } .page-content.mail-page > #mail-app { flex: 1; min-height: 0; } /* On mobile, hide the footer so the mail app uses the full viewport height. */ @media (max-width: 639.98px) { .full.height:has(.mail-page) ~ .page-footer { display: none; } } /* In-thread search highlight. Global (not scoped) because it must reach nodes produced via v-html inside bubbles and html cards. */ #mail-app mark.search-hit { background: var(--color-highlight-bg); color: var(--color-text); border-radius: 2px; padding: 0 1px; } #mail-app mark.search-hit.current { outline: 1px solid var(--color-highlight-fg); } /* Thread context menu. It is teleported to (outside the Vue scoped component subtree), so its styles live here, unscoped. */ body > .ctx-menu { position: fixed; z-index: 1002; min-width: 140px; background: var(--color-menu); border: 0.5px solid var(--color-secondary-alpha-30); border-radius: var(--border-radius-medium); padding: 4px; display: flex; flex-direction: column; gap: 2px; } body > .ctx-menu .ctx-item { display: flex; align-items: center; gap: 8px; border: none; background: transparent; color: var(--color-text); font-size: 13px; padding: 7px 10px; border-radius: var(--border-radius); cursor: pointer; text-align: left; min-height: 36px; } body > .ctx-menu .ctx-item:hover { background: var(--color-hover); } body > .ctx-menu .ctx-item.danger:hover { color: var(--color-danger); } /* Delete conversation confirmation dialog (teleported, so unscoped). */ body > .confirm-overlay { position: fixed; inset: 0; z-index: 1003; background: var(--color-overlay-backdrop); display: flex; align-items: center; justify-content: center; padding: 16px; } body > .confirm-overlay .confirm-modal { width: 100%; max-width: 360px; background: var(--color-menu); border: 0.5px solid var(--color-secondary-alpha-30); border-radius: var(--border-radius-medium); padding: 20px; display: flex; flex-direction: column; align-items: center; gap: 12px; } body > .confirm-overlay .confirm-icon { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: var(--border-radius-full); background: var(--color-red-badge-bg); color: var(--color-danger); } body > .confirm-overlay .confirm-title { font-size: 15px; font-weight: var(--font-weight-medium); color: var(--color-text); } body > .confirm-overlay .confirm-desc { font-size: 13px; line-height: 1.4; color: var(--color-text-light-2); text-align: center; } body > .confirm-overlay .confirm-actions { display: flex; gap: 8px; width: 100%; margin-top: 4px; } body > .confirm-overlay .confirm-cancel { flex: 1; display: inline-flex; align-items: center; justify-content: center; border: 0.5px solid var(--color-secondary-alpha-30); background: var(--color-body); color: var(--color-text); font-size: 13px; padding: 8px 12px; border-radius: var(--border-radius-medium); cursor: pointer; min-height: 38px; } body > .confirm-overlay .confirm-cancel:hover { background: var(--color-hover); } body > .confirm-overlay .confirm-delete { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 6px; border: none; background: var(--color-danger); color: var(--color-white); font-size: 13px; padding: 8px 12px; border-radius: var(--border-radius-medium); cursor: pointer; min-height: 38px; } body > .confirm-overlay .confirm-delete:hover { opacity: 0.9; } /* Block toggle row: a right-aligned chevron that flips up when expanded. */ body > .ctx-menu .ctx-toggle .ctx-chev { margin-left: auto; color: var(--color-text-light-2); transition: transform 0.12s ease; } body > .ctx-menu .ctx-toggle .ctx-chev.up { transform: rotate(180deg); } /* Expandable Block panel: its option buttons go edge-to-edge on hover so the whole row is the target, not just the centered label. */ body > .ctx-menu .ctx-expand { display: flex; flex-direction: column; gap: 0; padding-top: 2px; border-top: 0.5px solid var(--color-secondary-alpha-30); margin-top: 2px; } body > .ctx-menu .ctx-expand .ctx-opt { width: 100%; border-radius: 0; padding-left: 10px; /* flush with the other menu items' text, no icon column */ } body > .ctx-menu .ctx-expand .ctx-opt:hover { width: 100%; background: var(--color-hover); } /* Delete confirmation label inside context menu */ body > .ctx-menu .ctx-confirm-label { padding: 6px 12px 2px; font-size: 11px; color: var(--color-text-light-2); } /* Rename group dialog input */ body > .confirm-overlay .rename-input { width: 100%; font-size: 14px; font-family: var(--fonts-regular); color: var(--color-text); background: var(--color-secondary-bg); border: 0.5px solid var(--color-secondary-alpha-30); border-radius: var(--border-radius-medium); padding: 8px 10px; outline: none; } body > .confirm-overlay .rename-input:focus { border-color: var(--color-primary); } body > .confirm-overlay .rename-modal { align-items: stretch; } /* Mobile overflow menu: transparent full-screen click catcher with the menu positioned in the top-right corner, just like the context menu (no darkening). */ body > .overflow-backdrop { position: fixed; inset: 0; z-index: 1002; } body > .overflow-backdrop .overflow-menu { position: absolute; top: 56px; right: 8px; min-width: 160px; background: var(--color-menu); border: 0.5px solid var(--color-secondary-alpha-30); border-radius: var(--border-radius-medium); padding: 4px; display: flex; flex-direction: column; gap: 2px; } body > .overflow-backdrop .ctx-item { display: flex; align-items: center; gap: 8px; border: none; background: transparent; color: var(--color-text); font-size: 13px; padding: 7px 10px; border-radius: var(--border-radius); cursor: pointer; text-align: left; min-height: 36px; } body > .overflow-backdrop .ctx-item:hover { background: var(--color-hover); } body > .overflow-backdrop .ctx-item.danger:hover { color: var(--color-danger); } /* Audio player buttons: instant hover, no delay (overrides all inherited transitions) */ #mail-app .audio-player a.ap-btn, #mail-app .audio-player button.ap-btn, #mail-app .audio-player a.ap-download, #mail-app .audio-player .ap-download, #mail-app .audio-player .ap-close, #mail-app .audio-player .ap-btn, #mail-app .audio-player .ap-btn:hover { transition: none !important; transition-property: none !important; transition-duration: 0s !important; transition-delay: 0s !important; } /* Mobile: prevent iOS auto-zoom on input focus by ensuring font-size >= 16px on ALL form elements. The viewport meta override handles most cases, but newer iOS versions sometimes ignore user-scalable=no, so this is a belt-and-suspenders fix. Covers textareas, inputs (all types), and contenteditable elements. */ @media (max-width: 639.98px) { #mail-app input, #mail-app textarea, #mail-app select, #mail-app [contenteditable], body > .confirm-overlay input, body > .confirm-overlay textarea, body > .fwd-overlay input, body > .fwd-overlay textarea, body > .ctx-menu input, body > .overflow-menu input { font-size: 16px !important; } } /* Interactive UI elements (context menu, overflow menu, thread list items) always disable text selection. */ #mail-app .thread-item, #mail-app .ctx-menu, #mail-app .overflow-menu { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } /* Desktop: message content allows free text selection. */ #mail-app .msg-row, #mail-app .html-card { -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text; } /* Mobile: disable text selection on ALL message content by default. This prevents the native system copy/select popup from appearing alongside our custom context menu on long-press. Only messages explicitly marked with .plain-msg class (via the "Plain" button) get text selection re-enabled, which also allows the native copy UI to appear for that specific message. This is a robust cross-browser solution: user-select:none is universally supported on iOS Safari, Chrome Android, Firefox, etc. */ @media (max-width: 639.98px) { #mail-app .msg-row, #mail-app .html-card { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-touch-callout: none; } /* Global plain mode: ALL messages get text selection + native callout. The .plain-mode class is on the chat-pane root element. */ #mail-app .plain-mode .msg-row, #mail-app .plain-mode .msg-row .msg-bubble, #mail-app .plain-mode .msg-row .msg-text, #mail-app .plain-mode .html-card, #mail-app .plain-mode .html-card .html-body, #mail-app .plain-mode .html-body { -webkit-user-select: text !important; -moz-user-select: text !important; -ms-user-select: text !important; user-select: text !important; -webkit-touch-callout: default !important; } } /* Prevent double-tap-to-zoom on mobile. touch-action: manipulation allows panning and pinching but disables double-tap zoom gesture. */ @media (max-width: 639.98px) { #mail-app, body > .confirm-overlay, body > .fwd-overlay, body > .ctx-menu, body > .overflow-menu, body > .att-lightbox { touch-action: manipulation; } } /* Rendering optimization: content-visibility:auto lets the browser skip rendering off-screen message bubbles entirely. contain-intrinsic-size provides a placeholder height so scrollbar calculations work without actually rendering the content. This makes large conversations smooth regardless of message count. */ #mail-app .msg-row { content-visibility: auto; contain-intrinsic-size: auto 60px; } #mail-app .html-card { content-visibility: auto; contain-intrinsic-size: auto 120px; }