Files
ihasmail-inbuxa/web/src/styles/app.css
T
jcoffey-dev 5f32d3d82c Choose recipients from the address books
Addressing a message worked only if you already knew the name you were
half-way through typing. Autocomplete answers "finish this for me"; there
was no answer to "who is there?", which is the question someone has when
they open a compose window and want the person from the team list whose
surname they cannot summon.

The To row now opens the address books -- from a button beside Cc and
Bcc, where someone thinking about recipients is already looking, and from
the To label itself for anyone who tries that first. Search across every
book or narrow to one, tick as many people as the message needs, and send
them to To, Cc or Bcc. Picking for a field that is hidden opens it, since
a Bcc dropped somewhere invisible is worse than no Bcc.

Every address is its own row rather than every person. Somebody with a
work address and a personal one is a choice the writer has to make, and a
picker that listed the card and quietly took the first address would be
making it for them.

Shared books are in it on the same footing as the reader's own -- that
being the point of having added them -- with the account named on each
row, so it is never a mystery whose list a name came from. Books that
have not been added contribute nothing, the same rule the To field
already follows.

Verified against the mock: the picker lists the reader's contacts and the
shared book's, each row naming its source; ticking one of each and
choosing Cc opens the Cc row with both in it.
2026-08-27 11:19:45 -07:00

1038 lines
80 KiB
CSS
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.
/* ==========================================================================
ihasmail design system
========================================================================== */
:root {
--font-sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
--fs: 14px;
--radius: 10px;
--radius-sm: 6px;
--radius-lg: 16px;
--shadow-1: 0 1px 2px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.08);
--shadow-2: 0 4px 16px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.08);
--shadow-3: 0 12px 40px rgba(0, 0, 0, 0.18), 0 4px 12px rgba(0, 0, 0, 0.1);
--row-h: 44px;
--topbar-h: 56px;
--sidebar-w: 256px;
--sidebar-w-collapsed: 68px;
--list-w: 42%;
--ease: cubic-bezier(0.2, 0, 0, 1);
/* Light palette */
--bg: #f6f8fa;
--bg-elev: #ffffff;
--bg-sunken: #eef1f4;
--bg-hover: rgba(15, 23, 42, 0.05);
--bg-active: rgba(15, 23, 42, 0.09);
--fg: #111827;
--fg-muted: #5b6472;
--fg-faint: #8b94a3;
--border: #e3e7ec;
--border-strong: #cbd2da;
--accent: #0f766e;
--accent-fg: #ffffff;
--accent-soft: #d9f1ee;
--accent-soft-fg: #0b5750;
--danger: #dc2626;
--danger-soft: #fee2e2;
--warn: #b45309;
--warn-soft: #fef3c7;
--success: #15803d;
--success-soft: #dcfce7;
--link: #0e7490;
--unread-bg: #ffffff;
--read-bg: #f3f5f7;
--selected-bg: #d9f1ee;
--focus-ring: 0 0 0 3px rgba(15, 118, 110, 0.35);
--star: #f59e0b;
--q1: #2563eb;
--q2: #16a34a;
--q3: #9333ea;
--scrollbar: rgba(0, 0, 0, 0.25);
color-scheme: light;
}
:root[data-theme="dark"] {
--bg: #0b1220;
--bg-elev: #111a2b;
--bg-sunken: #0d1524;
--bg-hover: rgba(255, 255, 255, 0.06);
--bg-active: rgba(255, 255, 255, 0.1);
--fg: #e5e9f0;
--fg-muted: #9aa5b8;
--fg-faint: #6b7689;
--border: #1f2a3d;
--border-strong: #31405a;
--accent: #2dd4bf;
--accent-fg: #052e2b;
--accent-soft: rgba(45, 212, 191, 0.16);
--accent-soft-fg: #99f6e4;
--danger: #f87171;
--danger-soft: rgba(248, 113, 113, 0.15);
--warn: #fbbf24;
--warn-soft: rgba(251, 191, 36, 0.15);
--success: #4ade80;
--success-soft: rgba(74, 222, 128, 0.15);
--link: #67e8f9;
--unread-bg: #121c2e;
--read-bg: #0e1627;
--selected-bg: rgba(45, 212, 191, 0.18);
--focus-ring: 0 0 0 3px rgba(45, 212, 191, 0.4);
--star: #fbbf24;
--q1: #60a5fa;
--q2: #4ade80;
--q3: #c084fc;
--scrollbar: rgba(255, 255, 255, 0.25);
--shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
--shadow-2: 0 4px 16px rgba(0, 0, 0, 0.5);
--shadow-3: 0 12px 40px rgba(0, 0, 0, 0.6);
color-scheme: dark;
}
/*
* The "ihasmail" theme: the palette from ihasmail.org, which is a teal-navy
* rather than the blue-slate of the plain dark theme, warmed by the orange the
* logo's cat is drawn in.
*
* It rides on data-theme="dark" rather than replacing it, so every dark-only
* rule further down this file -- tooltips, toasts, the message frame -- keeps
* applying without being duplicated. Only the palette is overridden.
*
* Specificity is doing deliberate work here. This block is [data-palette] plus
* :root, so 0,2,0; the accent variants below are :root[data-theme][data-accent],
* so 0,3,0 and they win. That is what makes the accent swatches keep working on
* top of this theme -- and because the default accent ("teal") has no rule of
* its own, ihasmail.org's own accent is what shows until someone picks another.
*/
:root[data-palette="ihasmail"] {
--bg: #0d2430;
--bg-elev: #12303e;
--bg-sunken: #0a1c26;
--bg-hover: rgba(70, 202, 195, 0.10);
--bg-active: rgba(70, 202, 195, 0.16);
--fg: #eaf6f6;
--fg-muted: #a3c3cb;
--fg-faint: #86aab4;
--border: #21505f;
--border-strong: #2e6a7a;
--accent: #46cac3;
--accent-fg: #062028;
--accent-soft: rgba(70, 202, 195, 0.16);
--accent-soft-fg: #9fe6e2;
--danger: #f87171;
--danger-soft: rgba(248, 113, 113, 0.15);
--warn: #f9a34b;
--warn-soft: rgba(249, 163, 75, 0.14);
--success: #4ade80;
--success-soft: rgba(74, 222, 128, 0.15);
--link: #6fdcd6;
--unread-bg: #163a4a;
--read-bg: #12303e;
--selected-bg: rgba(70, 202, 195, 0.18);
--focus-ring: 0 0 0 3px rgba(70, 202, 195, 0.4);
/* The cat is orange; so is the star. */
--star: #f9a34b;
--q1: #6fdcd6;
--q2: #4ade80;
--q3: #c084fc;
--scrollbar: rgba(163, 195, 203, 0.3);
--shadow-1: 0 1px 2px rgba(0, 0, 0, 0.45);
--shadow-2: 0 8px 24px rgba(0, 0, 0, 0.55);
--shadow-3: 0 22px 60px -28px rgba(0, 0, 0, 0.75);
color-scheme: dark;
}
/* Accent variants */
:root[data-accent="blue"] { --accent: #2563eb; --accent-soft: #dbeafe; --accent-soft-fg: #1e3a8a; --selected-bg: #dbeafe; --focus-ring: 0 0 0 3px rgba(37,99,235,.35); --link:#1d4ed8; }
:root[data-accent="purple"] { --accent: #7c3aed; --accent-soft: #ede9fe; --accent-soft-fg: #4c1d95; --selected-bg: #ede9fe; --focus-ring: 0 0 0 3px rgba(124,58,237,.35); --link:#6d28d9; }
:root[data-accent="rose"] { --accent: #e11d48; --accent-soft: #ffe4e6; --accent-soft-fg: #881337; --selected-bg: #ffe4e6; --focus-ring: 0 0 0 3px rgba(225,29,72,.35); --link:#be123c; }
:root[data-accent="orange"] { --accent: #ea580c; --accent-soft: #ffedd5; --accent-soft-fg: #7c2d12; --selected-bg: #ffedd5; --focus-ring: 0 0 0 3px rgba(234,88,12,.35); --link:#c2410c; }
:root[data-accent="green"] { --accent: #16a34a; --accent-soft: #dcfce7; --accent-soft-fg: #14532d; --selected-bg: #dcfce7; --focus-ring: 0 0 0 3px rgba(22,163,74,.35); --link:#15803d; }
:root[data-theme="dark"][data-accent="blue"] { --accent: #60a5fa; --accent-fg:#0b1a33; --accent-soft: rgba(96,165,250,.18); --accent-soft-fg:#bfdbfe; --selected-bg: rgba(96,165,250,.2); --link:#93c5fd; }
:root[data-theme="dark"][data-accent="purple"] { --accent: #a78bfa; --accent-fg:#1e1040; --accent-soft: rgba(167,139,250,.18); --accent-soft-fg:#ddd6fe; --selected-bg: rgba(167,139,250,.2); --link:#c4b5fd; }
:root[data-theme="dark"][data-accent="rose"] { --accent: #fb7185; --accent-fg:#3b0716; --accent-soft: rgba(251,113,133,.18); --accent-soft-fg:#fecdd3; --selected-bg: rgba(251,113,133,.2); --link:#fda4af; }
:root[data-theme="dark"][data-accent="orange"] { --accent: #fb923c; --accent-fg:#3b1605; --accent-soft: rgba(251,146,60,.18); --accent-soft-fg:#fed7aa; --selected-bg: rgba(251,146,60,.2); --link:#fdba74; }
:root[data-theme="dark"][data-accent="green"] { --accent: #4ade80; --accent-fg:#052e16; --accent-soft: rgba(74,222,128,.18); --accent-soft-fg:#bbf7d0; --selected-bg: rgba(74,222,128,.2); --link:#86efac; }
:root[data-density="comfortable"] { --row-h: 52px; }
:root[data-density="cozy"] { --row-h: 44px; }
:root[data-density="compact"] { --row-h: 36px; }
:root[data-fontsize="small"] { --fs: 13px; }
:root[data-fontsize="large"] { --fs: 15.5px; }
/* Reset ------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body, #root { height: 100%; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: var(--font-sans);
font-size: var(--fs);
line-height: 1.45;
color: var(--fg);
background: var(--bg);
-webkit-font-smoothing: antialiased;
overscroll-behavior: none;
}
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: 0; padding: 0; }
a { color: var(--link); }
img { max-width: 100%; }
::selection { background: var(--accent-soft); }
* { scrollbar-width: thin; scrollbar-color: var(--scrollbar) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb { background: var(--scrollbar); border-radius: 8px; }
:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.muted { color: var(--fg-muted); }
.faint { color: var(--fg-faint); }
.small { font-size: 0.875em; }
.mono { font-family: var(--font-mono); }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row { display: flex; align-items: center; gap: 8px; }
.col { display: flex; flex-direction: column; }
.grow { flex: 1 1 auto; min-width: 0; }
.spacer { flex: 1; }
.hidden { display: none !important; }
.wrap { flex-wrap: wrap; }
.gap-2 { gap: 2px; } .gap-4 { gap: 4px; } .gap-8 { gap: 8px; } .gap-12 { gap: 12px; } .gap-16 { gap: 16px; }
.mt-8 { margin-top: 8px; } .mt-16 { margin-top: 16px; } .mb-8 { margin-bottom: 8px; } .mb-16 { margin-bottom: 16px; }
.p-16 { padding: 16px; }
.center { display:flex; align-items:center; justify-content:center; }
.nowrap { white-space: nowrap; }
/* Buttons ---------------------------------------------------------------- */
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
height: 36px; padding: 0 14px; border-radius: var(--radius-sm);
border: 1px solid var(--border-strong); background: var(--bg-elev); color: var(--fg);
font-weight: 500; white-space: nowrap; user-select: none; transition: background .12s var(--ease), border-color .12s, box-shadow .12s, transform .05s;
}
.btn:hover { background: var(--bg-hover); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }
.btn-primary { background: var(--accent); border-color: transparent; color: var(--accent-fg); }
.btn-primary:hover { filter: brightness(1.06); background: var(--accent); }
.btn-danger { background: var(--danger); border-color: transparent; color: #fff; }
.btn-danger:hover { filter: brightness(1.06); background: var(--danger); }
.btn-ghost { border-color: transparent; background: transparent; }
.btn-ghost:hover { background: var(--bg-hover); }
.btn-soft { background: var(--accent-soft); border-color: transparent; color: var(--accent-soft-fg); }
.btn-sm { height: 30px; padding: 0 10px; font-size: .9em; }
.btn-lg { height: 44px; padding: 0 20px; font-size: 1.05em; border-radius: var(--radius); }
.btn-pill { border-radius: 999px; }
.btn-block { width: 100%; }
.icon-btn {
display: inline-flex; align-items: center; justify-content: center;
width: 36px; height: 36px; border-radius: 50%; color: var(--fg-muted); flex: 0 0 auto;
transition: background .12s var(--ease), color .12s;
}
.icon-btn:hover { background: var(--bg-hover); color: var(--fg); }
.icon-btn:disabled { opacity: .4; cursor: not-allowed; }
.icon-btn.active { color: var(--accent); background: var(--accent-soft); }
.icon-btn.sm { width: 30px; height: 30px; }
.icon-btn.xs { width: 26px; height: 26px; }
.icon-btn.danger:hover { color: var(--danger); background: var(--danger-soft); }
/* Inputs ----------------------------------------------------------------- */
.input, .select, .textarea {
width: 100%; height: 38px; padding: 0 12px; border-radius: var(--radius-sm);
border: 1px solid var(--border-strong); background: var(--bg-elev); color: var(--fg);
transition: border-color .12s, box-shadow .12s;
}
.textarea { height: auto; min-height: 90px; padding: 10px 12px; resize: vertical; line-height: 1.5; }
.input:focus, .select:focus, .textarea:focus { border-color: var(--accent); box-shadow: var(--focus-ring); outline: none; }
.input::placeholder, .textarea::placeholder { color: var(--fg-faint); }
.input.sm { height: 32px; padding: 0 10px; }
.select { appearance: none; padding-right: 30px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238b94a3' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 10px center; }
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.field > label, .label { font-size: .85em; font-weight: 600; color: var(--fg-muted); letter-spacing: .01em; }
.field-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
.hint { font-size: .85em; color: var(--fg-faint); }
.check { display: flex; align-items: center; gap: 10px; cursor: pointer; padding: 6px 0; }
.check input { width: 18px; height: 18px; accent-color: var(--accent); margin: 0; }
.switch { position: relative; width: 40px; height: 22px; border-radius: 999px; background: var(--border-strong); transition: background .15s; flex: 0 0 auto; }
.switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: #fff; box-shadow: var(--shadow-1); transition: transform .15s var(--ease); }
.switch[aria-checked="true"] { background: var(--accent); }
.switch[aria-checked="true"]::after { transform: translateX(18px); }
.switch-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--border); }
.switch-row:last-child { border-bottom: 0; }
.switch-row .switch-text { display: flex; flex-direction: column; gap: 2px; }
.switch-row .switch-text .hint { margin: 0; }
.chip { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 10px; border-radius: 999px; background: var(--bg-sunken); border: 1px solid var(--border); font-size: .85em; max-width: 100%; }
.chip .chip-x { display: inline-flex; border-radius: 50%; padding: 2px; color: var(--fg-muted); }
.chip .chip-x:hover { background: var(--bg-active); color: var(--fg); }
.chip.invalid { border-color: var(--danger); color: var(--danger); }
.badge { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; font-size: 11px; font-weight: 700; background: var(--accent); color: var(--accent-fg); }
.badge.muted { background: var(--bg-active); color: var(--fg-muted); }
.tag { display: inline-flex; align-items: center; gap: 4px; height: 20px; padding: 0 8px; border-radius: 6px; font-size: 11.5px; font-weight: 600; color: #fff; }
.kbd { display: inline-block; min-width: 20px; padding: 1px 6px; border-radius: 4px; border: 1px solid var(--border-strong); border-bottom-width: 2px; background: var(--bg-sunken); font-family: var(--font-mono); font-size: 11.5px; text-align: center; }
/* Avatar ----------------------------------------------------------------- */
.avatar { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 50%; color: #fff; font-weight: 600; font-size: 13px; flex: 0 0 auto; overflow: hidden; user-select: none; }
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar.sm { width: 28px; height: 28px; font-size: 11px; }
.avatar.lg { width: 56px; height: 56px; font-size: 20px; }
.avatar.xl { width: 88px; height: 88px; font-size: 30px; }
/* Menus / popovers ------------------------------------------------------- */
.popover { position: fixed; z-index: 1000; min-width: 180px; max-width: min(420px, calc(100vw - 16px)); max-height: min(70vh, 520px); overflow: auto; background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-2); padding: 6px; animation: pop .12s var(--ease); }
@keyframes pop { from { opacity: 0; transform: translateY(-4px) scale(.98); } to { opacity: 1; transform: none; } }
.menu-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px; border-radius: var(--radius-sm); text-align: left; color: var(--fg); white-space: nowrap; }
.menu-item:hover, .menu-item.active { background: var(--bg-hover); }
.menu-item:disabled { opacity: .5; cursor: default; }
.menu-item.danger { color: var(--danger); }
.menu-item .menu-kbd { margin-left: auto; color: var(--fg-faint); font-size: .85em; }
.menu-item svg { color: var(--fg-muted); flex: 0 0 auto; }
.menu-item.danger svg { color: var(--danger); }
.menu-sep { height: 1px; background: var(--border); margin: 6px 4px; }
.menu-title { padding: 6px 10px 4px; font-size: .78em; font-weight: 700; color: var(--fg-faint); text-transform: uppercase; letter-spacing: .05em; }
.menu-search { padding: 4px 4px 6px; }
.tooltip { position: fixed; z-index: 2000; background: #1f2937; color: #fff; padding: 5px 8px; border-radius: 6px; font-size: 12px; pointer-events: none; white-space: nowrap; box-shadow: var(--shadow-2); animation: pop .1s var(--ease); }
:root[data-theme="dark"] .tooltip { background: #e5e9f0; color: #0b1220; }
/* Dialogs ---------------------------------------------------------------- */
.dialog-backdrop { position: fixed; inset: 0; z-index: 900; background: rgba(2, 6, 23, 0.45); display: flex; align-items: center; justify-content: center; padding: 16px; animation: fade .15s var(--ease); backdrop-filter: blur(2px); }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
.dialog { background: var(--bg-elev); border-radius: var(--radius-lg); box-shadow: var(--shadow-3); width: 100%; max-width: 520px; max-height: calc(100vh - 32px); display: flex; flex-direction: column; animation: rise .18s var(--ease); border: 1px solid var(--border); }
@keyframes rise { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: none; } }
.dialog.lg { max-width: 760px; }
.dialog.xl { max-width: 980px; }
.dialog.sm { max-width: 400px; }
.dialog-head { display: flex; align-items: center; gap: 8px; padding: 16px 20px 8px; }
.dialog-head h2 { margin: 0; font-size: 1.15em; font-weight: 650; flex: 1; }
.dialog-body { padding: 8px 20px 16px; overflow: auto; }
.dialog-foot { display: flex; align-items: center; justify-content: flex-end; gap: 8px; padding: 12px 20px 16px; border-top: 1px solid var(--border); }
.dialog-foot .left { margin-right: auto; }
/* Toasts ----------------------------------------------------------------- */
.toast-host { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 3000; display: flex; flex-direction: column; gap: 8px; align-items: center; pointer-events: none; padding: 0 12px; width: 100%; max-width: 520px; }
.toast { pointer-events: auto; display: flex; align-items: center; gap: 12px; background: #1f2937; color: #fff; padding: 10px 10px 10px 16px; border-radius: var(--radius); box-shadow: var(--shadow-2); min-width: 260px; max-width: 100%; position: relative; overflow: hidden; animation: rise .2s var(--ease); }
:root[data-theme="dark"] .toast { background: #e5e9f0; color: #0b1220; }
.toast-error { background: #991b1b; color: #fff; }
:root[data-theme="dark"] .toast-error { background: #fecaca; color: #450a0a; }
.toast-success { background: #14532d; color: #fff; }
:root[data-theme="dark"] .toast-success { background: #bbf7d0; color: #052e16; }
.toast-msg { flex: 1; }
.toast-action { color: #5eead4; font-weight: 700; padding: 6px 8px; border-radius: 6px; }
.toast-action:hover { background: rgba(255,255,255,.12); }
:root[data-theme="dark"] .toast-action { color: #0f766e; }
.toast-close { color: inherit; opacity: .7; display: inline-flex; padding: 4px; border-radius: 50%; }
.toast-close:hover { opacity: 1; background: rgba(255,255,255,.12); }
.toast-progress { position: absolute; left: 0; bottom: 0; height: 3px; background: #5eead4; width: 100%; transform-origin: left; animation: shrink linear forwards; }
@keyframes shrink { from { transform: scaleX(1); } to { transform: scaleX(0); } }
/* Spinner / skeleton ----------------------------------------------------- */
.spinner { width: 20px; height: 20px; border: 2px solid var(--border-strong); border-top-color: var(--accent); border-radius: 50%; animation: spin .7s linear infinite; flex: 0 0 auto; }
.spinner.lg { width: 32px; height: 32px; border-width: 3px; }
@keyframes spin { to { transform: rotate(360deg); } }
.skeleton { background: linear-gradient(90deg, var(--bg-sunken) 25%, var(--bg-hover) 37%, var(--bg-sunken) 63%); background-size: 400% 100%; animation: shimmer 1.4s ease infinite; border-radius: 6px; }
@keyframes shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
.empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 48px 24px; color: var(--fg-muted); text-align: center; }
.empty svg { color: var(--fg-faint); }
.empty h3 { margin: 8px 0 0; font-weight: 600; color: var(--fg); }
.empty p { margin: 0; max-width: 360px; }
.error-box { padding: 12px 14px; border-radius: var(--radius-sm); background: var(--danger-soft); color: var(--danger); border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent); }
.info-box { padding: 12px 14px; border-radius: var(--radius-sm); background: var(--accent-soft); color: var(--accent-soft-fg); }
.warn-box { padding: 12px 14px; border-radius: var(--radius-sm); background: var(--warn-soft); color: var(--warn); }
/* ==========================================================================
App shell
========================================================================== */
.app { height: 100%; display: grid; grid-template-rows: var(--topbar-h) 1fr; overflow: hidden; }
.topbar { display: flex; align-items: center; gap: 8px; padding: 0 12px; background: var(--bg); position: relative; z-index: 50; }
.topbar .brand { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 1.2em; letter-spacing: -.01em; color: var(--fg); text-decoration: none; padding-right: 8px; min-width: 0; }
.topbar .brand img { width: 34px; height: 34px; object-fit: contain; }
.topbar .brand .brand-name { color: #14b8a6; }
.topbar .brand .brand-name span { color: var(--fg-muted); font-weight: 500; }
.searchbar { flex: 1 1 auto; max-width: 720px; margin: 0 auto; position: relative; }
.searchbar .search-input { display: flex; align-items: center; gap: 8px; height: 44px; padding: 0 8px 0 14px; border-radius: 999px; background: var(--bg-sunken); border: 1px solid transparent; transition: background .12s, box-shadow .12s, border-color .12s; }
.searchbar .search-input:focus-within { background: var(--bg-elev); box-shadow: var(--shadow-1); border-color: var(--border); }
.searchbar input { flex: 1; border: 0; background: transparent; outline: none; min-width: 0; height: 100%; }
.searchbar input::placeholder { color: var(--fg-faint); }
.search-panel { position: absolute; top: calc(100% + 6px); left: 0; right: 0; background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-2); padding: 16px; z-index: 60; animation: pop .12s var(--ease); }
.search-panel .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 14px; }
.search-suggest { position: absolute; top: calc(100% + 6px); left: 0; right: 0; background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-2); padding: 6px; z-index: 60; }
.topbar-actions { display: flex; align-items: center; gap: 4px; }
/* Live-updates indicator. Three states, and a raised bead rather than a flat
speck: at 8px flat it was invisible against either theme. The gloss is a
highlight over a solid colour rather than a colour-mix, so it needs no
per-theme variant -- the same bead reads on light and dark alike. */
.push-status { display: inline-flex; align-items: center; padding: 0 4px; }
.push-dot {
width: 12px;
height: 12px;
border-radius: 50%;
background:
radial-gradient(circle at 34% 30%, rgba(255, 255, 255, .8), rgba(255, 255, 255, 0) 46%),
var(--dot);
box-shadow:
0 0 0 3px var(--dot-halo),
0 1px 2px rgba(0, 0, 0, .45),
inset 0 -1px 2px rgba(0, 0, 0, .3);
}
.push-dot.connected { --dot: #16a34a; --dot-halo: rgba(22, 163, 74, .25); }
.push-dot.connecting { --dot: #eab308; --dot-halo: rgba(234, 179, 8, .25); animation: push-pulse 1.6s ease-in-out infinite; }
.push-dot.disconnected { --dot: #dc2626; --dot-halo: rgba(220, 38, 38, .25); }
@keyframes push-pulse { 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .push-dot.connecting { animation: none; } }
.app-body { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 0; transition: grid-template-columns .2s var(--ease); }
.app-body.collapsed { grid-template-columns: var(--sidebar-w-collapsed) 1fr; }
.sidebar { display: flex; flex-direction: column; min-height: 0; padding: 4px 8px 8px 8px; gap: 2px; overflow: hidden; }
.sidebar-scroll { overflow-y: auto; overflow-x: hidden; flex: 1; min-height: 0; padding-bottom: 8px; }
.compose-btn { display: flex; align-items: center; gap: 12px; height: 52px; padding: 0 22px 0 18px; margin: 6px 4px 12px; border-radius: 16px; background: var(--bg-elev); box-shadow: var(--shadow-1); font-weight: 600; font-size: 1em; color: var(--fg); transition: box-shadow .15s, transform .05s, background .12s; white-space: nowrap; }
.compose-btn:hover { box-shadow: var(--shadow-2); background: var(--bg-elev); }
.compose-btn svg { color: var(--accent); }
.collapsed .compose-btn { width: 52px; padding: 0; justify-content: center; margin-left: auto; margin-right: auto; }
.collapsed .compose-btn span { display: none; }
.nav-section { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px 4px; font-size: .75em; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--fg-faint); }
.nav-section .icon-btn { width: 24px; height: 24px; }
.collapsed .nav-section { display: none; }
.nav-item { position: relative; display: flex; align-items: center; gap: 12px; height: 36px; padding: 0 12px; border-radius: 0 999px 999px 0; margin-right: 8px; color: var(--fg); text-decoration: none; white-space: nowrap; transition: background .12s; user-select: none; font-weight: 450; }
.nav-item:hover { background: var(--bg-hover); }
.nav-item.unread .nav-label, .nav-item.unread .nav-count { font-weight: 700; color: var(--fg); }
.nav-item.active { background: var(--accent-soft); color: var(--accent-soft-fg); font-weight: 650; }
.nav-item.active.unread .nav-label, .nav-item.active.unread .nav-count { color: inherit; }
.nav-item.drop-target { background: var(--accent-soft); outline: 2px dashed var(--accent); outline-offset: -2px; }
.nav-item.folder-row.dragging { opacity: .45; }
/* A folder colour tints its icon; the label keeps the sidebar's contrast. */
.folder-row .folder-icon { display: inline-flex; align-items: center; }
/* .nav-item svg sets colour on the svg itself, so inheriting from the span is
not enough -- the icon has to be targeted directly to win the cascade.
The fill overrides lucide's own fill="none": a CSS rule beats a presentation
attribute, and a solid folder reads at a glance where an outline does not. */
.folder-row .folder-icon[style*="--folder-color"] svg { color: var(--bg); fill: var(--folder-color); }
/* The Folders heading doubles as the way back to the top level while dragging. */
.nav-section.drop-target { background: var(--accent-soft); outline: 2px dashed var(--accent); outline-offset: -2px; border-radius: var(--radius-sm); color: var(--accent-soft-fg); }
.nav-item svg { flex: 0 0 auto; color: var(--fg-muted); }
.nav-item.active svg { color: inherit; }
.nav-item .nav-label { flex: 1; overflow: hidden; text-overflow: ellipsis; }
.nav-item .nav-count { font-size: .8em; font-weight: 700; color: var(--fg-muted); }
.nav-item.active .nav-count { color: inherit; }
.nav-item .nav-more { opacity: 0; width: 24px; height: 24px; margin-right: -6px; }
.nav-item:hover .nav-more, .nav-item:focus-within .nav-more { opacity: 1; }
.nav-item .nav-twisty { width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; color: var(--fg-faint); border-radius: 4px; }
.nav-item .nav-twisty:hover { background: var(--bg-active); }
/* Folder rows keep the expand chevron in a gutter to the LEFT of the icon, so a
folder with subfolders and one without still line up on their icon. */
.nav-item.folder-row { --folder-indent: 0px; position: relative; padding-left: calc(30px + var(--folder-indent)); }
.nav-item.folder-row.depth-1 { --folder-indent: 16px; }
.nav-item.folder-row.depth-2 { --folder-indent: 32px; }
.nav-item.folder-row.depth-3 { --folder-indent: 48px; }
.nav-item.folder-row.depth-4 { --folder-indent: 64px; }
.nav-item.folder-row .nav-twisty { position: absolute; left: calc(8px + var(--folder-indent)); top: 50%; transform: translateY(-50%); margin: 0; }
/* Labels sit in the same column: a 20px slot matching the folder icons. */
.nav-item.folder-row .nav-label-color { flex: 0 0 20px; width: 20px; height: 20px; border-radius: 0; display: inline-flex; align-items: center; justify-content: center; }
.nav-item.folder-row .nav-label-color::before { content: ""; width: 11px; height: 11px; border-radius: 3px; background: var(--label-color, var(--accent)); }
.collapsed .nav-item { justify-content: center; padding: 0; margin: 0 auto; width: 44px; border-radius: 999px; }
.collapsed .nav-item .nav-label, .collapsed .nav-item .nav-count, .collapsed .nav-item .nav-more, .collapsed .nav-item .nav-twisty { display: none; }
.collapsed .nav-item.depth-1, .collapsed .nav-item.depth-2, .collapsed .nav-item.depth-3 { display: none; }
.collapsed .nav-item .nav-dot { position: absolute; top: 6px; right: 6px; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.nav-label-color { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
/* Outlook-style module bar at the bottom of the sidebar */
.module-bar { display: flex; align-items: stretch; justify-content: space-around; gap: 2px; padding: 6px 4px 4px; border-top: 1px solid var(--border); flex: 0 0 auto; }
.module-link { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; flex: 1; min-width: 0; padding: 6px 2px; border-radius: var(--radius-sm); color: var(--fg-muted); text-decoration: none; font-size: 11px; font-weight: 500; transition: background .12s, color .12s; }
.module-link:hover { background: var(--bg-hover); color: var(--fg); }
.module-link.active { color: var(--accent-soft-fg); background: var(--accent-soft); }
.module-link svg { flex: 0 0 auto; }
.module-link .module-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.collapsed .module-bar { flex-direction: column; gap: 4px; }
.collapsed .module-link .module-label { display: none; }
.collapsed .module-link { width: 44px; height: 44px; margin: 0 auto; padding: 0; border-radius: 999px; }
.quota { padding: 10px 12px; font-size: .8em; color: var(--fg-muted); }
.quota-bar { height: 4px; background: var(--bg-active); border-radius: 4px; overflow: hidden; margin-top: 6px; }
.quota-bar > span { display: block; height: 100%; background: var(--accent); }
.quota-bar > span.warn { background: var(--warn); }
.quota-bar > span.danger { background: var(--danger); }
.collapsed .quota { display: none; }
.main { min-height: 0; min-width: 0; display: flex; flex-direction: column; background: var(--bg-elev); border-radius: var(--radius-lg) 0 0 0; border: 1px solid var(--border); border-right: 0; border-bottom: 0; overflow: hidden; }
/* Mobile drawer */
.drawer-backdrop { display: none; }
.mobile-tabbar { display: none; }
.fab { display: none; }
/* ==========================================================================
Mail
========================================================================== */
.mail-layout { display: grid; grid-template-columns: minmax(340px, var(--list-w)) 1fr; height: 100%; min-height: 0; flex: 1; }
.mail-layout.pane-off { grid-template-columns: 1fr; }
.mail-layout.pane-off.reading { grid-template-columns: 1fr; }
.mail-layout .mail-list-pane { display: flex; flex-direction: column; min-height: 0; min-width: 0; border-right: 1px solid var(--border); }
.mail-layout.pane-bottom .mail-list-pane { border-right: 0; border-bottom: 1px solid var(--border); }
.mail-layout.pane-off .mail-list-pane { border-right: 0; }
.mail-layout .mail-reading-pane { min-height: 0; min-width: 0; display: flex; flex-direction: column; background: var(--bg-elev); }
.list-toolbar { display: flex; align-items: center; gap: 2px; height: 48px; padding: 0 8px 0 12px; border-bottom: 1px solid var(--border); flex: 0 0 auto; }
.list-toolbar .tb-sep { width: 1px; height: 20px; background: var(--border); margin: 0 6px; }
.list-toolbar .tb-title { font-weight: 650; font-size: 1.02em; margin-right: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.list-toolbar .tb-count { color: var(--fg-muted); font-size: .85em; white-space: nowrap; }
.select-all { width: 18px; height: 18px; accent-color: var(--accent); margin: 0 6px 0 0; cursor: pointer; }
.mail-list { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; position: relative; outline: none; }
.mail-list-inner { position: relative; width: 100%; }
.msg-row { position: absolute; left: 0; right: 0; display: flex; align-items: center; gap: 8px; height: var(--row-h); padding: 0 12px 0 8px; border-bottom: 1px solid var(--border); background: var(--read-bg); cursor: pointer; user-select: none; -webkit-user-select: none; transition: box-shadow .1s; }
.msg-row.unread { background: var(--unread-bg); }
.msg-row.unread .msg-from, .msg-row.unread .msg-subject { font-weight: 700; color: var(--fg); }
.msg-row:hover { box-shadow: inset 0 0 0 1px var(--border-strong), var(--shadow-1); z-index: 2; }
.msg-row.selected { background: var(--selected-bg); }
.msg-row.focused { box-shadow: inset 3px 0 0 var(--accent); }
.msg-row.open { background: var(--selected-bg); }
.msg-row.dragging { opacity: .5; }
.msg-row .msg-check { width: 18px; height: 18px; accent-color: var(--accent); margin: 0; flex: 0 0 auto; opacity: 0; transition: opacity .1s; cursor: pointer; }
.msg-row:hover .msg-check, .msg-row.selected .msg-check, .mail-list.has-selection .msg-check { opacity: 1; }
.msg-row .msg-star { color: var(--fg-faint); flex: 0 0 auto; display: inline-flex; padding: 4px; border-radius: 50%; }
.msg-row .msg-star:hover { color: var(--star); background: var(--bg-hover); }
.msg-row .msg-star.on { color: var(--star); }
.msg-row .msg-from { width: 180px; flex: 0 0 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--fg); display: flex; align-items: center; gap: 6px; }
.msg-row .msg-from .thread-count { color: var(--fg-muted); font-weight: 400; font-size: .9em; }
.msg-row .msg-main { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 6px; overflow: hidden; }
.msg-row .msg-subject { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 0 1 auto; min-width: 40%; max-width: 100%; color: var(--fg); }
.msg-row .msg-preview { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--fg-muted); flex: 1 1 0; min-width: 0; }
.msg-row .msg-preview::before { content: " "; }
.msg-row .msg-meta { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; color: var(--fg-muted); font-size: .85em; white-space: nowrap; }
.msg-row.unread .msg-meta .msg-date { color: var(--fg); font-weight: 700; }
.msg-row .msg-actions { display: none; align-items: center; gap: 0; margin-left: 4px; }
.msg-row:hover .msg-actions { display: flex; }
.msg-row:hover .msg-meta .msg-date { display: none; }
.msg-row .msg-labels { display: inline-flex; gap: 4px; flex: 0 0 auto; }
.msg-row .msg-labels .tag { height: 18px; font-size: 10.5px; padding: 0 6px; }
.msg-row .avatar { width: 32px; height: 32px; font-size: 12px; }
.mail-list.compact .msg-row .avatar { display: none; }
.msg-row .msg-attach { color: var(--fg-faint); }
.msg-row .msg-answered { color: var(--fg-faint); }
.msg-row .msg-important { color: var(--warn); }
.list-footer { padding: 12px; text-align: center; color: var(--fg-muted); font-size: .9em; }
.list-hint { padding: 8px 12px; font-size: .85em; color: var(--fg-muted); background: var(--bg-sunken); border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px; }
.list-hint button { color: var(--link); font-weight: 600; white-space: nowrap; }
.drag-ghost { position: fixed; top: -1000px; left: -1000px; padding: 8px 12px; background: var(--accent); color: var(--accent-fg); border-radius: 999px; font-weight: 600; box-shadow: var(--shadow-2); pointer-events: none; z-index: 5000; }
/* Splitter between list and reading pane */
.splitter { position: relative; background: var(--border); flex: 0 0 auto; z-index: 3; touch-action: none; }
.splitter.vertical { width: 6px; cursor: col-resize; }
.splitter.horizontal { height: 6px; cursor: row-resize; }
.splitter:hover, .splitter:focus-visible { background: var(--accent); box-shadow: none; }
.splitter .splitter-grip { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); border-radius: 4px; background: var(--fg-faint); opacity: 0; transition: opacity .15s; }
.splitter.vertical .splitter-grip { width: 3px; height: 36px; }
.splitter.horizontal .splitter-grip { height: 3px; width: 36px; }
.splitter:hover .splitter-grip { opacity: .8; }
.mail-layout.pane-right { grid-template-columns: var(--list-size, 520px) auto 1fr; }
.mail-layout.pane-bottom { grid-template-columns: 1fr; grid-template-rows: var(--list-size, 340px) auto 1fr; }
/* Two-line rows (narrow lists / mobile) */
.mail-list.two-line .msg-row { height: calc(var(--row-h) * 1.5); align-items: flex-start; padding-top: 8px; }
.mail-list.two-line .msg-row .msg-from { width: auto; flex: 1; }
.mail-list.two-line .msg-row .msg-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.mail-list.two-line .msg-row .msg-line1 { display: flex; align-items: center; gap: 8px; }
.mail-list.two-line .msg-row .msg-main { display: flex; gap: 6px; }
.mail-list.two-line .msg-row .msg-subject { flex: 0 1 auto; }
.mail-list.two-line .msg-row .msg-preview { flex: 1; }
.mail-list.two-line .msg-row .msg-meta { margin-left: auto; }
.mail-list.two-line .msg-row:hover .msg-meta .msg-date { display: inline; }
.mail-list.two-line .msg-row:hover .msg-actions { display: none; }
/* Reading pane / thread view */
.thread-view { display: flex; flex-direction: column; min-height: 0; height: 100%; }
.thread-toolbar { display: flex; align-items: center; gap: 2px; height: 48px; padding: 0 8px; border-bottom: 1px solid var(--border); flex: 0 0 auto; }
.thread-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 0 0 80px; }
.thread-subject { padding: 20px 24px 8px; display: flex; align-items: flex-start; gap: 12px; }
.thread-subject h1 { margin: 0; font-size: 1.35em; font-weight: 600; line-height: 1.3; flex: 1; overflow-wrap: anywhere; }
.thread-subject .labels { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.message { margin: 0 16px 8px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-elev); transition: box-shadow .12s; }
.message.collapsed { cursor: pointer; }
.message.collapsed:hover { box-shadow: var(--shadow-1); }
.message.unread-msg { border-left: 3px solid var(--accent); }
.message-head { display: flex; align-items: flex-start; gap: 12px; padding: 12px 16px; }
.message-head .who { flex: 1; min-width: 0; }
.message-head .who .from { font-weight: 650; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.message-head .who .from .email { color: var(--fg-muted); font-weight: 400; font-size: .9em; }
.message-head .who .to { color: var(--fg-muted); font-size: .88em; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.message-head .who .to button { color: var(--fg-muted); display: inline-flex; align-items: center; }
.message-head .who .snippet { color: var(--fg-muted); font-size: .92em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.message-head .meta { display: flex; align-items: center; gap: 2px; color: var(--fg-muted); font-size: .85em; white-space: nowrap; flex: 0 0 auto; }
.message-head .meta .date { margin-right: 6px; }
.message-details { margin: 0 16px 8px; padding: 10px 12px; background: var(--bg-sunken); border-radius: var(--radius-sm); font-size: .88em; display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; }
.message-details dt { color: var(--fg-muted); }
.message-details dd { margin: 0; overflow-wrap: anywhere; }
/*
* Layout containment here is a security control, not a layout tweak.
*
* Message bodies render in a shadow root, which scopes selectors but not
* layout: a `position:fixed` rule in mail CSS is still positioned against the
* viewport, so a sender could paint over the whole application. Containment
* inside the shadow root cannot stop it — mail CSS lives in the same tree and
* simply overrides it, and `:host` reaches the host element too (an
* `!important` there even beats an `!important` from this file, because
* importance reverses tree order). An ancestor of the host is the one thing
* mail CSS has no selector for. `layout` rather than `paint`: it makes this a
* containing block for fixed descendants without clipping tall messages.
*/
.message-body { padding: 4px 16px 16px; contain: layout; }
.message-body .body-host { display: block; }
.remote-banner { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; padding: 8px 12px; background: var(--warn-soft); color: var(--warn); border-radius: var(--radius-sm); font-size: .9em; }
.remote-banner button { color: inherit; font-weight: 700; text-decoration: underline; }
.scheduled-banner { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; padding: 8px 12px; background: var(--accent-soft); color: var(--accent-soft-fg); border-radius: var(--radius-sm); font-size: .9em; }
.scheduled-banner button { color: inherit; font-weight: 700; text-decoration: underline; }
.receipt-banner { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; padding: 8px 12px; background: var(--warn-soft); color: var(--warn); border-radius: var(--radius-sm); font-size: .9em; }
.receipt-banner button { color: inherit; font-weight: 700; text-decoration: underline; }
.quote-toggle { display: inline-flex; align-items: center; gap: 4px; margin: 8px 0; padding: 2px 10px; border-radius: 999px; background: var(--bg-sunken); color: var(--fg-muted); font-size: 12px; border: 1px solid var(--border); }
.quote-toggle:hover { background: var(--bg-active); }
.attachments { display: flex; flex-wrap: wrap; gap: 10px; padding: 4px 16px 16px; }
.attachment { display: flex; align-items: center; gap: 10px; width: 240px; max-width: 100%; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-elev); text-decoration: none; color: inherit; position: relative; transition: box-shadow .12s; }
.attachment:hover { box-shadow: var(--shadow-1); }
.attachment .att-icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; background: var(--accent-soft); color: var(--accent-soft-fg); flex: 0 0 auto; overflow: hidden; }
.attachment .att-icon img { width: 100%; height: 100%; object-fit: cover; }
.attachment .att-text { flex: 1; min-width: 0; }
.attachment .att-name { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: .92em; }
.attachment .att-size { color: var(--fg-muted); font-size: .8em; }
.attachment .att-actions { display: none; gap: 0; }
.attachment:hover .att-actions { display: flex; }
.attachment:hover .att-size { display: none; }
.attachment .att-progress { position: absolute; left: 0; bottom: 0; height: 3px; background: var(--accent); border-radius: 0 0 0 6px; }
.attachment.error { border-color: var(--danger); }
.thread-actions { display: flex; gap: 8px; padding: 8px 16px 16px; flex-wrap: wrap; }
.invite-card { margin: 0 16px 12px; padding: 14px 16px; border: 1px solid var(--border); border-left: 4px solid var(--accent); border-radius: var(--radius-sm); background: var(--bg-sunken); }
.invite-card h4 { margin: 0 0 4px; }
.invite-card .rsvp { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.vcard-card { margin: 0 16px 12px; padding: 12px 16px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-sunken); display: flex; align-items: center; gap: 12px; }
.unsubscribe-row { margin: 0 16px 8px; font-size: .88em; color: var(--fg-muted); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.reply-box { margin: 8px 16px 24px; }
.reply-box .reply-prompt { display: flex; gap: 8px; align-items: center; padding: 12px; border: 1px solid var(--border); border-radius: var(--radius); color: var(--fg-muted); }
.reply-box .reply-prompt button { display: inline-flex; align-items: center; gap: 6px; padding: 8px 14px; border-radius: 999px; border: 1px solid var(--border-strong); color: var(--fg); font-weight: 500; }
.reply-box .reply-prompt button:hover { background: var(--bg-hover); }
.no-thread { height: 100%; display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 8px; color: var(--fg-muted); }
.no-thread img { width: 140px; opacity: .6; filter: grayscale(.3); }
.thread-nav { display: flex; align-items: center; gap: 2px; margin-left: auto; color: var(--fg-muted); font-size: .85em; }
.print-only { display: none; }
/* Labels (keywords) */
.label-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
/* ==========================================================================
Composer
========================================================================== */
.composer-dock { position: fixed; right: 16px; bottom: 0; display: flex; align-items: flex-end; gap: 12px; z-index: 800; pointer-events: none; }
.composer { pointer-events: auto; width: 580px; max-width: calc(100vw - 32px); height: 600px; max-height: calc(100vh - 24px); display: flex; flex-direction: column; background: var(--bg-elev); border-radius: var(--radius-lg) var(--radius-lg) 0 0; box-shadow: var(--shadow-3); border: 1px solid var(--border); border-bottom: 0; overflow: hidden; animation: rise .2s var(--ease); }
.composer.minimized { height: 44px; width: 280px; }
.composer.maximized { position: fixed; inset: 24px; width: auto; height: auto; max-width: none; max-height: none; border-radius: var(--radius-lg); border-bottom: 1px solid var(--border); }
.composer-head { display: flex; align-items: center; gap: 4px; height: 44px; padding: 0 6px 0 14px; background: var(--bg-sunken); border-bottom: 1px solid var(--border); flex: 0 0 auto; cursor: default; }
.composer-head .title { flex: 1; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.composer-head .status { color: var(--fg-faint); font-size: .8em; margin-right: 6px; white-space: nowrap; }
.composer-body { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.composer-fields { flex: 0 0 auto; padding: 0 12px; }
.composer-field { display: flex; align-items: center; gap: 8px; min-height: 40px; border-bottom: 1px solid var(--border); padding: 4px 0; }
.composer-field > label { color: var(--fg-muted); width: 42px; flex: 0 0 auto; font-size: .92em; }
.composer-field .field-extra { display: flex; gap: 4px; color: var(--fg-muted); font-size: .85em; }
.composer-field .field-extra button { color: var(--fg-muted); padding: 2px 6px; border-radius: 4px; }
.composer-field .field-extra button:hover { background: var(--bg-hover); color: var(--fg); }
.composer-field input.plain { flex: 1; border: 0; background: transparent; outline: none; min-width: 80px; height: 30px; }
.composer-field .from-select { flex: 1; border: 0; background: transparent; color: var(--fg); padding: 0; height: 30px; cursor: pointer; }
/*
* A native <select>'s dropdown is painted by the browser from the element's own
* colours, not the page's. `.from-select` is deliberately transparent so it
* sits flush in the composer's From line -- which left its popup with no
* background of its own, so the browser drew a light one while the text kept
* the app's light foreground: light on light, unreadable in any dark theme.
*
* Styling `option` fixes the popup without giving the closed control a box.
* Scoped to every select rather than this one, because nothing else in the app
* styled options either -- the next transparent select would have arrived with
* the same bug.
*/
select option,
select optgroup { background-color: var(--bg-elev); color: var(--fg); }
.recipients { flex: 1; display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-width: 0; position: relative; }
.recipients .chip { height: 24px; }
.recipients input { flex: 1; min-width: 120px; border: 0; background: transparent; outline: none; height: 28px; }
.composer-editor { flex: 1; min-height: 0; display: flex; flex-direction: column; position: relative; }
.editor-area { flex: 1; min-height: 120px; overflow-y: auto; padding: 12px 16px; outline: none; line-height: 1.5; font-size: 14px; font-family: var(--font-sans); }
.editor-area:empty::before, .editor-area[data-empty="true"]::before { content: attr(data-placeholder); color: var(--fg-faint); pointer-events: none; position: absolute; }
.editor-area blockquote { margin: 0 0 0 .8ex; border-left: 2px solid var(--border-strong); padding-left: 1ex; color: var(--fg-muted); }
.editor-area img { max-width: 100%; height: auto; }
.editor-area a { color: var(--link); }
.editor-area .ihm-signature { color: var(--fg-muted); }
.editor-area pre { font-family: var(--font-mono); background: var(--bg-sunken); padding: 8px; border-radius: 6px; overflow: auto; }
.editor-textarea { flex: 1; border: 0; resize: none; outline: none; padding: 12px 16px; font-family: var(--font-mono); font-size: 13.5px; line-height: 1.5; background: transparent; }
.editor-toolbar { display: flex; align-items: center; gap: 1px; flex-wrap: wrap; padding: 4px 8px; border-top: 1px solid var(--border); flex: 0 0 auto; }
.editor-toolbar .icon-btn { width: 30px; height: 30px; border-radius: 6px; }
.editor-toolbar .tb-sep { width: 1px; height: 18px; background: var(--border); margin: 0 4px; }
.editor-toolbar select { height: 28px; border: 0; background: transparent; color: var(--fg-muted); font-size: .85em; max-width: 110px; }
.composer-attachments { display: flex; flex-wrap: wrap; gap: 8px; padding: 8px 12px 0; max-height: 140px; overflow-y: auto; }
.composer-attachments .attachment { width: 200px; padding: 6px 8px; }
.composer-foot { display: flex; align-items: center; gap: 4px; padding: 8px 12px; border-top: 1px solid var(--border); flex: 0 0 auto; }
.composer-foot .send-group { display: flex; }
.composer-foot .send-group .btn:first-child { border-radius: 999px 0 0 999px; padding-left: 20px; }
.composer-foot .send-group .btn:last-child { border-radius: 0 999px 999px 0; padding: 0 8px; border-left: 1px solid rgba(255,255,255,.3); }
.composer-foot .more-actions { display: flex; align-items: center; gap: 2px; margin-left: 8px; }
.composer.dropping .composer-body { outline: 3px dashed var(--accent); outline-offset: -8px; }
.suggest-list { position: absolute; top: 100%; left: 0; min-width: 300px; max-width: 100%; background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: var(--shadow-2); z-index: 20; padding: 4px; max-height: 260px; overflow: auto; }
.suggest-item { display: flex; align-items: center; gap: 10px; padding: 6px 8px; border-radius: 6px; cursor: pointer; }
.suggest-item.active, .suggest-item:hover { background: var(--bg-hover); }
.suggest-item .s-name { font-weight: 550; }
.suggest-item .s-email { color: var(--fg-muted); font-size: .85em; }
.suggest-item .s-src { margin-left: auto; font-size: .72em; color: var(--fg-faint); text-transform: uppercase; letter-spacing: .05em; }
.emoji-grid { display: grid; grid-template-columns: repeat(8, 32px); gap: 2px; padding: 4px; }
.emoji-grid button { font-size: 20px; height: 32px; border-radius: 6px; }
.emoji-grid button:hover { background: var(--bg-hover); }
.link-popup { display: flex; gap: 6px; padding: 8px; }
.color-grid { display: grid; grid-template-columns: repeat(8, 22px); gap: 4px; padding: 6px; }
.color-grid button { width: 22px; height: 22px; border-radius: 4px; border: 1px solid rgba(0,0,0,.1); }
/* ==========================================================================
Settings
========================================================================== */
.settings-layout { display: grid; grid-template-columns: 240px 1fr; height: 100%; min-height: 0; flex: 1; }
.settings-nav { border-right: 1px solid var(--border); padding: 12px 8px; overflow-y: auto; }
.settings-nav .nav-item { margin-right: 0; border-radius: var(--radius-sm); }
.settings-content { overflow-y: auto; padding: 24px 32px 64px; max-width: 860px; }
.settings-content h1 { margin: 0 0 4px; font-size: 1.5em; font-weight: 650; }
.settings-content h2 { margin: 28px 0 12px; font-size: 1.05em; font-weight: 650; padding-bottom: 6px; border-bottom: 1px solid var(--border); }
.settings-content .lead { color: var(--fg-muted); margin: 0 0 20px; }
.card { border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-elev); padding: 14px 16px; margin-bottom: 12px; }
.card.clickable { cursor: pointer; transition: box-shadow .12s; }
.card.clickable:hover { box-shadow: var(--shadow-1); }
.card-head { display: flex; align-items: center; gap: 10px; }
.card-head h3 { margin: 0; font-size: 1em; font-weight: 650; flex: 1; }
.theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; }
.theme-card { border: 2px solid var(--border); border-radius: var(--radius); padding: 8px; cursor: pointer; text-align: center; font-size: .9em; }
.theme-card.active { border-color: var(--accent); }
.theme-card .preview { height: 56px; border-radius: 6px; margin-bottom: 6px; border: 1px solid var(--border); }
.swatches { display: flex; gap: 10px; flex-wrap: wrap; }
.swatch { width: 32px; height: 32px; border-radius: 50%; border: 3px solid transparent; cursor: pointer; }
.swatch.active { border-color: var(--fg); }
.rule-card { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px; margin-bottom: 10px; background: var(--bg-elev); }
.rule-card.disabled { opacity: .6; }
.rule-card.dragging { opacity: .5; }
/* The line shows which side of this card the dragged rule would land on. */
.rule-card.drop-above { box-shadow: inset 0 3px 0 0 var(--accent); }
.rule-card.drop-below { box-shadow: inset 0 -3px 0 0 var(--accent); }
.rule-card .drag-handle { display: flex; align-items: center; padding: 2px; margin-left: -4px; color: var(--fg-faint); cursor: grab; touch-action: none; }
.rule-card .drag-handle:active { cursor: grabbing; }
.rule-row { display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 8px; align-items: center; margin-bottom: 8px; }
.rule-row.actions { grid-template-columns: 1fr 2fr auto; }
/* A header typed by hand needs a box of its own, alongside the comparator. */
.rule-row.named-header { grid-template-columns: 1fr 1fr 1fr 1fr auto; }
.code { font-family: var(--font-mono); font-size: 12.5px; line-height: 1.5; white-space: pre; overflow: auto; background: var(--bg-sunken); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 12px; min-height: 240px; width: 100%; resize: vertical; tab-size: 2; }
.shortcut-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px 32px; }
.shortcut-grid h3 { margin: 0 0 6px; font-size: .9em; text-transform: uppercase; letter-spacing: .05em; color: var(--fg-faint); }
.shortcut-row { display: flex; justify-content: space-between; align-items: center; padding: 4px 0; gap: 12px; }
.shortcut-row .keys { display: flex; gap: 4px; }
.sessions-table { width: 100%; border-collapse: collapse; font-size: .92em; }
.sessions-table th, .sessions-table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); }
.sessions-table th { color: var(--fg-muted); font-weight: 600; font-size: .85em; }
/* ==========================================================================
Contacts
========================================================================== */
.contacts-layout { display: grid; grid-template-columns: 220px minmax(280px, 360px) 1fr; height: 100%; min-height: 0; flex: 1; }
.contacts-books { border-right: 1px solid var(--border); padding: 12px 8px; overflow-y: auto; overflow-x: hidden; }
.contacts-list { border-right: 1px solid var(--border); display: flex; flex-direction: column; min-height: 0; }
.contacts-list .list-search { padding: 10px 12px; border-bottom: 1px solid var(--border); }
.contacts-scroll { flex: 1; overflow-y: auto; }
.contact-row { display: flex; align-items: center; gap: 12px; padding: 8px 14px; cursor: pointer; border-bottom: 1px solid var(--border); }
.contact-row:hover { background: var(--bg-hover); }
.contact-row.active { background: var(--selected-bg); }
.contact-row .c-name { font-weight: 550; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.contact-row .c-email { color: var(--fg-muted); font-size: .85em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.contact-letter { position: sticky; top: 0; background: var(--bg-sunken); padding: 4px 14px; font-size: .78em; font-weight: 700; color: var(--fg-muted); letter-spacing: .05em; z-index: 1; }
.contact-detail { overflow-y: auto; padding: 28px 32px 64px; }
.contact-hero { display: flex; align-items: center; gap: 20px; margin-bottom: 20px; }
.contact-hero h1 { margin: 0; font-size: 1.6em; font-weight: 650; }
.contact-hero .sub { color: var(--fg-muted); }
.contact-section { margin-bottom: 20px; }
.contact-section h3 { margin: 0 0 8px; font-size: .8em; text-transform: uppercase; letter-spacing: .06em; color: var(--fg-faint); }
.contact-kv { display: flex; align-items: flex-start; gap: 12px; padding: 6px 0; }
.contact-kv .k { width: 90px; color: var(--fg-muted); font-size: .9em; flex: 0 0 auto; padding-top: 2px; }
.contact-kv .v { flex: 1; overflow-wrap: anywhere; }
.contact-form .multi { display: flex; flex-direction: column; gap: 8px; }
.contact-form .multi-row { display: grid; grid-template-columns: 1fr 130px auto; gap: 8px; align-items: center; }
.contact-form .multi-row.address { grid-template-columns: 1fr auto; }
.contact-form .addr-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
/* ==========================================================================
Calendar
========================================================================== */
.cal-layout { display: grid; grid-template-columns: 240px 1fr; height: 100%; min-height: 0; }
.cal-side { border-right: 1px solid var(--border); padding: 12px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; }
.cal-main { display: flex; flex-direction: column; min-height: 0; min-width: 0; flex: 1; height: 100%; }
.cal-toolbar { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--border); flex: 0 0 auto; }
.cal-toolbar h2 { margin: 0 8px; font-size: 1.2em; font-weight: 650; min-width: 0; white-space: nowrap; }
.view-switch { display: inline-flex; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); overflow: hidden; }
.view-switch button { padding: 0 12px; height: 34px; font-weight: 500; color: var(--fg-muted); }
.view-switch button.active { background: var(--accent-soft); color: var(--accent-soft-fg); }
.view-switch button:not(:last-child) { border-right: 1px solid var(--border-strong); }
.mini-cal { font-size: .85em; }
.mini-cal .mc-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; font-weight: 650; }
.mini-cal .mc-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; text-align: center; }
.mini-cal .mc-dow { color: var(--fg-faint); font-size: .85em; padding: 2px 0; }
.mini-cal .mc-day { height: 26px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.mini-cal .mc-day:hover { background: var(--bg-hover); }
.mini-cal .mc-day.other { color: var(--fg-faint); }
.mini-cal .mc-day.today { font-weight: 700; color: var(--accent); }
.mini-cal .mc-day.selected { background: var(--accent); color: var(--accent-fg); }
.mini-cal .mc-day.has-events::after { content: ""; display: block; width: 4px; height: 4px; border-radius: 50%; background: var(--accent); position: relative; top: 9px; left: -1px; }
.cal-list-item { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--radius-sm); cursor: pointer; }
.cal-list-item:hover { background: var(--bg-hover); }
.cal-list-item .cal-color { width: 14px; height: 14px; border-radius: 4px; border: 2px solid transparent; flex: 0 0 auto; }
.cal-list-item.hidden-cal .cal-color { background: transparent !important; }
.cal-list-item .cal-name { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cal-list-item .nav-more { opacity: 0; }
.cal-list-item:hover .nav-more { opacity: 1; }
.month-grid { flex: 1; display: grid; grid-template-rows: auto repeat(6, 1fr); min-height: 0; }
.month-grid .dow-row { display: grid; grid-template-columns: repeat(7, 1fr); border-bottom: 1px solid var(--border); }
.month-grid .dow-row div { padding: 6px; text-align: center; font-size: .8em; font-weight: 600; color: var(--fg-muted); text-transform: uppercase; letter-spacing: .04em; }
.month-grid .week-row { display: grid; grid-template-columns: repeat(7, 1fr); min-height: 0; border-bottom: 1px solid var(--border); }
.month-cell { border-right: 1px solid var(--border); padding: 4px; min-width: 0; min-height: 0; display: flex; flex-direction: column; gap: 2px; overflow: hidden; cursor: pointer; position: relative; }
.month-cell:last-child { border-right: 0; }
.month-cell.other { background: var(--bg-sunken); color: var(--fg-faint); }
.month-cell:hover { background: var(--bg-hover); }
.month-cell .day-num { min-width: 26px; height: 26px; padding: 0 4px; display: flex; align-items: center; justify-content: center; border-radius: 999px; font-size: .9em; font-weight: 500; align-self: flex-start; white-space: nowrap; }
.month-cell.today .day-num { background: var(--accent); color: var(--accent-fg); font-weight: 700; }
.month-cell .more { font-size: .78em; color: var(--fg-muted); padding-left: 4px; font-weight: 600; }
.ev-chip { display: flex; align-items: center; gap: 4px; padding: 1px 6px; border-radius: 4px; font-size: .8em; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer; color: #fff; font-weight: 500; flex: 0 0 auto; border: 1px solid transparent; }
.ev-chip.timed { background: transparent !important; color: var(--fg); border-color: transparent !important; }
.ev-chip.timed .ev-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.ev-chip.timed .ev-time { color: var(--fg-muted); }
.ev-chip:hover { filter: brightness(.95); }
.ev-chip.timed:hover { background: var(--bg-hover) !important; }
.ev-chip.declined { text-decoration: line-through; opacity: .6; }
.ev-chip.tentative { border-style: dashed; opacity: .85; }
.ev-chip.cancelled { text-decoration: line-through; opacity: .5; }
.week-view { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.week-head { display: grid; grid-template-columns: 56px repeat(var(--cols, 7), 1fr); border-bottom: 1px solid var(--border); flex: 0 0 auto; }
.week-head .wh-day { padding: 8px 4px 4px; text-align: center; border-left: 1px solid var(--border); cursor: pointer; }
.week-head .wh-day .dow { font-size: .75em; text-transform: uppercase; color: var(--fg-muted); font-weight: 600; }
.week-head .wh-day .dnum { font-size: 1.4em; font-weight: 500; width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; }
.week-head .wh-day.today .dnum { background: var(--accent); color: var(--accent-fg); }
.week-allday { display: grid; grid-template-columns: 56px repeat(var(--cols, 7), 1fr); border-bottom: 1px solid var(--border); min-height: 28px; flex: 0 0 auto; }
.week-allday .ad-cell { border-left: 1px solid var(--border); padding: 2px; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.week-allday .ad-label { font-size: .7em; color: var(--fg-faint); text-align: right; padding: 4px 6px; }
.week-scroll { flex: 1; overflow-y: auto; position: relative; }
.week-body { display: grid; grid-template-columns: 56px repeat(var(--cols, 7), 1fr); position: relative; height: calc(24 * var(--hour-h, 48px)); }
.week-body .time-col { position: relative; }
.week-body .time-col .hour-label { position: absolute; right: 6px; transform: translateY(-50%); font-size: .72em; color: var(--fg-faint); }
.week-body .day-col { position: relative; border-left: 1px solid var(--border); cursor: pointer; }
.week-body .day-col.today { background: color-mix(in srgb, var(--accent-soft) 35%, transparent); }
.week-body .hour-line { position: absolute; left: 0; right: 0; border-top: 1px solid var(--border); pointer-events: none; }
.week-body .half-line { position: absolute; left: 0; right: 0; border-top: 1px dashed color-mix(in srgb, var(--border) 60%, transparent); pointer-events: none; }
.week-body .now-line { position: absolute; left: 0; right: 0; border-top: 2px solid var(--danger); z-index: 5; pointer-events: none; }
.week-body .now-line::before { content: ""; position: absolute; left: -5px; top: -6px; width: 10px; height: 10px; border-radius: 50%; background: var(--danger); }
.week-body .work-hours { position: absolute; left: 0; right: 0; background: color-mix(in srgb, var(--bg-elev) 60%, transparent); pointer-events: none; }
.ev-block { position: absolute; border-radius: 6px; padding: 3px 6px; font-size: .8em; color: #fff; overflow: hidden; cursor: pointer; box-shadow: var(--shadow-1); border: 1px solid rgba(255,255,255,.4); line-height: 1.3; z-index: 2; transition: box-shadow .1s; }
.ev-block:hover { box-shadow: var(--shadow-2); z-index: 3; }
.ev-block .ev-title { font-weight: 650; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ev-block .ev-time { opacity: .9; font-size: .92em; }
.ev-block.declined { opacity: .5; text-decoration: line-through; }
.ev-block.tentative { background-image: repeating-linear-gradient(45deg, rgba(255,255,255,.12) 0 6px, transparent 6px 12px); }
.ev-block.draft-new { opacity: .7; border: 2px dashed #fff; }
.agenda { overflow-y: auto; padding: 12px 16px 64px; }
.agenda-day { display: grid; grid-template-columns: 120px 1fr; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--border); }
.agenda-day .ad-date { font-weight: 600; }
.agenda-day .ad-date.today { color: var(--accent); }
.agenda-day .ad-date small { display: block; color: var(--fg-muted); font-weight: 400; }
.agenda-ev { display: flex; align-items: center; gap: 10px; padding: 6px 8px; border-radius: var(--radius-sm); cursor: pointer; }
.agenda-ev:hover { background: var(--bg-hover); }
.agenda-ev .ev-dot { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; }
.agenda-ev .ev-when { width: 140px; color: var(--fg-muted); font-size: .9em; flex: 0 0 auto; }
.event-popover { width: 360px; max-width: calc(100vw - 24px); padding: 14px 16px; }
.event-popover h3 { margin: 0 0 6px; font-size: 1.15em; font-weight: 650; padding-left: 16px; position: relative; }
.event-popover h3::before { content: ""; position: absolute; left: 0; top: 6px; width: 10px; height: 10px; border-radius: 3px; background: var(--ev-color, var(--accent)); }
.event-popover .ev-line { display: flex; gap: 10px; align-items: flex-start; color: var(--fg-muted); font-size: .92em; margin: 6px 0; }
.event-popover .ev-line svg { flex: 0 0 auto; margin-top: 2px; }
.participant-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: .92em; }
.participant-row .p-status { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; }
.p-status.accepted { background: var(--success); } .p-status.declined { background: var(--danger); } .p-status.tentative { background: var(--warn); } .p-status.needs-action { background: var(--fg-faint); }
.event-form .time-row { display: grid; grid-template-columns: 1fr auto 1fr; gap: 8px; align-items: center; }
.event-form .alerts-list { display: flex; flex-direction: column; gap: 6px; }
.event-form .alerts-list .row { gap: 6px; }
.freebusy { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.freebusy .fb-row { display: flex; align-items: center; gap: 8px; font-size: .85em; }
.freebusy .fb-bar { flex: 1; height: 14px; background: var(--bg-sunken); border-radius: 4px; position: relative; overflow: hidden; }
.freebusy .fb-busy { position: absolute; top: 0; bottom: 0; background: var(--danger); opacity: .65; }
.freebusy .fb-window { position: absolute; top: 0; bottom: 0; border: 2px solid var(--accent); border-radius: 3px; }
/* ==========================================================================
Files
========================================================================== */
.files-layout { display: flex; flex-direction: column; height: 100%; min-height: 0; flex: 1; }
.files-toolbar { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.breadcrumb { display: flex; align-items: center; gap: 4px; flex: 1; min-width: 0; overflow: hidden; }
.breadcrumb button { padding: 4px 8px; border-radius: 6px; color: var(--fg-muted); font-weight: 500; white-space: nowrap; }
.breadcrumb button:hover { background: var(--bg-hover); color: var(--fg); }
.breadcrumb button.current { color: var(--fg); font-weight: 650; }
.files-table { width: 100%; border-collapse: collapse; }
.files-table th { text-align: left; padding: 8px 12px; font-size: .8em; color: var(--fg-muted); font-weight: 600; border-bottom: 1px solid var(--border); position: sticky; top: 0; background: var(--bg-elev); z-index: 1; }
.files-table td { padding: 8px 12px; border-bottom: 1px solid var(--border); }
.files-table tr { cursor: pointer; }
.files-table tbody tr:hover { background: var(--bg-hover); }
.files-table tr.selected { background: var(--selected-bg); }
.files-table .f-name { display: flex; align-items: center; gap: 10px; font-weight: 500; }
.files-table .f-name svg { color: var(--fg-muted); }
.files-scroll { flex: 1; overflow: auto; }
.files-layout.dropping .files-scroll { outline: 3px dashed var(--accent); outline-offset: -10px; }
/* ==========================================================================
Login
========================================================================== */
.login-page { min-height: 100%; display: flex; align-items: center; justify-content: center; padding: 24px; background: radial-gradient(1200px 600px at 10% -10%, var(--accent-soft), transparent 60%), radial-gradient(900px 500px at 110% 110%, var(--accent-soft), transparent 60%), var(--bg); }
.login-card { width: 100%; max-width: 400px; background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-2); padding: 32px 28px 24px; animation: rise .25s var(--ease); }
.login-card .logo { display: flex; flex-direction: column; align-items: center; gap: 6px; margin-bottom: 20px; }
.login-card .logo img { width: 120px; height: auto; }
/* The logo is the mark alone, so the name is set here rather than baked in. */
.login-card h1 { margin: 0; font-size: 1.6em; font-weight: 800; letter-spacing: -.02em; color: #14b8a6; }
.login-card .sub { color: var(--fg-muted); font-size: .92em; margin: 0; }
.login-card .tagline { color: var(--fg-muted); font-size: 1em; margin: 4px 0 0; text-align: center; }
.login-card .foot a { color: var(--fg-muted); text-decoration: underline; }
.login-card .foot { margin-top: 20px; font-size: .8em; color: var(--fg-faint); text-align: center; }
.login-card .pw-wrap { position: relative; }
.login-card .pw-wrap .icon-btn { position: absolute; right: 2px; top: 1px; }
/* ==========================================================================
Responsive
========================================================================== */
@media (max-width: 1100px) {
:root { --list-w: 44%; }
.msg-row .msg-from { width: 140px; }
.contacts-layout { grid-template-columns: 200px 300px 1fr; }
}
@media (max-width: 900px) {
.mail-layout:not(.pane-off):not(.pane-bottom) { grid-template-columns: 1fr !important; }
.mail-layout.pane-bottom { grid-template-rows: 1fr !important; }
.mail-layout.pane-bottom .mail-reading-pane { display: none; }
.mail-layout.pane-bottom.reading .mail-list-pane { display: none; }
.mail-layout.pane-bottom.reading .mail-reading-pane { display: flex; }
.mail-layout .splitter { display: none; }
.mail-layout:not(.pane-bottom) .mail-reading-pane { display: none; }
.mail-layout.reading .mail-list-pane { display: none; }
.mail-layout.reading .mail-reading-pane { display: flex; }
.settings-layout { grid-template-columns: 1fr; }
.settings-nav { display: none; }
.settings-layout.section .settings-nav { display: none; }
.settings-layout.root .settings-nav { display: block; border-right: 0; }
.settings-layout.root .settings-content { display: none; }
.settings-content { padding: 16px 16px 80px; }
.contacts-layout { grid-template-columns: 1fr; }
.contacts-books { display: none; }
.contacts-layout.detail .contacts-list { display: none; }
.contacts-layout:not(.detail) .contact-detail { display: none; }
.cal-layout { grid-template-columns: 1fr; }
.cal-side { display: none; }
}
@media (max-width: 768px) {
:root { --topbar-h: 52px; }
.app-body, .app-body.collapsed { grid-template-columns: 1fr; }
.sidebar { position: fixed; top: 0; bottom: 0; left: 0; width: min(300px, 85vw); background: var(--bg-elev); z-index: 950; transform: translateX(-105%); transition: transform .22s var(--ease); box-shadow: var(--shadow-3); padding-top: 12px; }
.sidebar.open { transform: none; }
.collapsed .sidebar .nav-item, .collapsed .sidebar .compose-btn { all: revert; }
.collapsed .module-bar { flex-direction: row; }
.collapsed .module-link { width: auto; height: auto; padding: 6px 2px; border-radius: var(--radius-sm); }
.collapsed .module-link .module-label { display: block; }
.drawer-backdrop { display: block; position: fixed; inset: 0; z-index: 940; background: rgba(2,6,23,.45); opacity: 0; pointer-events: none; transition: opacity .2s; }
.drawer-backdrop.open { opacity: 1; pointer-events: auto; }
.main { border-radius: 0; border: 0; }
.topbar { padding: 0 6px; gap: 4px; }
.topbar .brand .brand-name { display: none; }
.searchbar .search-input { height: 40px; }
.topbar-actions .hide-mobile { display: none; }
.msg-row { padding-right: 8px; }
.mail-list { padding-bottom: 72px; }
.composer { width: 100vw; max-width: 100vw; height: 100vh; max-height: 100vh; border-radius: 0; position: fixed; inset: 0; }
.composer.minimized { height: 44px; width: 100vw; top: auto; bottom: 0; }
.composer-dock { right: 0; }
.dialog { max-height: calc(100vh - 16px); }
.fab { display: flex; position: fixed; right: 18px; bottom: 78px; width: 56px; height: 56px; border-radius: 18px; background: var(--accent); color: var(--accent-fg); align-items: center; justify-content: center; box-shadow: var(--shadow-2); z-index: 700; }
.mobile-tabbar { display: grid; grid-template-columns: repeat(4, 1fr); position: fixed; left: 0; right: 0; bottom: 0; height: 60px; background: var(--bg-elev); border-top: 1px solid var(--border); z-index: 700; padding-bottom: env(safe-area-inset-bottom); }
.mobile-tabbar a { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; color: var(--fg-muted); text-decoration: none; font-size: 11px; }
.mobile-tabbar a.active { color: var(--accent); }
.main { padding-bottom: 60px; }
.thread-subject { padding: 14px 16px 8px; }
.message { margin: 0 8px 8px; }
.message-head { padding: 10px 12px; }
.message-body { padding: 4px 12px 12px; }
.thread-toolbar .hide-mobile { display: none; }
.list-toolbar .hide-mobile { display: none; }
.week-head .wh-day .dnum { width: 32px; height: 32px; font-size: 1.1em; }
.cal-toolbar h2 { font-size: 1em; }
.contact-detail { padding: 16px 16px 80px; }
.files-table .hide-mobile { display: none; }
.event-popover { width: calc(100vw - 24px); }
}
@media (hover: none) {
.msg-row .msg-check { opacity: 1; }
.msg-row .msg-actions { display: none !important; }
.msg-row:hover .msg-meta .msg-date { display: inline; }
.nav-item .nav-more, .cal-list-item .nav-more { opacity: 1; }
}
@media print {
.topbar, .sidebar, .mail-list-pane, .thread-toolbar, .composer-dock, .toast-host, .mobile-tabbar, .fab, .reply-box, .thread-actions, .quote-toggle { display: none !important; }
.app, .app-body, .main, .mail-layout, .mail-reading-pane, .thread-view, .thread-scroll { display: block !important; height: auto !important; overflow: visible !important; grid-template-columns: 1fr !important; border: 0 !important; }
.message { break-inside: avoid; border: 1px solid #ccc; }
.print-only { display: block; }
body { background: #fff; color: #000; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
/* Addresses in a message carry a right-click menu (see mail/AddressMenu.tsx). */
.addr { cursor: context-menu; }
.message-details .addr:hover, .message-head .from .addr:hover { text-decoration: underline dotted; text-underline-offset: 2px; }
/* ---- Date & time fields (custom pickers; see ui/datefield.tsx) ---- */
.dp-field { position: relative; display: inline-flex; align-items: center; width: 100%; }
.dp-field .input { width: 100%; padding-right: 30px; }
.dp-open { position: absolute; right: 4px; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border: 0; background: none; color: var(--fg-muted); cursor: pointer; border-radius: var(--radius-sm); }
button.dp-open:hover { background: var(--bg-hover); color: var(--fg); }
button.dp-open:disabled { cursor: default; opacity: .5; }
.dp-field.w-auto { width: auto; }
.dp-field.w-auto .input { width: 10em; }
.dp-datetime { display: flex; gap: 6px; align-items: center; }
.dp-datetime .dp-field { flex: 1 1 auto; }
.dp-datetime .dp-time-field { flex: 0 0 8.5em; }
.dp-pop { padding: 10px; }
.dp-pop-wide { display: flex; }
.dp-split { display: flex; gap: 10px; align-items: stretch; }
.dp-cal { width: 15.5em; }
.dp-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; font-weight: 650; }
.dp-dow, .dp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; text-align: center; }
.dp-dow { color: var(--fg-faint); font-size: .8em; padding-bottom: 2px; }
.dp-day { height: 28px; border: 0; background: none; color: inherit; font: inherit; font-size: .9em; border-radius: 50%; cursor: pointer; }
.dp-day:hover { background: var(--bg-hover); }
.dp-day:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.dp-day.other { color: var(--fg-faint); }
.dp-day.today { font-weight: 700; color: var(--accent); }
.dp-day.selected { background: var(--accent); color: var(--accent-fg); font-weight: 600; }
.dp-foot { display: flex; justify-content: space-between; margin-top: 6px; }
.dp-times { display: flex; flex-direction: column; gap: 1px; overflow-y: auto; max-height: 17em; min-width: 7.5em; border-left: 1px solid var(--border); padding-left: 8px; }
.dp-time { border: 0; background: none; color: inherit; font: inherit; font-size: .9em; text-align: left; padding: 4px 8px; border-radius: var(--radius-sm); cursor: pointer; white-space: nowrap; }
.dp-time:hover { background: var(--bg-hover); }
.dp-time.selected { background: var(--accent); color: var(--accent-fg); }
@media (max-width: 480px) {
.dp-datetime { flex-wrap: wrap; }
.dp-datetime .dp-time-field { flex: 1 1 100%; }
.dp-split { flex-direction: column; }
.dp-times { flex-direction: row; overflow-x: auto; max-height: none; border-left: 0; border-top: 1px solid var(--border); padding: 6px 0 0; }
}
/* Files: the sidebar tree reuses .nav-item, so only the parts the mail tree has
no equivalent for are here. A row in the list is a drop target the same way a
folder in the tree is, and says so the same way. */
.files-table tbody tr.drop-target > td { background: var(--accent-soft); }
.files-table tbody tr.drop-target > td:first-child { box-shadow: inset 2px 0 0 var(--accent); }
.files-table tbody tr[draggable="true"] { cursor: grab; }
.files-table tbody tr[draggable="true"]:active { cursor: grabbing; }
.sidebar .nav-item[draggable="true"] { cursor: pointer; }
.f-name .faint { flex: none; }
/* The "Shared with me" header carries a refresh control, so it is a row rather
than the plain label the other sections use. */
.sidebar .nav-section { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.spin { animation: spin 1s linear infinite; }
@media (prefers-reduced-motion: reduce) { .spin { animation: none; } }
/* The composer's To label doubles as the way into the address books. */
.composer-field label .link-btn { background: none; border: 0; padding: 0; font: inherit; color: inherit; cursor: pointer; text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 3px; }
.composer-field label .link-btn:hover { color: var(--accent); }
.composer-field label .link-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }