/* ===== Global cursor & selection rules ============================
   App chrome reads as labels, not editable content. The CSS `cursor`
   property is NOT inherited by default - each element falls back to
   `cursor: auto` which the browser then computes per-element type,
   showing the I-beam over any text node. Setting `cursor: default` on
   <body> alone doesn't propagate. The fix: force every element to
   inherit cursor via the universal selector, so the body-level default
   actually applies everywhere. Then re-opt-in to the I-beam on inputs
   / contenteditable, and to pointer on buttons / anchors. */
* { cursor: inherit; }
body { cursor: default; }
a, button, [role="button"], summary, label[for], select,
.btn, .nav-link, .dropdown-item, .dropdown-toggle,
[data-bs-toggle], [data-coworker-trigger], [data-user-profile-id],
[data-reply-target] {
    cursor: pointer;
}
input, textarea, [contenteditable="true"], [contenteditable=""] {
    cursor: text;
}
input[type="checkbox"], input[type="radio"], input[type="file"],
input[type="submit"], input[type="button"], input[type="reset"],
input[type="range"], input[type="color"] {
    cursor: pointer;
}
button:disabled, .btn:disabled,
input:disabled, select:disabled, textarea:disabled,
[aria-disabled="true"] {
    cursor: not-allowed;
}
/* GLOBAL RULE: app chrome (titles, labels, list rows, button text,
   nav, badges, modal content) is NOT selectable by default. The
   per-class opt-out list this replaced was always playing catch-up
   to new components - the team-row name/email kept slipping through.
   Inverting the default: opt OUT globally, opt IN only inside
   genuinely user-content surfaces below. */
body {
    user-select: none;
    -webkit-user-select: none;
}

/* Selection allowed inside actual content / editable surfaces:
   form inputs, contenteditable nodes, code/keyboard/blockquote
   blocks, and the explicit `.is-prose` opt-in class for any
   long-form copy a feature wants users to be able to copy
   (message body, note body, AI output, etc). */
input, textarea, select, [contenteditable="true"], [contenteditable=""],
.is-prose, .is-prose *,
.message-body, .note-body, .email-body,
.msg-body, .msg-body *,
pre, code, kbd, samp, blockquote {
    cursor: text;
    user-select: text;
    -webkit-user-select: text;
}

/* PLATFORM-WIDE: interactive controls placed INSIDE a user-content
   surface (.msg-body, .note-body, etc.) must NOT inherit the text
   cursor / selectable text from their wrapper. Without this,
   placing any <button> / [role="button"] / .btn inside a message
   body, note body, or .is-prose region would give the wrong
   affordance: clicking briefly drag-selects nearby text and the
   cursor reads as I-beam over the label. The selector intentionally
   beats the wildcard rule above on specificity by anchoring to a
   class + a tag/attribute. The descendant-of-button rule that
   follows pushes the same override one level deeper so button
   children (svg icons, label spans) match. */
.msg-body button, .msg-body [role="button"], .msg-body .btn,
.note-body button, .note-body [role="button"], .note-body .btn,
.email-body button, .email-body [role="button"], .email-body .btn,
.message-body button, .message-body [role="button"], .message-body .btn,
.is-prose button, .is-prose [role="button"], .is-prose .btn,
.msg-body button *, .msg-body [role="button"] *, .msg-body .btn *,
.note-body button *, .note-body [role="button"] *, .note-body .btn *,
.email-body button *, .email-body [role="button"] *, .email-body .btn *,
.message-body button *, .message-body [role="button"] *, .message-body .btn *,
.is-prose button *, .is-prose [role="button"] *, .is-prose .btn * {
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
}

/* Chips inside chip-input must allow click-to-remove without
   accidentally selecting their text. */
.chip-input .chip-input-chip { user-select: none; -webkit-user-select: none; }

:root {
    --bg: #f8fafc;
    --surface: #ffffff;
    --surface-2: #f1f5f9;
    --border: #e2e8f0;
    --border-strong: #cbd5e1;
    --text: #0f172a;
    --text-muted: #64748b;
    --text-subtle: #94a3b8;
    --primary: #4f46e5;
    --primary-hover: #4338ca;
    --primary-bg: #eef2ff;
    --danger: #dc2626;
    --danger-bg: #fef2f2;
    --warning: #d97706;
    --warning-bg: #fffbeb;
    --success: #059669;
    --success-bg: #ecfdf5;
    --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04), 0 1px 3px rgba(15, 23, 42, 0.06);
    --shadow: 0 4px 6px -2px rgba(15, 23, 42, 0.05), 0 10px 15px -3px rgba(15, 23, 42, 0.08);
    /* Single source of truth for the "letter-initial bubble"
       appearance - the diagonal indigo-to-purple gradient with white
       glyph used for every user / workspace / team avatar fallback
       across the app. Every surface (workspace cards, share modal,
       chip pickers, profile card hero, mention popovers, …) reads
       this variable so changing the brand once here propagates
       everywhere instead of grepping a dozen call sites. */
    --avatar-bg: linear-gradient(135deg, var(--primary), #7c3aed);
    --avatar-color: #fff;
}

html[data-theme="dark"] {
    --bg: #0b1220;
    --surface: #0f172a;
    --surface-2: #1e293b;
    --border: #1f2937;
    --border-strong: #334155;
    --text: #e5e7eb;
    --text-muted: #94a3b8;
    --text-subtle: #64748b;
    --primary: #818cf8;
    --primary-hover: #a5b4fc;
    --primary-bg: #1e1b4b;
    --danger: #f87171;
    --danger-bg: #3f1212;
    --warning: #fbbf24;
    --warning-bg: #402a0b;
    --success: #34d399;
    --success-bg: #0b2a20;
    --shadow-sm: 0 1px 2px rgba(0,0,0,.3);
    --shadow: 0 4px 12px rgba(0,0,0,.4);
}
html[data-theme="dark"] .app-nav .navbar { background: var(--surface) !important; border-bottom-color: var(--border); }
html[data-theme="dark"] .app-nav .navbar-brand { color: var(--text) !important; }

/* -------------------------------------------------------------------------
   Generic cursor policy.

   The browser default shows the I-beam (text cursor) over any text-bearing
   element, even read-only display surfaces like note bodies, activity
   feeds, and sidebar labels. That visual hint says "you can edit this"
   when in fact you can't - users find it counter-intuitive and have asked
   for it to go away.

   Body gets the regular arrow cursor by default; only actual editable
   inputs opt back into the I-beam. The :where() wrapper keeps specificity
   at zero so any explicit cursor: pointer/grab/etc rule still wins.

   Selection still works (you can still drag-select a note body to copy it)
   because user-select isn't touched here. Only the cursor visual changes.
   ------------------------------------------------------------------------- */
body { cursor: default; }
:where(input):not([type="button"], [type="submit"], [type="reset"], [type="checkbox"], [type="radio"], [type="file"], [type="color"], [type="range"], [type="image"]),
:where(textarea),
:where([contenteditable=""], [contenteditable="true"]) {
    cursor: text;
}

/* -------------------------------------------------------------------------
   Global text-selection policy.

   Chrome text is NOT selectable by default - clicking a paragraph, button
   label, modal description, sidebar row, etc. shouldn't drop a blinking
   caret or highlight letters. That was the repeated complaint ("edit
   cursor when I click on text"). Text-selection stays ON only where the
   user actually wants to copy content: message subjects / bodies / headers,
   sender / recipient addresses, notes, labels, and explicit form inputs.

   Ordering matters: the `text`/`auto` overrides below have to sit AFTER the
   global `none` to win the cascade. Do not move this block.
   ------------------------------------------------------------------------- */
html, body {
    -webkit-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

/* Inputs / textareas / editable areas: browsers use their own selection
   model here, but declaring explicitly keeps the intent clear and
   future-proofs against any theme that tries to disable it. */
input, textarea, select, [contenteditable="true"], [contenteditable=""] {
    -webkit-user-select: auto;
    -ms-user-select: auto;
    user-select: auto;
}

/* Email content - the ONE place the user said selection must stay on.
   NOTE: .mail-snippet / .mail-from / .mail-subject are deliberately
   EXCLUDED here even though they're text. Those are list-row labels
   on /Mail (sender / subject / preview snippet) and the row itself
   is a clickable navigation target. Marking them user-selectable
   made browsers paint the text-edit (I-beam) cursor on hover, which
   the user said reads as "this is editable text" and breaks the
   click affordance. The row's pointer cursor now wins, and a user
   who really wants to select a sender name can still drag-select
   within the row body since user-select isn't none, just default. */
.msg-body, .msg-body *,
.msg-body-host, .msg-body-host *,
.msg-subject, .msg-headers, .msg-headers *,
.account-email,
.note-body, .note-body *,
[data-selectable="true"], [data-selectable="true"] * {
    -webkit-user-select: text;
    -ms-user-select: text;
    user-select: text;
}

/* Mail-list row text: force the click cursor on every text child so
   the row reads as a single clickable unit. cursor isn't inherited
   by default so each child element defaults to `auto` → text I-beam
   on text nodes; an explicit `cursor: pointer` here matches the
   `.mail-row > .mail-content` declaration further down. */
.mail-row,
.mail-row *,
.mail-from,
.mail-subject,
.mail-snippet {
    cursor: pointer;
}
/* But leave the per-control overrides alone - checkbox, star, and
   the per-row "open in new tab" icon each set their own cursor and
   should NOT inherit pointer-over-everything. */
.mail-row input[type="checkbox"],
.mail-row .mail-star,
.mail-row .mail-open-tab {
    cursor: default;
}
.mail-row .mail-star { cursor: pointer; }
.mail-row .mail-open-tab { cursor: pointer; }
/* (Legacy .mail-row hover/unread rules removed - new zebra-aware rules live
   alongside .mail-row elsewhere and would otherwise get overridden here.) */

/* Theme-aware scrollbars - subtle, no system default white/gray bars in dark mode */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
}
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
    background: var(--border-strong);
    border-radius: 10px;
    border: 2px solid transparent;
    background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover {
    background: var(--text-muted);
    background-clip: content-box;
}
*::-webkit-scrollbar-corner { background: transparent; }

html[data-theme="dark"] *::-webkit-scrollbar-thumb { background: #334155; background-clip: content-box; }
html[data-theme="dark"] *::-webkit-scrollbar-thumb:hover { background: #475569; background-clip: content-box; }
html[data-theme="dark"] { color-scheme: dark; }
html { color-scheme: light; }

/* Bootstrap utilities that default to a dark gray - unreadable on dark surfaces.
   Route them through our theme token so they stay legible in both modes. */
html[data-theme="dark"] .text-muted,
html[data-theme="dark"] .form-text,
html[data-theme="dark"] .text-secondary { color: var(--text-muted) !important; }

* { box-sizing: border-box; }

html, body {
    height: 100%;
    margin: 0;
    padding: 0;
    overflow: hidden;
    background: var(--bg);
    color: var(--text);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: 14px;
    line-height: 1.5;
}

body { display: flex; flex-direction: column; }

/* Navbar */
.app-nav {
    flex: 0 0 auto;
    /* Own stacking context so dropdowns + stray overlays from page content
       below can never visually cover the nav, making links unclickable. */
    position: relative;
    z-index: 100;
}
.app-nav .navbar {
    background: #ffffff !important;
    border-bottom: 1px solid var(--border);
    padding: .5rem 1.5rem;
}
.app-nav a, .app-nav button { pointer-events: auto; }
.app-nav .navbar-brand {
    color: var(--text) !important;
    font-weight: 700;
    letter-spacing: -.01em;
    font-size: 1rem;
}
/* Brand mark = icon + wordmark. The wordmark uses Plus Jakarta Sans
   (loaded in _Layout.cshtml) at weight 800 so "Burofolk" reads as a
   logotype. Slightly tightened tracking + a tiny gap from the icon
   keeps the unit cohesive. */
.brand-mark {
    display: inline-flex; align-items: center; gap: .55rem;
    padding: 0 !important;
    line-height: 1;
}
.brand-mark-icon {
    flex: 0 0 auto;
    display: block;
    transition: transform .35s cubic-bezier(.34, 1.56, .64, 1);
}
.brand-mark:hover .brand-mark-icon { transform: rotate(-6deg) scale(1.06); }
.brand-mark-wordmark {
    font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
    font-weight: 800;
    font-size: 1.18rem;
    letter-spacing: -.02em;
    color: var(--text);
    line-height: 1;
}
.app-nav .nav-link {
    color: var(--text-muted) !important;
    font-weight: 500;
    font-size: .9rem;
    padding: .4rem .75rem !important;
    border-radius: 6px;
}
.app-nav .nav-link:hover { background: var(--surface-2); color: var(--text) !important; }

/* Premium pill-style primary nav */
.app-primary-nav { gap: .25rem; }
.app-nav .nav-link-pill {
    display: inline-flex; align-items: center; gap: .45rem;
    padding: .45rem .75rem !important;
    border-radius: 8px;
    font-size: .88rem; font-weight: 500;
    color: var(--text) !important;
    background: transparent;
    transition: background .12s ease, color .12s ease;
}
.app-nav .nav-link-pill svg { color: var(--text-subtle); transition: color .12s ease; }
.app-nav .nav-link-pill:hover { background: var(--surface-2); }
.app-nav .nav-link-pill:hover svg { color: var(--primary); }
.app-nav .nav-link-pill.is-active { background: var(--primary-bg); color: var(--primary) !important; }
.app-nav .nav-link-pill.is-active svg { color: var(--primary); }
.app-nav .nav-link-pill.is-active .nav-pill-count { background: var(--primary); color: #fff; }
.app-nav .nav-link-pill.dropdown-toggle::after {
    margin-left: .3rem; border-top-color: var(--text-subtle);
}
.app-nav .nav-link-pill .nav-pill-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 18px; height: 18px; padding: 0 5px;
    background: var(--primary-bg); color: var(--primary);
    border-radius: 9px; font-size: .7rem; font-weight: 600;
    margin-left: .15rem;
}

/* Accounts nav link - swap the mailbox glyph for a spinner while any
   account is actively syncing, so the nav itself signals "work in progress"
   across browser tabs. Static look by default, animated pulse when active. */
.nav-workspaces-icon {
    position: relative;
    display: inline-flex; align-items: center; justify-content: center;
    width: 14px; height: 14px;
}
.nav-workspaces-icon .nav-workspaces-mailbox {
    display: block;
}
.nav-workspaces-icon .nav-workspaces-spinner {
    position: absolute; inset: 0;
    border-radius: 50%;
    border: 2px solid transparent;
    border-top-color: currentColor;
    border-right-color: currentColor;
    opacity: 0;
    animation: nav-workspaces-spin .9s linear infinite;
}
.app-nav .nav-link-pill.is-syncing .nav-workspaces-mailbox { opacity: 0; }
.app-nav .nav-link-pill.is-syncing .nav-workspaces-spinner { opacity: 1; }
.app-nav .nav-link-pill.is-syncing { color: var(--primary) !important; }
.app-nav .nav-link-pill.is-syncing svg { color: var(--primary); }
@keyframes nav-workspaces-spin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

/* Dropdowns - soft, elevated, premium look */
.app-nav .dropdown-menu {
    border: 1px solid var(--border);
    box-shadow: 0 12px 32px rgba(15, 23, 42, .14);
    border-radius: 12px;
    padding: .35rem;
    min-width: 260px;
    margin-top: .5rem;
    background: var(--surface);
    /* Breathing room around the dropdown body so items don't butt up
       against the rounded border at the top and bottom. Bootstrap's
       default is 0 padding which looks cramped with our custom items. */
    padding: .35rem;
}
.app-nav .dropdown-header {
    font-size: .7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--text-subtle);
    padding: .5rem .75rem .4rem;
}
.app-nav .dropdown-item {
    font-size: .88rem; color: var(--text);
    padding: .55rem .7rem; border-radius: 8px;
    display: flex; align-items: center; gap: .7rem;
}
.app-nav .dropdown-item + .dropdown-item { margin-top: 2px; }
.app-nav .dropdown-item:hover, .app-nav .dropdown-item:focus {
    background: var(--surface-2); color: var(--text);
}
.app-nav .dropdown-divider { margin: .4rem .25rem; border-top-color: var(--border); }

/* Archive mailbox picker - avatar + label + email + trailing indicator.
   Width hugs content (tight min so very first char anchors) and items get
   a clear active / hover treatment so the user can tell at a glance which
   workspace is already open. */
.app-archive-menu { min-width: 260px; }
.app-archive-item {
    /* Two-line rows need vertical breathing room; gap between avatar
       and text matches the spacing used on workspace cards / collab
       rows so the picker feels part of the same system. */
    padding: .6rem .8rem !important;
    gap: .75rem !important;
    position: relative;
    align-items: center !important;
}
.app-archive-avatar {
    width: 36px; height: 36px; border-radius: 50%;
    background: var(--avatar-bg);
    color: var(--avatar-color);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 600; font-size: .95rem; flex: 0 0 auto;
}
/* Emoji glyphs render small inside a bubble sized for a single capital
   letter. has-emoji opts into a bigger font + transparent background so
   the picked icon dominates the slot. */
.app-archive-avatar.has-emoji {
    background: var(--surface-2); font-size: 1.25rem;
    border: 1px solid var(--border);
    box-shadow: 0 2px 6px rgba(99, 102, 241, 0.18);
}
/* SVG line-icon variant: subtle bordered slot, monochrome stroke in the
   primary color. The stroked SVG carries its own visual weight, so we
   drop the saturated gradient the first-letter bubble uses. */
.app-archive-avatar.has-svg {
    background: var(--primary-bg); color: var(--primary);
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
}
.app-archive-avatar.has-svg svg { display: block; }
.quick-open-avatar.has-svg {
    background: var(--primary-bg); color: var(--primary);
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
}
.quick-open-avatar.has-svg svg { display: block; }
.app-archive-meta { display: flex; flex-direction: column; gap: 1px; min-width: 0; line-height: 1.3; flex: 1 1 auto; }
.app-archive-email {
    color: var(--text);
    font-weight: 600;
    font-size: .92rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    max-width: 240px;
}
.app-archive-count {
    color: var(--text-subtle);
    font-size: .76rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    max-width: 240px;
}

/* Trailing chevron reveals on hover/focus. Keeps the item clean at rest
   but gives the user feedback that it's navigable. */
.app-archive-arrow {
    flex: 0 0 auto;
    color: var(--text-subtle);
    opacity: 0;
    transform: translateX(-4px);
    transition: opacity 120ms ease, transform 120ms ease, color 120ms ease;
    display: inline-flex;
}
.app-archive-item:hover .app-archive-arrow,
.app-archive-item:focus-visible .app-archive-arrow {
    opacity: 1;
    transform: translateX(0);
    color: var(--primary);
}

/* Check-mark on the currently-open workspace. Replaces the chevron -
   tells the user "you're already here" without making them read labels. */
.app-archive-current {
    flex: 0 0 auto;
    width: 20px; height: 20px; border-radius: 50%;
    background: var(--primary-bg);
    color: var(--primary);
    display: inline-flex; align-items: center; justify-content: center;
}

/* Active-row treatment - subtle tinted background + primary label. */
.app-archive-item.is-current {
    background: color-mix(in srgb, var(--primary) 6%, transparent);
}
.app-archive-item.is-current .app-archive-email { color: var(--primary); }

.app-archive-manage { color: var(--text-muted) !important; }
.app-archive-manage svg { color: currentColor; }

/* Right-side tools (search / theme / prefs / user menu).
   The vertical divider that visually separates the primary nav
   from the tool strip used to live on the LEFT edge of this
   container. With the presence cluster ("buddies" stack) now
   sitting at the start of the tools strip, that put the divider
   AT THE LEFT of the buddies - which read as "buddies belong with
   the workspace nav" instead of "buddies + tools form the right
   group". The presence-cluster <li> now owns its own border-right
   (see .nav-presence-li below), so the divider correctly sits to
   the RIGHT of the buddies, separating them from Actions / bell /
   profile. We keep a small left padding here for breathing room
   from the primary nav. */
.app-nav .nav-tools {
    display: flex;
    align-items: center;
    gap: 2px;
    padding-left: .5rem;
    margin-left: .5rem;
}
.app-nav .nav-icon-btn {
    width: 34px;
    height: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 8px;
    cursor: pointer;
    color: var(--text-muted);
    transition: background .12s ease, color .12s ease, border-color .12s ease;
    padding: 0;
}
.app-nav .nav-icon-btn:hover,
.app-nav .nav-icon-btn:focus-visible {
    background: var(--surface-2);
    color: var(--text);
    border-color: var(--border);
    outline: none;
}
.app-nav .nav-icon-btn:active { transform: translateY(0.5px); }
.app-nav .nav-icon-btn svg { display: block; }

/* Bell with unread-mention count. Anchors the little pill that floats
   above the top-right of the icon, matching the visual weight of the
   other nav icons. When no mentions are unread the pill is hidden and
   the bell stays the same muted color as the theme-toggle next to it. */
/* ===== Mobile navbar drawer (below the md breakpoint) =================
   Default Bootstrap stacks every list-item vertically and centers them
   weirdly - the icons end up floating in the middle of the page next to
   the left-aligned link list. Below the .navbar-expand-md breakpoint we
   restyle the collapse target as a proper drawer:
     - Brand stays on the top bar with the toggler.
     - Drawer items each get full row: icon + readable label.
     - The "icon strip" (Activity / Bell / Approvals / Theme) stays in a
       single horizontal row at the bottom so they don't take a full
       row each just to expose 4 icons.
     - User menu becomes a clear full-width row that opens a dropdown
       in place. */
/* ============================================================
   Mobile navbar drawer (below md breakpoint).

   Replaces Bootstrap's inline collapse with a proper slide-in
   drawer from the right: full-height fixed panel, dimmed
   backdrop, body-scroll lock, full-width row treatment for every
   nav item, inline dropdowns. Driven by the same .show class
   Bootstrap toggles on the collapse target - we just override
   the visual completely.

   The drawer is ALWAYS present in the DOM (display: flex !important
   to override Bootstrap's display: none) but lives off-screen via
   transform: translateX(100%) plus visibility: hidden. The .show
   class slides it in. The collapsing intermediate state gets the
   same transform so Bootstrap's height animation doesn't fight
   the slide.
   ============================================================ */
/* Drawer chrome (head, backdrop) is JS-injected into the navbar
   collapse element for mobile. On desktop the navbar renders
   inline and these extras would leak into the bar - hide them by
   default and re-show inside the mobile media query below.
   Same goes for the user-row enhancements (email subline,
   chevron) - hidden on desktop, surfaced in the drawer. */
.nav-drawer-head,
.nav-drawer-backdrop,
.app-nav .user-menu .user-email-sub,
.app-nav .user-menu .user-chev { display: none; }
/* user-stack is a transparent wrapper on desktop so the inline
   .user-email span retains its layout. */
.app-nav .user-menu .user-stack { display: contents; }

@media (max-width: 767.98px) {
    /* Make sure the toggler stays on top of the drawer. */
    .app-nav .navbar-toggler {
        position: relative;
        z-index: 1052;
        border: 1px solid var(--border);
        border-radius: 9px;
        padding: .35rem .55rem;
    }

    .app-nav .navbar-collapse {
        position: fixed;
        top: 0; right: 0; bottom: 0; left: 0;
        width: 100vw;
        margin: 0;
        padding: 0;
        background: var(--surface);
        display: flex !important;
        flex-direction: column;
        overflow-y: auto;
        overflow-x: hidden;
        z-index: 1051;
        transform: translateX(100%);
        transition: transform .26s cubic-bezier(.32, .72, 0, 1), visibility .26s;
        visibility: hidden;
        height: auto !important;
    }
    .app-nav .navbar-collapse.show,
    .app-nav .navbar-collapse.collapsing {
        transform: translateX(0);
        visibility: visible;
    }
    .app-nav .navbar-collapse.collapsing {
        transition: transform .26s cubic-bezier(.32, .72, 0, 1);
    }

    /* Backdrop injected by JS (see _Layout.cshtml mobileDrawer block). */
    .nav-drawer-backdrop {
        position: fixed; inset: 0;
        background: rgba(15, 23, 42, .55);
        z-index: 1050;
        opacity: 0;
        transition: opacity .25s ease;
        pointer-events: none;
    }
    .nav-drawer-backdrop.is-visible {
        opacity: 1;
        pointer-events: auto;
    }
    body.nav-drawer-open { overflow: hidden; }

    /* Drawer header: brand mark on the left, close X on the right.
       Sticky so it stays visible while the user scrolls a long
       item list. Width forced to 100% with align-self stretch
       guards against any parent that fails to stretch us. */
    .nav-drawer-head {
        display: flex !important; align-items: center; justify-content: space-between;
        align-self: stretch;
        width: 100%;
        padding: .9rem 1.1rem;
        border-bottom: 1px solid var(--border);
        background: var(--surface);
        position: sticky; top: 0; z-index: 2;
    }
    .nav-drawer-brand {
        display: inline-flex; align-items: center; gap: .55rem;
        text-decoration: none;
        color: var(--text);
    }
    .nav-drawer-brand span {
        font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
        font-weight: 800;
        font-size: 1.1rem;
        letter-spacing: -.02em;
    }
    .nav-drawer-close {
        background: transparent;
        border: 0;
        border-radius: 50%;
        width: 36px; height: 36px;
        display: inline-flex; align-items: center; justify-content: center;
        color: var(--text-muted);
        cursor: pointer;
        transition: background .15s ease, color .15s ease;
    }
    .nav-drawer-close:hover,
    .nav-drawer-close:focus-visible {
        background: var(--surface-2);
        color: var(--text);
        outline: none;
    }

    /* Navbar lists become column-stack rows. Force align-self
       stretch + width 100% so the container never shrink-wraps
       to its content (which made everything look right-shifted
       in the unbalanced layout the user reported). */
    .app-nav .navbar-collapse .navbar-nav {
        display: flex !important;
        flex-direction: column;
        align-self: stretch;
        width: 100%;
        gap: 2px;
        margin: 0 !important;
        padding: .35rem .75rem;
        border-left: 0 !important;
        align-items: stretch;
        list-style: none;
    }
    .app-nav .navbar-collapse .navbar-nav.ms-auto {
        flex-direction: column;
        align-items: stretch;
        border-top: 1px solid var(--border);
        margin-top: .5rem !important;
        padding-top: .75rem;
    }
    .app-nav .navbar-collapse .navbar-nav .nav-item {
        width: 100%;
    }

    /* Universal full-width row treatment for every link/button kind. */
    .app-nav .navbar-collapse .nav-link-pill,
    .app-nav .navbar-collapse .nav-icon-btn,
    .app-nav .navbar-collapse .user-menu,
    .app-nav .navbar-collapse .nav-inboxes-toggle {
        display: flex !important;
        width: 100%;
        align-items: center;
        justify-content: flex-start !important;
        gap: .85rem;
        padding: .7rem .9rem !important;
        border-radius: 10px;
        font-size: .94rem !important;
        font-weight: 500 !important;
        height: auto !important;
        color: var(--text) !important;
        background: transparent !important;
        border: 0 !important;
        text-decoration: none;
    }
    .app-nav .navbar-collapse .nav-link-pill:hover,
    .app-nav .navbar-collapse .nav-icon-btn:hover,
    .app-nav .navbar-collapse .user-menu:hover,
    .app-nav .navbar-collapse .nav-inboxes-toggle:hover {
        background: var(--surface-2) !important;
    }
    .app-nav .navbar-collapse .nav-link-pill.is-active {
        background: var(--primary-bg) !important;
        color: var(--primary) !important;
    }

    /* Icons sized identically across all rows for visual rhythm. */
    .app-nav .navbar-collapse .nav-link-pill svg,
    .app-nav .navbar-collapse .nav-icon-btn svg,
    .app-nav .navbar-collapse .user-menu svg {
        width: 18px;
        height: 18px;
        flex: 0 0 auto;
    }
    /* Counts sit inline NEXT to their label (not floated to the
       far right edge) - the user noted "2 far away from
       Workspaces" looked disconnected. The right edge is
       reserved for genuine row chevrons / actions. */
    .app-nav .navbar-collapse .nav-pill-count,
    .app-nav .navbar-collapse .nav-bell-count,
    .app-nav .navbar-collapse .nav-approvals-count {
        position: static;
        margin-left: .15rem;
        transform: none;
        box-shadow: none;
    }

    /* The icon-strip's icons normally don't have labels - inject one
       via ::after using the title attribute so each row reads
       cleanly in the drawer ("Activity", "Mentions", "Approvals"). */
    .app-nav .navbar-collapse .nav-icon-btn::after {
        content: attr(title);
        flex: 1 1 auto;
        text-align: left;
    }

    /* User menu: avatar + display name on a single row, dropdown
       opens inline below it. */
    .app-nav .navbar-collapse .user-menu .user-email {
        display: inline-block !important;
        font-weight: 500;
        font-size: .94rem;
        color: var(--text) !important;
        max-width: none !important;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        flex: 1 1 auto;
        min-width: 0;
    }
    .app-nav .navbar-collapse .user-menu .user-avatar {
        width: 32px !important;
        height: 32px !important;
        font-size: .85rem !important;
        flex: 0 0 auto;
    }
    /* Drawer has its own close X - the page-level hamburger is
       redundant once the drawer is up. Hide it while the drawer
       is open so the brand wordmark is the only thing in that
       corner. */
    body.nav-drawer-open .app-nav .navbar-toggler { display: none; }
    /* Bootstrap's dropdown-toggle ::after triangle adds visual
       noise next to "Inboxes" in the drawer. Items expand inline
       below their parent so the affordance is obvious. */
    .app-nav .navbar-collapse .dropdown-toggle::after { display: none !important; }
    /* The nav-workspaces icon wrapper is 14px on desktop; the
       drawer rule blows the svg up to 18px which leaves the
       spinner (positioned on the wrapper) sized smaller and
       off-center. Resize the wrapper so spinner + icon stay
       aligned on a single visual ring. */
    .app-nav .navbar-collapse .nav-workspaces-icon {
        width: 18px;
        height: 18px;
    }
    /* Mentions row reads as "Mentions  No unread mentions" - the
       muted "0" pill in between is redundant noise. Hide the
       count badge when the bell isn't in its has-unread state. */
    .app-nav .navbar-collapse .nav-bell:not(.has-unread) .nav-bell-count {
        display: none;
    }

    /* The icon strip in the navbar uses `title` attributes for
       hover tooltips on desktop - we render those as labels in
       the drawer via `::after { content: attr(title) }`. But the
       desktop tooltip text ("No unread mentions", "Toggle dark
       mode", "n items awaiting your decision") is verbose for a
       menu row. Override per-icon with semantic, terse labels. */
    .app-nav .navbar-collapse .nav-bell::after {
        content: 'Mentions';
    }
    .app-nav .navbar-collapse #themeToggle::after {
        content: 'Dark mode';
    }
    html[data-theme="dark"] .app-nav .navbar-collapse #themeToggle::after {
        content: 'Light mode';
    }
    .app-nav .navbar-collapse .nav-approvals::after {
        content: 'Approvals';
    }

    /* Workspaces row: the syncing spinner on the leading icon
       reads as a "broken C" inside the drawer's larger row. We
       drop the syncing state in the drawer and just show the
       static mailbox icon - the syncing badge in the desktop
       navbar covers that signal. */
    .app-nav .navbar-collapse .nav-workspaces-spinner { display: none !important; }
    .app-nav .navbar-collapse .nav-workspaces-mailbox { opacity: 1 !important; }
    .app-nav .navbar-collapse .nav-link-pill.is-syncing { color: var(--text) !important; }

    /* Premium user row: avatar + name (top) + email (bottom) +
       chevron at the right edge. Slightly inset card feel via
       the surface-2 background and rounded corners - it visually
       anchors the bottom of the drawer. */
    .app-nav .navbar-collapse .user-menu {
        margin: .25rem .35rem;
        padding: .75rem 1rem !important;
        background: var(--surface-2) !important;
        border-radius: 12px;
        gap: .85rem;
    }
    .app-nav .navbar-collapse .user-menu:hover {
        background: var(--surface-3, var(--surface-2)) !important;
    }
    .app-nav .navbar-collapse .user-stack {
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
        min-width: 0;
        gap: 1px;
    }
    .app-nav .navbar-collapse .user-stack .user-name {
        display: block !important;
        font-weight: 600;
        font-size: .95rem;
        color: var(--text) !important;
        line-height: 1.25;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .app-nav .navbar-collapse .user-stack .user-email-sub {
        display: block;
        font-size: .8rem;
        color: var(--text-muted);
        line-height: 1.25;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .app-nav .navbar-collapse .user-menu .user-chev {
        display: inline-flex;
        flex: 0 0 auto;
        color: var(--text-muted);
        margin-left: auto;
    }
    /* The user-menu nav-item now has its own card treatment, so
       drop the hairline border and extra padding-top added
       earlier. */
    .app-nav .navbar-collapse .navbar-nav .nav-item.dropdown:has(.user-menu) {
        margin-top: .65rem !important;
        border-top: 0;
        padding-top: 0;
    }

    /* Inline dropdowns - no float, no shadow, just an indented list. */
    .app-nav .navbar-collapse .dropdown-menu {
        position: static !important;
        transform: none !important;
        width: 100%;
        margin: .15rem 0 .35rem;
        padding: .15rem 0 .15rem 2.4rem;
        background: transparent !important;
        border: 0 !important;
        box-shadow: none !important;
    }
    .app-nav .navbar-collapse .dropdown-menu .dropdown-item {
        padding: .55rem .75rem;
        border-radius: 8px;
        font-size: .9rem;
        color: var(--text);
    }
    .app-nav .navbar-collapse .dropdown-menu .dropdown-item:hover {
        background: var(--surface-2);
    }
    .app-nav .navbar-collapse .dropdown-divider {
        margin: .25rem 0;
        border-color: var(--border-soft, var(--border));
    }
    /* Approvals dropdown body: full-width like other dropdowns. */
    .app-nav .navbar-collapse .approvals-menu {
        width: 100% !important;
        padding-left: 0 !important;
        margin-top: .35rem;
    }

    /* Pin the user-menu dropdown to the bottom of the drawer so the
       sign-out path is always reachable without scrolling. The
       `:has()` selector is supported in every modern browser at
       this breakpoint. */
    .app-nav .navbar-collapse .navbar-nav .nav-item.dropdown:has(.user-menu) {
        margin-top: .5rem !important;
        border-top: 1px solid var(--border);
        padding-top: .75rem;
    }
}

.app-nav .nav-bell { position: relative; text-decoration: none; }
.app-nav .nav-bell.has-unread { color: var(--primary); }

/* Live "new invitation arrived" toast. Pops from the bottom-right when
   the approvals poll detects an invitation arrived since the last
   tick. Click Refresh to reload (so the new invite card renders on
   the page they're already on); dismiss to ignore - the badge in
   the navbar still counts the items. */

/* In-page toast banner for server-rendered flash messages. Slides in
   from the top of the content area, holds for a few seconds, then
   slides out + collapses (margin/max-height animate) so content below
   doesn't jump when it disappears. Distinct from .invite-toast above
   (the bottom-right floating notification for live invitation arrivals). */
.ui-toast {
    display: flex; align-items: center; gap: .65rem;
    padding: .75rem 1rem;
    margin-bottom: 1rem;
    border-radius: 12px;
    border: 1px solid transparent;
    font-size: .92rem; line-height: 1.4;
    box-shadow: 0 6px 18px rgba(15, 23, 42, .06);
    animation: ui-toast-in .35s cubic-bezier(.34, 1.56, .64, 1) both;
    overflow: hidden;
}
.ui-toast-success {
    background: color-mix(in srgb, var(--success) 10%, var(--surface));
    border-color: color-mix(in srgb, var(--success) 30%, var(--border));
    color: color-mix(in srgb, var(--success) 80%, var(--text));
}
.ui-toast-success .ui-toast-icon { color: var(--success); }
.ui-toast-error {
    background: color-mix(in srgb, var(--danger) 10%, var(--surface));
    border-color: color-mix(in srgb, var(--danger) 30%, var(--border));
    color: color-mix(in srgb, var(--danger) 80%, var(--text));
}
.ui-toast-error .ui-toast-icon { color: var(--danger); }
.ui-toast-warning {
    background: color-mix(in srgb, #F59E0B 10%, var(--surface));
    border-color: color-mix(in srgb, #F59E0B 30%, var(--border));
    color: color-mix(in srgb, #F59E0B 75%, var(--text));
}
.ui-toast-warning .ui-toast-icon { color: #B45309; }
.ui-toast-info {
    background: color-mix(in srgb, var(--primary) 10%, var(--surface));
    border-color: color-mix(in srgb, var(--primary) 30%, var(--border));
    color: color-mix(in srgb, var(--primary) 80%, var(--text));
}
.ui-toast-info .ui-toast-icon { color: var(--primary); }
/* Floating variant: fixed at bottom-right of viewport. Used by
   the JS Burofolk.toast() API for client-side notifications
   that don't have a server-render slot. */
.ui-toast-floating {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 1080;
    min-width: 280px;
    max-width: 420px;
    box-shadow: 0 14px 36px rgba(15, 23, 42, .14);
    margin-bottom: 0;
}
.ui-toast-floating + .ui-toast-floating { bottom: calc(1.25rem + 70px); }
.ui-toast-floating + .ui-toast-floating + .ui-toast-floating { bottom: calc(1.25rem + 140px); }
.ui-toast-icon {
    flex: 0 0 auto;
    width: 28px; height: 28px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 8px;
    background: var(--surface);
}
.ui-toast-msg { flex: 1 1 auto; min-width: 0; font-weight: 500; }
.ui-toast-dismiss {
    flex: 0 0 auto;
    background: transparent; border: 0;
    width: 24px; height: 24px;
    display: inline-flex; align-items: center; justify-content: center;
    color: inherit; opacity: .55;
    cursor: pointer; border-radius: 6px;
    font-size: 1.05rem; line-height: 1;
}
.ui-toast-dismiss:hover { opacity: 1; background: rgba(15, 23, 42, .06); }
.ui-toast.is-leaving {
    animation: ui-toast-out .35s ease forwards;
}
@keyframes ui-toast-in {
    0%   { opacity: 0; transform: translateY(-10px); }
    100% { opacity: 1; transform: translateY(0); }
}
@keyframes ui-toast-out {
    0%   { opacity: 1; max-height: 200px; }
    100% { opacity: 0; transform: translateY(-8px); max-height: 0; margin-bottom: 0; padding-top: 0; padding-bottom: 0; border-width: 0; }
}
@media (prefers-reduced-motion: reduce) {
    .ui-toast { animation: none; }
    .ui-toast.is-leaving { animation: none; opacity: 0; }
}

.invite-toast {
    position: fixed; bottom: 1.25rem; right: 1.25rem;
    display: flex; align-items: center; gap: .65rem;
    padding: .65rem .85rem;
    background: var(--surface);
    border: 1px solid color-mix(in srgb, var(--primary) 30%, var(--border));
    border-radius: 12px;
    box-shadow: 0 14px 36px rgba(15, 23, 42, .14);
    color: var(--text);
    font-size: .9rem;
    z-index: 2000;
    opacity: 0;
    transform: translateY(8px);
    transition: opacity .22s ease, transform .22s ease;
    max-width: min(420px, calc(100vw - 2rem));
}
.invite-toast.is-visible { opacity: 1; transform: translateY(0); }
.invite-toast > svg { color: var(--primary); flex: 0 0 auto; }
.invite-toast .btn { padding: .25rem .65rem; font-size: .82rem; }
.invite-toast-dismiss {
    background: transparent; border: 0; padding: 0;
    width: 22px; height: 22px;
    color: var(--text-muted);
    cursor: pointer; font-size: 1rem; line-height: 1;
    border-radius: 6px;
}
.invite-toast-dismiss:hover { background: var(--surface-2); color: var(--text); }
/* Activity: same icon-button skin as the bell. Becomes primary-colored
   while on the /Activity page so users know they're on it. */
.app-nav .nav-activity { text-decoration: none; }
.app-nav .nav-activity.is-active { color: var(--primary); background: var(--primary-bg); border-color: color-mix(in srgb, var(--primary) 25%, var(--border)); }
/* The pill anchors to the top-right CORNER of the icon button (negative
   offsets so it sits outside the bell glyph, not on top of it). At
   16x16 the bell only has ~9px of padding around it inside a 34x34
   button - placing the pill INSIDE that padding made it cover the
   bell's clapper and rendered the "0" unreadable. Shifting it outward
   by a couple of pixels keeps the bell fully visible while still
   visually attaching the pill to that icon. */
.app-nav .nav-bell-count {
    position: absolute;
    top: -3px; right: -4px;
    min-width: 15px; height: 15px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--primary);
    color: #fff;
    font-size: 9px;
    font-weight: 700;
    line-height: 15px;
    text-align: center;
    box-shadow: 0 0 0 2px var(--surface);
    pointer-events: none;
    letter-spacing: 0;
}
/* Zero-state pill: always rendered (so the layout doesn't shift when a
   mention arrives) but visually quieter so it doesn't shout when there's
   nothing to read. Lighter gray fill, muted text, no heavy ring. */
.app-nav .nav-bell:not(.has-unread) .nav-bell-count {
    background: var(--surface-2);
    color: var(--text-muted);
    box-shadow: 0 0 0 2px var(--surface);
    border: 1px solid var(--border);
    /* Border eats a pixel from the inner content; bump line-height to
       compensate so the digit stays vertically centered. */
    line-height: 13px;
}

/* Approvals icon: same anchor + pill geometry as the bell, but the
   accent color is reserved for when something's actually pending (the
   icon is hidden entirely otherwise, so the .has-pending state is the
   only one that ever renders). */
.app-nav .nav-approvals { position: relative; text-decoration: none; color: var(--primary); }
.app-nav .nav-approvals.has-pending { color: var(--primary); }
.app-nav .nav-approvals .nav-approvals-count {
    background: #ef4444;
    box-shadow: 0 0 0 2px var(--surface);
}

/* Approvals dropdown menu - card with a small header, scrollable list of
   pending items, and a row hover that highlights the whole entry. Width
   is comfortable for the longest workspace label without forcing
   wrapping; max-height keeps a 20-item queue scrollable rather than
   running off the bottom of the viewport. */
.approvals-menu {
    width: 360px;
    max-width: calc(100vw - 1rem);
    padding: 0;
    overflow: hidden;
}
.approvals-menu-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: .6rem .8rem;
    border-bottom: 1px solid var(--border);
}
.approvals-menu-header h6 { margin: 0; font-size: .8rem; font-weight: 600; color: var(--text); letter-spacing: .02em; }
.approvals-menu-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 20px; height: 20px; padding: 0 .4rem;
    border-radius: 999px;
    background: #ef4444; color: #fff;
    font-size: .7rem; font-weight: 700;
}
.approvals-menu-body { max-height: 400px; overflow-y: auto; }
.approvals-menu-loading,
.approvals-menu-empty { padding: .9rem .8rem; text-align: center; }

.approval-row {
    display: flex; gap: .6rem; align-items: flex-start;
    padding: .55rem .8rem;
    text-decoration: none; color: inherit;
    border-bottom: 1px solid var(--border-soft, var(--border));
}
.approval-row:last-child { border-bottom: 0; }
.approval-row:hover { background: var(--surface-2); }
.approval-row-avatar {
    width: 32px; height: 32px; border-radius: 8px;
    object-fit: cover; flex: 0 0 auto;
    background: var(--surface-2);
}
.approval-row-avatar-letter {
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--primary); color: #fff;
    font-weight: 600; font-size: .85rem;
}
.approval-row-text { display: flex; flex-direction: column; min-width: 0; gap: 2px; flex: 1 1 auto; }
.approval-row-title {
    font-weight: 600; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.approval-row-kind {
    display: inline-block;
    padding: 1px 6px; margin-right: 4px;
    border-radius: 6px;
    background: var(--surface-2);
    color: var(--text-muted);
    font-size: .65rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
    vertical-align: middle;
}
.approval-row-sub { font-size: .78rem; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.approval-row-meta { font-size: .7rem; color: var(--text-subtle, var(--text-muted)); }

/* Theme toggle: show sun in light mode, moon in dark mode */
.app-nav .nav-icon-btn#themeToggle .moon { display: none; }
html[data-theme="dark"] .app-nav .nav-icon-btn#themeToggle .sun { display: none; }
html[data-theme="dark"] .app-nav .nav-icon-btn#themeToggle .moon { display: block; }
html[data-theme="dark"] .app-nav .nav-icon-btn#themeToggle { color: #fbbf24; }
.app-nav .user-menu { display: inline-flex; align-items: center; gap: .55rem; padding: .25rem .5rem !important; border-radius: 8px; }

/* Command palette launcher button - visible label so it's discoverable */
.nav-cmdk-btn {
    display: inline-flex; align-items: center; gap: .4rem;
    padding: .35rem .6rem; height: 30px;
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: 8px; cursor: pointer; color: var(--text-muted);
    font-size: .8rem; font-weight: 500;
    transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.nav-cmdk-btn:hover { background: var(--surface); border-color: var(--primary); color: var(--primary); }
.nav-cmdk-btn svg { color: currentColor; flex: 0 0 auto; }
.nav-cmdk-btn kbd {
    display: inline-block; padding: 1px 5px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 4px; font-size: .68rem; color: var(--text-muted);
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    line-height: 1.2;
}
.nav-cmdk-btn:hover kbd { color: var(--primary); border-color: var(--primary); }

/* ── Online-now presence stack (top nav) ──────────────────────────
   Horizontally-overlapping avatar pile that surfaces "who's around"
   from every page. Three visual tiers driven by data-status:
     · online → full-color + green dot
     · idle   → full-color + amber dot
     · recent → 40% opacity, no dot (recently online but not active)
   The cluster is hidden when the roster is empty so the topbar
   doesn't carry an empty slot. */
.nav-presence-toggle {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: 2px 6px 2px 4px; height: 30px;
    background: transparent; border: 1px solid transparent;
    border-radius: 999px;
    cursor: pointer; color: var(--text-muted);
    transition: background .12s ease, border-color .12s ease;
}
.nav-presence-toggle:hover {
    background: var(--surface-2);
    border-color: var(--border);
}
.nav-presence-stack {
    display: inline-flex; align-items: center;
    /* Negative gap creates the overlapping pile look. The right-hand
       avatars end up "on top" via reverse z-index in the JS render. */
}
.nav-presence-stack .nav-presence-avatar { margin-left: -8px; }
.nav-presence-stack .nav-presence-avatar:first-child { margin-left: 0; }

.nav-presence-avatar {
    position: relative;
    width: 26px; height: 26px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--primary), #8b5cf6);
    color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: .72rem;
    text-transform: uppercase;
    overflow: visible;
    border: 2px solid var(--surface);
    flex: 0 0 auto;
    transition: transform .12s ease, opacity .12s ease;
}
.nav-presence-avatar img {
    width: 100%; height: 100%;
    border-radius: 50%;
    object-fit: cover;
    display: block;
}
.nav-presence-avatar:hover {
    transform: translateY(-1px) scale(1.06);
    z-index: 5;
}

/* Status dot in the lower-right corner. Sized + positioned so it
   sits on top of the avatar's white border ring without being
   clipped. The recent state has no dot - its muted opacity is the
   signal. */
.nav-presence-avatar::after {
    content: "";
    position: absolute;
    right: -1px; bottom: -1px;
    width: 9px; height: 9px;
    border-radius: 50%;
    border: 2px solid var(--surface);
    background: transparent;
}
.nav-presence-avatar[data-status="online"]::after { background: #22c55e; }
.nav-presence-avatar[data-status="idle"]::after   { background: #f59e0b; }
.nav-presence-avatar[data-status="recent"] { opacity: .42; }
.nav-presence-avatar[data-status="recent"]::after { display: none; }
/* "You" marker: subtle ring tint so the user can find themselves
   in the stack at a glance. Doesn't compete with the status dot. */
.nav-presence-avatar.is-self {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 38%, transparent);
}

.nav-presence-overflow {
    margin-left: -6px;
    height: 26px;
    min-width: 26px;
    padding: 0 7px;
    border-radius: 999px;
    background: var(--surface-2);
    border: 2px solid var(--surface);
    color: var(--text-muted);
    font-size: .68rem; font-weight: 700;
    display: inline-flex; align-items: center; justify-content: center;
}

/* Popover that opens on click - a per-row list with avatar + name +
   "in {workspace}" / "{relative-time} ago" status copy. Clicking a
   row triggers the existing global profile-card modal via
   data-user-profile-id so we get the avatar profile UI for free. */
.nav-presence-pop {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 1100;
    /* Auto-fit width: the popover sizes to the LONGEST row's natural
       width (max-content) but stays inside a sensible band. A solo
       roster reads tightly; a row with "Loïc Carrère in support@lm-
       kit.com · viewing a ticket · just now" gets only the width it
       needs and not a pixel more. Caps at 92vw on phones and 560px
       on big screens so a pathological row can't blow past the
       viewport or feel like a billboard. */
    width: max-content;
    min-width: 280px;
    max-width: min(92vw, 560px);
    max-height: 70vh;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 18px 38px -10px rgba(15,23,42,.22), 0 4px 10px -2px rgba(15,23,42,.08);
    padding: .5rem;
    text-align: left;
}
.nav-presence-li {
    position: relative;
    /* Owns the divider that used to live on .nav-tools as
       border-left. Keeping it here means it sits BETWEEN the
       buddies stack and the rest of the tools (Actions / bell /
       profile), where the user expects the visual separation. */
    padding-right: .55rem;
    margin-right: .25rem;
    border-right: 1px solid var(--border);
}
.nav-presence-pop[hidden] { display: none; }
.nav-presence-pop-head {
    display: flex; align-items: baseline; justify-content: space-between;
    padding: .5rem .6rem .65rem;
    border-bottom: 1px solid var(--border);
    margin-bottom: .35rem;
}
.nav-presence-pop-head strong { font-size: .9rem; color: var(--text); }
.nav-presence-pop-sub { font-size: .72rem; color: var(--text-muted); }
.nav-presence-pop-list { display: flex; flex-direction: column; gap: 1px; }
/* Single-row layout: avatar | name | status pill · location · since.
   Everything on one line so a long roster stays compact (the user's
   roster will grow as the team does). The row scrolls horizontally
   only via ellipsis on its overflowing children, never by stretching
   the popover wider than its 320px shell. */
.nav-presence-row {
    display: flex; align-items: center; gap: .55rem;
    padding: .35rem .5rem;
    border-radius: 8px;
    color: var(--text);
    transition: background .1s ease;
    min-width: 0;
}
.nav-presence-row:hover { background: var(--surface-2); }
.nav-presence-row[data-status="recent"] { opacity: .55; }
/* Profile-trigger button - avatar + name, opens the profile card on
   click. Sits as a sibling of the meta line so anchor clicks inside
   the meta don't bubble through it. Sized to its CONTENT (not a
   width cap) so the name shows in full; the meta line gets whatever
   space remains and ellipsis-truncates only when necessary. */
.nav-presence-row-trigger {
    display: inline-flex; align-items: center; gap: .55rem;
    padding: 0;
    margin: 0;
    background: transparent; border: 0;
    cursor: pointer;
    color: inherit; font-family: inherit;
    text-align: left;
    flex: 0 0 auto;
    min-width: 0;
}
.nav-presence-row-trigger:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
    border-radius: 4px;
}
.nav-presence-row .nav-presence-avatar { margin-left: 0; width: 28px; height: 28px; font-size: .76rem; flex: 0 0 auto; }
/* Names show in full - no ellipsis. The wider popover (460px)
   accommodates typical full names; pathological lengths still
   wrap onto a second line rather than getting cut off. */
.nav-presence-row-name {
    font-size: .85rem; font-weight: 600; color: var(--text);
    white-space: nowrap;
}
/* Meta runs to the end of the row, ellipsis-truncating long
   location names if (and only if) the row would otherwise wrap. */
.nav-presence-row-meta {
    flex: 1 1 auto;
    min-width: 0;
    display: inline-flex; align-items: center; gap: .35rem;
    font-size: .78rem; color: var(--text-muted);
    line-height: 1.3;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.nav-presence-row-meta .nav-presence-loc {
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.nav-presence-since { color: var(--text-subtle); }
.nav-presence-row .nav-presence-row-meta a {
    color: var(--primary); text-decoration: none;
}
.nav-presence-row .nav-presence-row-meta a:hover { text-decoration: underline; }

/* Status pill + location chunk inside each popover row meta line.
   The pill is the at-a-glance signal (color tells you online vs
   idle vs recently online); the location chunk that follows is
   "where they are navigating" - the primary information the user
   came to the popover for. */
.nav-presence-pill {
    display: inline-flex; align-items: center;
    padding: 0 .5rem;
    height: 16px;
    border-radius: 999px;
    font-size: .65rem;
    font-weight: 700;
    letter-spacing: .02em;
    text-transform: uppercase;
    line-height: 1;
}
.nav-presence-pill.is-online {
    background: color-mix(in srgb, #22c55e 16%, transparent);
    color: #15803d;
}
.nav-presence-pill.is-idle {
    background: color-mix(in srgb, #f59e0b 18%, transparent);
    color: #b45309;
}
.nav-presence-pill.is-recent {
    background: var(--surface-2);
    color: var(--text-muted);
}
html[data-theme="dark"] .nav-presence-pill.is-online { color: #4ade80; }
html[data-theme="dark"] .nav-presence-pill.is-idle   { color: #fbbf24; }

.nav-presence-loc {
    color: var(--text);
}
.nav-presence-loc a { font-weight: 600; }
.nav-presence-pop-empty {
    padding: .85rem .6rem;
    color: var(--text-muted);
    font-size: .8rem;
    text-align: center;
}

.app-nav .user-avatar { width: 28px; height: 28px; border-radius: 50%; background: linear-gradient(135deg, var(--primary), #8b5cf6); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-weight: 700; font-size: .8rem; flex: 0 0 auto; }
/* User name in the navbar pill is hidden by default - the avatar
   is enough on desktop, and the dropdown header repeats the name
   + email anyway. The drawer override (mobile rules) re-shows it
   alongside the email subline. */
.app-nav .user-email { display: none; }
.app-nav .dropdown-menu-end .dropdown-header { line-height: 1.4; padding: .5rem 1rem; }

/* Main shell */
.app-main { flex: 1 1 auto; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
.app-page { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.app-page-inner { max-width: 1200px; margin: 0 auto; padding: 2rem 1.5rem; }

/* "Shared" badge on viewer account rows - low-weight pill right next to
   the account's email so the user instantly sees which accounts are theirs
   vs. which are ones they were invited to. */
.account-shared-badge {
    display: inline-block;
    margin-left: .55rem;
    padding: .05rem .45rem;
    font-size: .62rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    background: var(--primary-bg);
    color: var(--primary);
    border-radius: 999px;
    vertical-align: middle;
}

/* Share / manage-access modal. Two stacked sections (pending + active),
   each rendered as Collaborators-page-style rows so this modal is the
   single source of truth for "who has access". The legacy
   .share-list classes below are still used by older render paths until
   they're migrated. */
.share-section + .share-section { margin-top: 1.25rem; }
.share-section-head {
    display: flex; align-items: baseline; gap: .55rem;
    margin: 0 0 .55rem;
}
.share-section-head h6 {
    margin: 0;
    font-size: .72rem; font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--text-muted);
}
.share-section-count {
    font-size: .68rem; font-weight: 700;
    color: var(--primary);
    background: var(--primary-bg);
    padding: 1px 7px;
    border-radius: 999px;
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
    line-height: 1.4;
    font-variant-numeric: tabular-nums;
}
.share-rows {
    display: flex; flex-direction: column; gap: .4rem;
}
.share-row {
    display: flex; align-items: center; gap: .75rem;
    padding: .55rem .75rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 10px;
}
.share-row-avatar {
    flex: 0 0 auto;
    width: 36px; height: 36px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%;
    background: var(--avatar-bg);
    color: var(--avatar-color);
    font-weight: 700; font-size: .85rem;
    text-transform: uppercase;
    overflow: hidden;
}
.share-row-avatar-img { object-fit: cover; }
.share-row-avatar-pending {
    background: linear-gradient(135deg, #f59e0b, #ec4899);
    color: #fff;
}
/* Team-grant row avatar. The JS renders an <img> when the team
   has an uploaded icon (icon == ":upload"); otherwise a first-
   letter initial. Both fill the round bubble. */
.share-row-avatar-team {
    background: color-mix(in srgb, var(--primary) 18%, var(--surface));
    color: var(--primary);
    border: 1px solid color-mix(in srgb, var(--primary) 30%, var(--border));
    display: inline-flex; align-items: center; justify-content: center;
    overflow: hidden;
}
.share-row-avatar-team img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
}
.share-row-badge-team {
    color: var(--primary);
    background: var(--primary-bg);
    border-color: color-mix(in srgb, var(--primary) 30%, var(--border));
}
.share-row-text {
    flex: 1 1 auto; min-width: 0;
    display: flex; flex-direction: column; gap: 2px;
    line-height: 1.3;
}
.share-row-label {
    font-weight: 600; color: var(--text); font-size: .9rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    display: inline-flex; align-items: center; gap: .45rem;
}
.share-row-meta {
    font-size: .76rem; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.share-row-badge {
    flex: 0 0 auto;
    font-size: .62rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase;
    color: #b45309;
    background: color-mix(in srgb, #f59e0b 18%, var(--surface));
    border: 1px solid color-mix(in srgb, #f59e0b 35%, var(--border));
    padding: 1px 7px;
    border-radius: 999px;
    line-height: 1.4;
}
.share-row-badge-good {
    color: #065F46;
    background: color-mix(in srgb, #10B981 14%, var(--surface));
    border-color: color-mix(in srgb, #10B981 35%, var(--border));
}
.share-row-badge-bad {
    color: #991B1B;
    background: color-mix(in srgb, #DC2626 14%, var(--surface));
    border-color: color-mix(in srgb, #DC2626 35%, var(--border));
    cursor: help;
}
html[data-theme="dark"] .share-row-badge-good { color: #6EE7B7; }
html[data-theme="dark"] .share-row-badge-bad { color: #FCA5A5; }

/* "Admin" role badge in the Manage modal - primary tint to mark a
   row that carries admin rights, distinct from the warmer "pending"
   amber and the green/red status badges. */
.share-row-badge-admin {
    color: var(--primary);
    background: var(--primary-bg);
    border-color: color-mix(in srgb, var(--primary) 35%, var(--border));
}
html[data-theme="dark"] .share-row-badge-admin { color: #c4b5fd; }

/* ── Dashboard pending-admin-promotion cards ────────────────────
   Action-required banner pinned at the top of the home page.
   Each card stacks identity (who proposed) + copy + Accept/Decline.
   Accent color is primary so it reads as "important + welcoming",
   not "alert" - admin promotion is a positive event. */
.admin-promo-banner {
    display: flex; flex-direction: column; gap: .75rem;
    margin: 0 0 1.5rem;
}
.admin-promo-card {
    display: flex; align-items: center; gap: 1rem;
    padding: .9rem 1.1rem;
    background: linear-gradient(135deg,
        color-mix(in srgb, var(--primary) 9%, var(--surface)),
        var(--surface));
    border: 1px solid color-mix(in srgb, var(--primary) 30%, var(--border));
    border-radius: 12px;
    box-shadow: 0 4px 12px -4px color-mix(in srgb, var(--primary) 22%, transparent);
}
.admin-promo-card-icon { flex: 0 0 auto; }
.admin-promo-avatar {
    width: 40px; height: 40px;
    border-radius: 50%;
}
.admin-promo-card-text { flex: 1 1 auto; min-width: 0; line-height: 1.4; }
.admin-promo-card-title {
    font-size: .95rem;
    color: var(--text);
}
.admin-promo-card-sub {
    margin-top: .2rem;
    font-size: .78rem;
    color: var(--text-muted);
}
.admin-promo-card-actions {
    flex: 0 0 auto;
    display: inline-flex; gap: .4rem; align-items: center;
}
@media (max-width: 720px) {
    .admin-promo-card { flex-direction: column; align-items: flex-start; gap: .55rem; }
    .admin-promo-card-actions { align-self: stretch; justify-content: flex-end; }
}
.share-row-actions {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; gap: .35rem;
}
.share-empty {
    padding: 1.5rem 1rem;
    text-align: center;
    color: var(--text-muted);
    background: var(--surface-2);
    border: 1px dashed var(--border-strong);
    border-radius: 10px;
}
.share-empty-icon { font-size: 1.6rem; line-height: 1; margin-bottom: .35rem; }
.share-invite-form { /* nothing extra - the modal-body baseline is enough */ }

/* New-workspace (type picker) page ------------------------------------ */
.ws-new-page { max-width: 880px; margin: 0 auto; padding: 1rem 1.25rem 2rem; }
.ws-new-header { margin-bottom: 1.5rem; }
.ws-new-header .btn-back {
    display: inline-flex; align-items: center; gap: .25rem;
    color: var(--text-muted); text-decoration: none;
    font-size: .85rem; margin-bottom: .85rem;
}
.ws-new-header .btn-back:hover { color: var(--primary); }
.ws-new-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
    gap: 1rem;
}
.ws-type-card {
    display: grid;
    grid-template-columns: 56px 1fr;
    grid-template-areas:
        "icon text"
        "icon action";
    gap: .25rem 1rem;
    padding: 1.1rem 1.25rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.ws-type-card.is-available:hover {
    border-color: color-mix(in srgb, var(--primary) 35%, var(--border));
    box-shadow: 0 12px 28px rgba(15, 23, 42, .07);
    transform: translateY(-1px);
}
.ws-type-card.is-coming-soon { opacity: .68; }
.ws-type-icon {
    grid-area: icon;
    width: 48px; height: 48px;
    border-radius: 12px;
    background: linear-gradient(135deg, color-mix(in srgb, var(--primary) 16%, var(--surface)),
                                       color-mix(in srgb, #06B6D4 12%, var(--surface)));
    color: var(--primary);
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
}
.ws-type-card.is-coming-soon .ws-type-icon {
    background: var(--surface-2);
    color: var(--text-muted);
    border-color: var(--border);
}
.ws-type-icon svg { width: 24px; height: 24px; }
.ws-type-text { grid-area: text; min-width: 0; }
.ws-type-text h3 {
    margin: 0 0 .25rem;
    font-size: 1.05rem; font-weight: 700;
    color: var(--text); letter-spacing: -.01em;
    display: inline-flex; align-items: center; gap: .5rem;
}
.ws-type-tag {
    font-size: .62rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-muted);
    background: var(--surface-2);
    border: 1px solid var(--border);
    padding: 1px 7px; border-radius: 999px;
    line-height: 1.4;
}
.ws-type-tagline {
    margin: 0 0 .35rem;
    font-size: .88rem; color: var(--text);
    font-weight: 500;
}
.ws-type-desc {
    margin: 0;
    font-size: .82rem; color: var(--text-muted); line-height: 1.5;
}
.ws-type-action { grid-area: action; margin-top: .8rem; }
.ws-type-action .btn { font-size: .85rem; padding: .4rem .9rem; }

/* Type badge on the workspace list/manage card. Compact pill that
   tells the user "this is an Inbox" at a glance - prepares the UI
   for mixed-type lists once more connectors land. */
.ws-type-badge {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: 1px 8px;
    font-size: .65rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--primary);
    background: var(--primary-bg);
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
    border-radius: 999px;
    line-height: 1.5;
    vertical-align: middle;
}
.ws-type-badge svg { width: 11px; height: 11px; }

/* Navbar local-time pill. Sits in the right-hand icon strip, just
   before the activity icon. Quiet typography - tabular numerals,
   muted color, no border - so it reads as ambient context, not as
   a button competing with the bell / theme toggle. The IANA id
   sits in the title tooltip; the strip itself shows date + time
   only, no city label.
   Responsiveness: the navbar already gets visually busy a long
   way above the Bootstrap md breakpoint (search box, brand,
   primary nav, online-now stack, command palette pill, bell,
   approvals, theme, avatar). The pill drops its label in stages
   so it doesn't crowd or wrap:
     - 1280+ px: full "Mon 27 Apr · 22:32"
     - 1080-1280: time only ("22:32")
     - below 1080:  hidden entirely
   Hidden values keep flowing into the title tooltip so a hover at
   any width still surfaces the same info. */
.nav-localtime {
    position: relative;
    display: inline-flex;
    align-items: baseline;
    gap: 0.4rem;
    /* Symmetric padding leaves room for separators on both sides;
       margins keep the time pill its own visual cluster between
       the actions/search affordance and the icon strip. */
    padding: 0 0.85rem;
    margin: 0 0.35rem;
    font-size: 0.78rem;
    line-height: 1;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.01em;
    cursor: default;
    user-select: none;
    white-space: nowrap;
    flex: 0 0 auto;
}
/* Identical hairline separators on both sides of the time pill.
   Single source of truth in this rule so left and right always
   match in height, color, and fade. Kept short (8px) so they read
   as a typographic breath in the icon strip rather than a border;
   the gradient fade softens the endpoints so neither side looks
   harder than the other. */
.nav-localtime::before,
.nav-localtime::after {
    content: "";
    position: absolute;
    top: 50%;
    width: 1px;
    height: 8px;
    transform: translateY(-50%);
    background: linear-gradient(
        to bottom,
        transparent 0%,
        color-mix(in srgb, var(--border) 60%, transparent) 30%,
        color-mix(in srgb, var(--border) 60%, transparent) 70%,
        transparent 100%
    );
    pointer-events: none;
}
.nav-localtime::before { left: 0; }
.nav-localtime::after  { right: 0; }
.nav-localtime-time {
    color: var(--text);
    font-weight: 600;
    letter-spacing: 0.04em;
}
.nav-localtime-dot {
    opacity: 0.45;
}
@media (max-width: 1280px) {
    .nav-localtime [data-nav-now-date],
    .nav-localtime .nav-localtime-dot { display: none; }
}
@media (max-width: 1080px) {
    .nav-localtime { display: none; }
}

/* Provider badge sibling to .ws-type-badge. Quieter (slate, not
   primary) so the type badge stays the focal chip; the provider
   badge reads as supplementary "and the source of this inbox is
   X" context. The icon is the inline brand mark from
   InboxProviderCatalog so Gmail / Microsoft / IMAP read at a glance. */
.ws-provider-badge {
    display: inline-flex; align-items: center; gap: .3rem;
    margin-left: .3rem;
    padding: 1px 8px;
    font-size: .65rem; font-weight: 600; letter-spacing: .02em;
    color: var(--text-muted);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 999px;
    line-height: 1.5;
    vertical-align: middle;
}
.ws-provider-badge-icon { display: inline-flex; align-items: center; }
.ws-provider-badge-icon svg { width: 11px; height: 11px; }

/* Team invitation chip-picker rendered inside each owned team card. Same
   chip-input as Collaborators / Workspaces - the layout glue around it
   is per-host. */
.team-invite-form { margin-top: .65rem; }
.team-invite-chip-input { width: 100%; }
.team-invite-submit-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: .5rem; margin-top: .5rem;
}
.team-invite-submit-row .form-text { margin: 0; flex: 1 1 auto; font-size: .75rem; }

/* Per-team pending-outgoing-invitations strip. Reads as a tertiary
   panel - quieter than the member list, but visible enough to remind
   the owner who they're still waiting on. */
.team-outgoing-invites {
    margin-top: .9rem;
    padding: .6rem .8rem;
    background: var(--surface-2);
    border: 1px dashed var(--border);
    border-radius: 8px;
}
.team-outgoing-head {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: .35rem;
}
.team-outgoing-head h6 { margin: 0; font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted); }
.team-outgoing-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 18px; height: 18px; padding: 0 .35rem;
    border-radius: 999px;
    background: var(--surface);
    color: var(--text-muted);
    font-size: .68rem; font-weight: 700;
    border: 1px solid var(--border);
}
.team-outgoing-list { list-style: none; margin: 0; padding: 0; }
.team-outgoing-row {
    display: flex; align-items: center; gap: .55rem;
    padding: .35rem 0;
    border-top: 1px solid var(--border-soft, var(--border));
}
.team-outgoing-row:first-child { border-top: 0; }
.team-outgoing-avatar {
    width: 26px; height: 26px; border-radius: 50%;
    background: var(--surface);
    border: 1px dashed var(--border);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .72rem; font-weight: 700; color: var(--text-muted);
}
.team-outgoing-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; line-height: 1.3; }
.team-outgoing-email { font-size: .82rem; font-weight: 500; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.team-outgoing-meta { font-size: .7rem; color: var(--text-muted); }

/* Leave-team button on member-of cards. Right-aligned at the bottom
   of the card body so it doesn't visually compete with the member
   roster above. */
/* Leave-team footer: separated from the member list by a soft rule and
   right-aligned so the action reads as a card-level operation, not a
   per-row one. The button itself is a quiet ghost so it doesn't compete
   with the primary content. */
.team-leave-form {
    margin-top: .85rem;
    padding-top: .65rem;
    border-top: 1px solid var(--border);
    display: flex; justify-content: flex-end;
}
.team-leave-form .btn {
    color: var(--text-muted);
    font-weight: 500;
}
.team-leave-form .btn:hover {
    color: var(--danger);
    background: var(--danger-bg);
}
/* Layout for the chip-picker invite row inside the Manage modal. The
   outer `.chip-input.share-chip-input` reuses the same chip styling as
   the Collaborators page; this just stacks the action button + form-text
   below it so the chip area can wrap freely. */
.share-chip-input { width: 100%; }
.share-invite-submit-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: .75rem; margin-top: .5rem;
}
.share-invite-submit-row .form-text { margin: 0; flex: 1 1 auto; }
@media (max-width: 540px) {
    .share-invite-submit-row { flex-direction: column-reverse; align-items: stretch; }
    .share-invite-submit-row .btn { width: 100%; }
}

/* Legacy classes - kept until any remaining render path is migrated to
   the new .share-row layout above. */
.share-list {
    list-style: none; padding: 0; margin: 0;
    display: flex; flex-direction: column; gap: .35rem;
}
.share-list li {
    display: flex; align-items: center; justify-content: space-between;
    gap: .75rem;
    padding: .5rem .7rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
}
.share-list-main { display: flex; flex-direction: column; min-width: 0; }
.share-list-main strong { font-weight: 600; color: var(--text); font-size: .875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.share-list-meta { font-size: .72rem; color: var(--text-muted); }
.share-list-actions { display: inline-flex; gap: .35rem; flex: 0 0 auto; }

/* Invite flash toast - top-right, auto-dismiss. Error variant uses
   danger-bg so a "delivery failed, copy this link" message is obvious. */
.invite-flash {
    position: fixed;
    top: 5.5rem; right: 1rem;
    max-width: 420px;
    padding: .75rem 1rem;
    border-radius: 10px;
    font-size: .85rem;
    line-height: 1.4;
    color: var(--text);
    border: 1px solid var(--border);
    background: var(--surface);
    box-shadow: 0 10px 30px -10px rgba(15, 23, 42, .25);
    z-index: 10000;
    transition: opacity .4s ease, transform .4s ease;
    word-break: break-word;
}
.invite-flash-success {
    border-color: #86efac;
    background: #f0fdf4;
    color: #166534;
}
.invite-flash-error {
    border-color: #fca5a5;
    background: #fef2f2;
    color: #991b1b;
}
.invite-flash-fade { opacity: 0; transform: translateY(-6px); }
html[data-theme="dark"] .invite-flash-success { background: rgba(22, 101, 52, .2); color: #bbf7d0; border-color: rgba(34, 197, 94, .4); }
html[data-theme="dark"] .invite-flash-error   { background: rgba(153, 27, 27, .2); color: #fecaca; border-color: rgba(239, 68, 68, .4); }

/* Auth pages (Register / Login) - centered card, no app chrome. */
.auth-body {
    min-height: 100vh;
    display: flex; flex-direction: column;
    background: linear-gradient(180deg, var(--surface-2) 0%, var(--surface) 100%);
}
.auth-main {
    flex: 1 1 auto;
    display: flex; align-items: center; justify-content: center;
    padding: 0 1rem 2rem;
}
/* Standalone brand mark above the auth card. The 3-bar logo paired with
   the Plus Jakarta Sans wordmark gives the auth pages a real identity
   - the previous "Sign in to Burofolk" plain text was forgettable
   chrome. Hover micro-rotation on the icon mirrors the navbar mark
   for consistency. */
.auth-brand-mark {
    display: flex; align-items: center; justify-content: center;
    gap: .65rem;
    margin: 2.25rem auto 1.5rem;
    text-decoration: none;
    color: inherit;
    line-height: 1;
}
.auth-brand-icon {
    flex: 0 0 auto;
    display: block;
    transition: transform .35s cubic-bezier(.34, 1.56, .64, 1);
}
.auth-brand-mark:hover .auth-brand-icon { transform: rotate(-6deg) scale(1.06); }
.auth-brand-wordmark {
    font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
    font-weight: 800;
    font-size: 1.7rem;
    letter-spacing: -.025em;
    color: var(--text);
    line-height: 1;
}
@media (max-width: 480px) {
    .auth-brand-mark { margin-top: 1.25rem; }
    .auth-brand-icon { width: 36px; height: 36px; }
    .auth-brand-wordmark { font-size: 1.4rem; }
}
.auth-card {
    width: 100%; max-width: 420px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 2.25rem 2rem 1.75rem;
    box-shadow: 0 10px 40px -15px rgba(15, 23, 42, .15),
                0 2px 6px -2px rgba(15, 23, 42, .08);
}
.auth-card h1 {
    margin: 0 0 .4rem;
    font-size: 1.35rem; font-weight: 700;
    letter-spacing: -.02em;
}
.auth-lead {
    color: var(--text-muted);
    font-size: .88rem;
    margin: 0 0 1.5rem;
    line-height: 1.5;
}
/* "Continue with Google" SSO button. Follows Google's brand-button
   guidelines (white background, full color G mark, dark "Roboto-ish"
   text) so the button is recognizably a Google sign-in entry point.
   Hidden via Razor when GoogleAuthOptions.IsConfigured is false. */
.btn-google-sso {
    display: inline-flex; align-items: center; justify-content: center;
    gap: .65rem;
    width: 100%;
    padding: .65rem 1rem;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    font-weight: 600; font-size: .92rem;
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease, transform .12s ease;
    font-family: inherit;
}
.btn-google-sso:hover {
    border-color: var(--text-muted);
    background: var(--surface-2);
}
.btn-google-sso:active { transform: translateY(0.5px); }
.btn-google-glyph {
    flex: 0 0 auto;
    width: 18px; height: 18px;
    display: inline-flex; align-items: center; justify-content: center;
}

/* "or" divider between SSO and email/password. Two thin rules with
   muted text in the middle, gentle and short so it reads as a soft
   alternative rather than a hard mode switch. */
.auth-divider {
    display: flex; align-items: center; gap: .65rem;
    margin: 1rem 0;
    color: var(--text-subtle, var(--text-muted));
    font-size: .72rem;
    text-transform: uppercase; letter-spacing: .08em;
}
.auth-divider::before,
.auth-divider::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: var(--border);
}

/* ============================================================
   Two-factor pages: Setup2fa, Disable2fa, RecoveryCodes share the
   same hero + card layout. Styles live here (not page-scoped) so
   /Auth/Profile/RecoveryCodes wasn't rendering as plain HTML when
   the user landed on it without `?fresh=1` (the `<style>` block
   in Setup2fa.cshtml was page-scoped and didn't apply elsewhere).
   ============================================================ */
.totp-setup-page { max-width: 880px; margin: 0 auto; padding: 1.5rem 1.25rem 3rem; }
.totp-back {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .35rem .55rem .35rem .25rem;
    margin-bottom: 1.25rem;
    border-radius: 8px;
    color: var(--text-muted); text-decoration: none;
    font-size: .85rem;
}
.totp-back:hover { color: var(--primary); background: var(--surface-2); }

.totp-hero { text-align: center; margin-bottom: 2rem; }
.totp-hero-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 56px; height: 56px;
    margin-bottom: 1rem;
    border-radius: 14px;
    background: linear-gradient(135deg, color-mix(in srgb, var(--primary) 18%, var(--surface)),
                                        color-mix(in srgb, #06B6D4 15%, var(--surface)));
    color: var(--primary);
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
}
.totp-hero h1 {
    font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
    font-weight: 800;
    font-size: clamp(1.6rem, 3vw, 2rem);
    letter-spacing: -.025em;
    margin: 0 0 .4rem;
    color: var(--text);
}
.totp-hero-sub {
    margin: 0 auto;
    max-width: 540px;
    color: var(--text-muted);
    line-height: 1.55;
}

.totp-error {
    margin-bottom: 1rem;
    padding: .75rem 1rem;
    border-radius: 12px;
    border: 1px solid color-mix(in srgb, var(--danger) 35%, var(--border));
    background: color-mix(in srgb, var(--danger) 8%, var(--surface));
    color: color-mix(in srgb, var(--danger) 80%, var(--text));
    font-size: .92rem;
}

.totp-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
    gap: 1.25rem;
}
.totp-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 1.5rem;
}
.totp-card + .totp-card { margin-top: 1rem; }
.totp-step-label {
    display: inline-block;
    padding: 2px 10px;
    margin-bottom: .9rem;
    border-radius: 999px;
    background: var(--primary-bg);
    color: var(--primary);
    font-size: .68rem; font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase;
}
.totp-card h2 {
    font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
    font-weight: 700; font-size: 1.05rem;
    letter-spacing: -.01em;
    margin: 0 0 .35rem; color: var(--text);
}
.totp-card-sub {
    margin: 0 0 1.1rem;
    color: var(--text-muted);
    font-size: .88rem; line-height: 1.55;
}
.totp-form { display: flex; flex-direction: column; gap: .85rem; }
.totp-tip { margin: 0; font-size: .76rem; color: var(--text-muted); line-height: 1.5; }

/* Two-factor security card on /Auth/Profile. Same surface treatment
   as the profile-card above it; just adds a header row with the
   Enabled/Off badge, a row layout for the per-control rows, and a
   muted hint line for the "off" state. */
.security-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1rem;
}
.security-title {
    font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
    font-weight: 700; font-size: 1.05rem;
    letter-spacing: -.01em;
    margin: 0 0 .15rem;
    color: var(--text);
}
.security-tagline {
    margin: 0;
    color: var(--text-muted);
    font-size: .88rem; line-height: 1.5;
}
.security-badge {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: .68rem; font-weight: 700;
    letter-spacing: .04em; text-transform: uppercase;
    line-height: 1.4;
    flex: 0 0 auto;
}
.security-badge-on {
    color: var(--success);
    background: color-mix(in srgb, var(--success) 14%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--success) 30%, var(--border));
}
.security-badge-off {
    color: var(--text-muted);
    background: var(--surface-2);
    border: 1px solid var(--border);
}
.security-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem;
    padding: .75rem 0;
    border-top: 1px solid var(--border);
}
.security-row:last-child { padding-bottom: 0; }
.security-row-text { display: flex; flex-direction: column; line-height: 1.4; }
.security-row-text strong { color: var(--text); font-weight: 600; font-size: .92rem; }
.security-row-meta { color: var(--text-muted); font-size: .76rem; margin-top: 1px; }
.security-hint {
    margin: .8rem 0 0;
    font-size: .8rem; color: var(--text-muted); line-height: 1.5;
}

.auth-form { display: flex; flex-direction: column; gap: 1rem; }
.auth-field { display: flex; flex-direction: column; gap: .35rem; }
.auth-field label {
    font-size: .78rem; font-weight: 600;
    color: var(--text);
}
.auth-label-row {
    display: flex; justify-content: space-between; align-items: center;
}
.auth-minor-link {
    font-size: .75rem;
    color: var(--primary);
    text-decoration: none;
    font-weight: 500;
}
.auth-minor-link:hover { text-decoration: underline; }
.auth-optional { font-weight: 400; color: var(--text-muted); }
.auth-field input {
    width: 100%;
    padding: .55rem .75rem;
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    background: var(--surface);
    color: var(--text);
    font-size: .9rem;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.auth-field input:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-bg);
}
.auth-hint {
    font-size: .72rem;
    color: var(--text-muted);
    line-height: 1.4;
}
.auth-alt {
    margin-top: 1.25rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--border);
    font-size: .85rem;
    color: var(--text-muted);
    text-align: center;
}
.auth-alt a { color: var(--primary); font-weight: 600; text-decoration: none; }
.auth-alt a:hover { text-decoration: underline; }
.auth-footer {
    flex: 0 0 auto;
    padding: 1rem 1.5rem;
    display: flex; justify-content: center;
    font-size: .75rem;
    color: var(--text-subtle);
}
.auth-footer-brand {
    display: inline-flex; align-items: center; gap: .5rem;
    color: var(--text-muted);
    text-decoration: none;
    transition: color .12s ease;
}
.auth-footer-brand:hover { color: var(--primary); }
.auth-footer-brand img { width: 18px; height: 18px; display: block; }

/* ── App footer ════════════════════════════════════════════════════
   Three-zone layout: brand identity (left), utility links (center,
   auto-margin), credit + copyright (right). A subtle gradient hairline
   sits on top of the border so the footer feels intentional, not
   tacked on. Slim height (~52px) but visually rich. */
.app-footer {
    flex: 0 0 auto;
    position: relative;
    display: flex;
    align-items: center;
    gap: 1.25rem;
    padding: .8rem 1.5rem;
    font-size: .78rem;
    color: var(--text-muted);
    background: var(--surface);
    border-top: 1px solid var(--border);
}
/* Brand-gradient hairline on top - same cyan/violet/pink wash as the
   3-bar mark. Decorative; sits ABOVE the border-top so the row stays
   anchored even when this animates / fades. */
.app-footer::before {
    content: "";
    position: absolute;
    top: -1px; left: 0; right: 0; height: 1px;
    background: linear-gradient(90deg, transparent 0%, #06B6D4 25%, #8B5CF6 50%, #EC4899 75%, transparent 100%);
    opacity: .55;
    pointer-events: none;
}

.app-footer-zone {
    display: inline-flex;
    align-items: center;
    gap: .65rem;
    flex: 0 0 auto;
    min-width: 0;
}
.app-footer-zone-utility {
    flex: 1 1 auto;
    justify-content: center;
}

/* Brand zone: 3-bar mark + Burofolk wordmark + italic tagline. */
.app-footer-brand {
    display: inline-flex; align-items: center; gap: .5rem;
    color: var(--text);
    text-decoration: none;
    font-family: 'Plus Jakarta Sans', 'Inter', system-ui, sans-serif;
    font-weight: 800;
    font-size: .9rem;
    letter-spacing: -.015em;
    transition: opacity .12s ease;
}
.app-footer-brand:hover { opacity: .8; color: var(--text); }
.app-footer-mark {
    display: inline-flex;
    width: 22px; height: 22px;
    flex: 0 0 auto;
}
.app-footer-mark svg { display: block; }
.app-footer-wordmark { line-height: 1; }
.app-footer-tagline {
    color: var(--text-subtle);
    font-size: .76rem;
    font-style: italic;
    letter-spacing: .005em;
    padding-left: .35rem;
    border-left: 1px solid var(--surface-2);
    margin-left: .15rem;
}

/* Status pill: green dot (radial gradient = self-contained, no halo
   that gets clipped) + label, on a lightly tinted pill background so
   the "operational" reads as a real status indicator and not just
   plain text. */
/* Footer status indicator - minimal "dot + plain label" style.
   The previous version was a colored pill with a tinted background,
   which read as a generated GitHub-status-page badge sitting awkwardly
   in our footer. The new version is just a small colored dot + muted
   text, sitting at the same visual level as the other footer links -
   matches the way Linear / Vercel surface status. The DOT alone
   carries the color signal; the LABEL stays in the footer's neutral
   muted color regardless of state, so the whole strip reads as one
   visual rhythm. */
.app-footer-status {
    display: inline-flex; align-items: center; gap: .4rem;
    padding: 0;
    background: transparent;
    color: var(--text-muted);
    font-weight: 500;
    font-size: .72rem;
    line-height: 1;
}
.app-footer-status-text { color: inherit; }
.app-footer-dot {
    width: 7px; height: 7px;
    border-radius: 50%;
    background: #10b981;
    box-shadow: 0 0 0 2px color-mix(in srgb, #10b981 25%, transparent);
    transition: background .15s ease, box-shadow .15s ease;
}
.app-footer-status.is-checking .app-footer-dot {
    background: #94a3b8;
    box-shadow: none;
}
.app-footer-status.is-down .app-footer-dot {
    background: #dc2626;
    box-shadow: 0 0 0 2px color-mix(in srgb, #dc2626 30%, transparent);
    /* Pulse so downtime is hard to miss without resorting to a
       loud red pill. Cheap CSS animation, no JS. */
    animation: app-footer-dot-pulse 1.4s ease-in-out infinite;
}
.app-footer-status.is-down .app-footer-status-text {
    /* Only the LABEL color shifts on the down state - the muted
       grey reads as "fine" against an actually-broken signal,
       so we tint to match the dot. The container stays
       background-less so the indicator never looks like a
       slapped-on badge. */
    color: #b91c1c;
}
@keyframes app-footer-dot-pulse {
    0%, 100% { box-shadow: 0 0 0 2px color-mix(in srgb, #dc2626 30%, transparent); }
    50%      { box-shadow: 0 0 0 6px color-mix(in srgb, #dc2626 0%, transparent); }
}
html[data-theme="dark"] .app-footer-status.is-down .app-footer-status-text {
    color: #fca5a5;
}

/* Utility links: small icon + label. Vertical separators between are
   a thin 12px line for visual rhythm. */
.app-footer-link {
    display: inline-flex; align-items: center; gap: .35rem;
    color: var(--text-muted);
    text-decoration: none;
    background: transparent;
    border: 0;
    padding: .2rem .35rem;
    font: inherit;
    font-weight: 500;
    cursor: pointer;
    border-radius: 6px;
    transition: color .12s ease, background .12s ease;
}
.app-footer-link:hover { color: var(--primary); background: var(--primary-bg); }
.app-footer-link svg { opacity: .75; }
.app-footer-link:hover svg { opacity: 1; }
.app-footer-link-cmd .app-footer-kbd { margin-left: .15rem; }
.app-footer-kbd {
    font-family: ui-monospace, 'SF Mono', monospace;
    font-size: .66rem;
    font-weight: 600;
    color: var(--text-subtle);
    background: var(--surface);
    border: 1px solid var(--border);
    padding: 1px .3rem;
    border-radius: 4px;
    line-height: 1.4;
}

/* Slim vertical divider - replaces the dot separator between utility
   links so the row feels less like a list of bullet points. */
.app-footer-zone-utility .app-footer-sep {
    width: 1px; height: 14px;
    background: var(--surface-2);
    opacity: 1;
}

/* Credit zone: Calico IIM link + copyright. The credit reads as
   small-caps "by Calico IIM" with the logo icon. */
.app-footer-zone-credit {
    color: var(--text-subtle);
    font-size: .72rem;
}
.app-footer-credit {
    display: inline-flex; align-items: center; gap: .35rem;
    color: var(--text-muted);
    text-decoration: none;
    transition: color .12s ease;
}
.app-footer-credit img { width: 14px; height: 14px; display: block; }
.app-footer-credit strong { font-weight: 700; color: var(--text-muted); }
.app-footer-credit:hover { color: var(--primary); }
.app-footer-credit:hover strong { color: var(--primary); }
.app-footer-zone-credit .app-footer-sep {
    width: 3px; height: 3px;
    border-radius: 50%;
    background: currentColor;
    opacity: .35;
}
.app-footer-meta { color: var(--text-subtle); font-size: .72rem; font-weight: 500; }

/* Responsive collapse: at narrow widths drop the tagline and recenter
   utility links so the brand mark + status + credit fit on one row.
   Below ~600 the credit zone wraps under the brand zone. */
@media (max-width: 900px) {
    .app-footer-tagline { display: none; }
}
@media (max-width: 720px) {
    .app-footer { padding: .65rem .9rem; gap: .85rem; flex-wrap: wrap; }
    .app-footer-zone-utility { flex: 1 1 100%; justify-content: flex-start; order: 3; }
    .app-footer-zone-credit { order: 2; margin-left: auto; }
}
@media (max-width: 480px) {
    .app-footer-link-cmd .app-footer-kbd { display: none; }
    .app-footer-credit span { display: none; }
}
/* Archive has its own bottom chrome (sticky pagination) - footer would just crowd it */
body:has(.archive-layout) .app-footer { display: none; }

/* Theme toggle + cmdk hint in footer */
.app-footer .theme-toggle, .app-footer .cmdk-hint {
    border: 1px solid var(--border); background: var(--surface); color: var(--text-muted);
    padding: .15rem .5rem; border-radius: 6px; cursor: pointer; font-size: .75rem; margin-left: .5rem;
}
.app-footer .theme-toggle:hover, .app-footer .cmdk-hint:hover { color: var(--primary); border-color: var(--primary); }
.app-footer kbd { font-family: ui-monospace, monospace; font-size: .72rem; background: var(--surface-2); padding: 0 .25rem; border-radius: 3px; }

/* Thread count badge - minimal pill next to sender name. Keeps a low
   visual weight so it never competes with the sender/subject typography. */
.thread-count {
    display: inline-flex; align-items: center;
    flex: 0 0 auto;
    font-size: .68rem; font-weight: 700;
    background: var(--surface-2); color: var(--text-muted);
    padding: 1px 7px; border-radius: 999px;
    line-height: 1.45;
    letter-spacing: .01em;
}
.mail-row.unread .thread-count { background: var(--primary-bg); color: var(--primary); }

/* Keyboard cursor on mail row */
.mail-row.is-cursor {
    background: var(--primary-bg);
    box-shadow: inset 3px 0 0 var(--primary);
}

/* Advanced search toggle + panel */
.adv-toggle {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: 0 .85rem; height: 42px;
    background: var(--surface); color: var(--text-muted);
    border: 1px solid var(--border-strong); border-radius: 10px;
    font-size: .85rem; font-weight: 500; cursor: pointer;
}
.adv-toggle:hover, .adv-toggle.active {
    color: var(--primary); border-color: var(--primary); background: var(--primary-bg);
}
.adv-panel {
    flex: 1 1 100%;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 12px; padding: 1rem 1.25rem;
    margin-top: .25rem; box-shadow: var(--shadow-sm);
}
.adv-panel .adv-grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: .75rem 1rem; margin-bottom: .75rem;
}
.adv-panel .adv-row { display: grid; gap: 1rem; margin-bottom: .75rem; }
.adv-panel .adv-row-2 { grid-template-columns: 1fr 1fr; }
.adv-panel .adv-dates { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
@media (max-width: 720px) {
    .adv-panel .adv-row-2 { grid-template-columns: 1fr; }
    .adv-panel .adv-dates { grid-template-columns: 1fr 1fr; }
}
.adv-panel .chk {
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .4rem .85rem;
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: 999px; cursor: pointer;
    font-size: .82rem; color: var(--text);
    transition: all .1s;
}
.adv-panel .chk:hover { border-color: var(--primary); }
.adv-panel .chk input[type="checkbox"] { width: 14px; height: 14px; accent-color: var(--primary); margin: 0; }
.adv-panel .chk:has(input:checked) { background: var(--primary-bg); border-color: var(--primary); color: var(--primary); }

/* Sort menu */
.sort-menu { position: relative; }
.sort-menu .adv-toggle { gap: .5rem; min-width: 140px; justify-content: space-between; }
.sort-menu .adv-toggle > span { flex: 1 1 auto; text-align: left; font-weight: 500; color: var(--text); }
.sort-menu .adv-toggle svg:first-child { color: var(--text-muted); flex: 0 0 auto; }
.sort-menu .adv-toggle svg:last-child { color: var(--text-subtle); flex: 0 0 auto; transition: transform .15s ease; }
.sort-menu .adv-toggle.active svg:last-child { transform: rotate(180deg); color: var(--primary); }
.sort-menu .adv-toggle.active { border-color: var(--primary); background: var(--primary-bg); }

.sort-dropdown {
    position: absolute; top: calc(100% + 6px); right: 0; z-index: 30;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 12px; box-shadow: 0 12px 32px rgba(15,23,42,.15);
    min-width: 240px; padding: .4rem;
    animation: slide-down-sm .12s ease;
}
@keyframes slide-down-sm { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
.sort-dropdown[hidden] { display: none; }
.sort-item {
    display: flex; align-items: center; gap: .6rem;
    padding: .55rem .75rem; font-size: .88rem;
    color: var(--text); text-decoration: none; border-radius: 8px;
    cursor: pointer;
}
.sort-item:hover { background: var(--surface-2); }
.sort-item.active { background: var(--primary-bg); color: var(--primary); font-weight: 600; }
.sort-item .check { width: 16px; display: inline-flex; align-items: center; justify-content: center; color: var(--primary); font-size: .85rem; flex: 0 0 auto; }
.sort-item:not(.active) .check { opacity: 0; }

/* Clickable star button */
.mail-flag.star-btn {
    background: transparent; border: 0; cursor: pointer;
    padding: 4px; border-radius: 6px;
    display: flex; align-items: center; justify-content: center;
    color: inherit;
    transition: transform .12s ease, background .12s ease;
}
.mail-flag.star-btn:hover { background: var(--surface-2); transform: scale(1.15); }
.mail-flag.star-btn .star-off { color: var(--text-subtle); opacity: .4; font-size: 1.1rem; }
.mail-flag.star-btn:hover .star-off { color: #eab308; opacity: .9; }
.mail-flag.star-btn.is-starred .star { color: #eab308; font-size: 1.05rem; }
.mail-flag.star-btn:disabled { opacity: .5; pointer-events: none; }

/* Preferences modal */
/* Preferences - premium redesign.
   Sectioned cards, proper dividers, iOS-style toggle switches, refined selects. */
.prefs-backdrop {
    position: fixed; inset: 0; z-index: 9998;
    background: rgba(15, 23, 42, .58);
    backdrop-filter: blur(8px) saturate(1.1);
    -webkit-backdrop-filter: blur(8px) saturate(1.1);
    display: flex; align-items: center; justify-content: center;
    padding: 1rem;
    animation: fade-in .16s ease;
}
.prefs-backdrop[hidden] { display: none; }
.prefs-panel {
    width: min(580px, 100%); max-height: 88vh; overflow: hidden;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: 0 30px 80px -20px rgba(15, 23, 42, .4),
                0 10px 30px -10px rgba(15, 23, 42, .25);
    display: flex; flex-direction: column;
    animation: prefs-rise .22s cubic-bezier(.2, .9, .3, 1);
}
@keyframes prefs-rise {
    from { opacity: 0; transform: translateY(10px) scale(.98); }
    to   { opacity: 1; transform: translateY(0)    scale(1);   }
}
html[data-theme="dark"] .prefs-panel {
    box-shadow: 0 30px 80px -20px rgba(0, 0, 0, .6),
                0 10px 30px -10px rgba(0, 0, 0, .45);
}

.prefs-header {
    display: flex; justify-content: space-between; align-items: center;
    padding: 1.1rem 1.5rem;
    border-bottom: 1px solid var(--border);
}
.prefs-header h3 {
    margin: 0;
    font-size: 1.1rem; font-weight: 700;
    letter-spacing: -.015em;
    color: var(--text);
}
.prefs-close {
    width: 32px; height: 32px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent;
    border: 1px solid transparent;
    color: var(--text-muted);
    cursor: pointer;
    border-radius: 8px;
    font-size: .95rem;
    line-height: 1;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.prefs-close:hover { background: var(--surface-2); color: var(--text); border-color: var(--border); }

.prefs-body {
    padding: .5rem .6rem 1rem;
    overflow-y: auto;
    background: var(--surface);
}

/* Sections are plain groupings - no card-in-a-card. The outer panel is
   already a surface; sub-cards would fragment the content. A single thin
   top border is enough to separate sections (skipped on the first). */
.prefs-section {
    margin: 0;
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
}
.prefs-section + .prefs-section {
    border-top: 1px solid var(--border);
    margin-top: .35rem;
    padding-top: .35rem;
}

/* Inline section header - icon + title. Quiet weight, left-aligned so the
   eye scans down cleanly instead of hitting a full-width gray banner per
   section. */
.prefs-section-head {
    display: flex; align-items: center; gap: .55rem;
    padding: .85rem .9rem .35rem;
    color: var(--text-muted);
}
.prefs-section-head h4 {
    margin: 0;
    font-size: .78rem; font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: inherit;
    line-height: 1;
}
.prefs-section-icon {
    width: 14px; height: 14px;
    opacity: .8;
    flex: 0 0 auto;
}

/* Field rows - uniform padding, subtle divider between rows. Control always
   right-aligned, label fills the remaining width and can wrap + host a
   sub-hint below without breaking the row grid. */
.prefs-field,
.prefs-check {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: .6rem .9rem;
    font-size: .875rem;
    color: var(--text);
    margin: 0;
    min-height: 44px;
    border: 0;
    border-radius: 8px;
    transition: background .12s ease;
}
.prefs-field:hover,
.prefs-check:hover { background: var(--surface-2); }

.prefs-field-text {
    flex: 1 1 auto;
    min-width: 0;
    cursor: default;
    font-weight: 500;
    color: var(--text);
    line-height: 1.35;
    display: flex; flex-direction: column; gap: .15rem;
}
.prefs-field-hint {
    font-size: .75rem;
    font-weight: 400;
    color: var(--text-muted);
    line-height: 1.4;
}

/* Refined select - custom chevron, focus ring, premium feel. */
.prefs-field select {
    flex: 0 0 auto;
    min-width: 180px;
    padding: .5rem 2.1rem .5rem .75rem;
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    background-color: var(--surface);
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    background-repeat: no-repeat;
    background-position: right .6rem center;
    background-size: 12px;
    color: var(--text);
    font-size: .875rem;
    font-weight: 500;
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
    transition: border-color .12s ease, box-shadow .12s ease, background-color .12s ease;
}
.prefs-field select:hover { border-color: var(--text-muted); }
.prefs-field select:focus-visible {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-bg);
}

/* Toggle switches in place of utilitarian checkboxes. */
.prefs-check { cursor: pointer; user-select: none; transition: background .12s ease; }
.prefs-check:hover { background: var(--surface-2); }
.prefs-check input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    position: relative;
    width: 40px; height: 22px;
    flex: 0 0 auto;
    margin: 0;
    border: 0;
    border-radius: 999px;
    background: var(--border-strong);
    cursor: pointer;
    transition: background .18s ease;
}
.prefs-check input[type="checkbox"]::after {
    content: '';
    position: absolute;
    top: 2px; left: 2px;
    width: 18px; height: 18px;
    background: #fff;
    border-radius: 50%;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .15),
                0 1px 3px rgba(15, 23, 42, .2);
    transition: transform .18s cubic-bezier(.3, 1.2, .5, 1);
}
.prefs-check input[type="checkbox"]:checked { background: var(--primary); }
.prefs-check input[type="checkbox"]:checked::after { transform: translateX(18px); }
.prefs-check input[type="checkbox"]:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--primary-bg);
}
html[data-theme="dark"] .prefs-check input[type="checkbox"] { background: #475569; }
html[data-theme="dark"] .prefs-check input[type="checkbox"]::after { background: #f1f5f9; }
html[data-theme="dark"] .prefs-check input[type="checkbox"]:checked { background: var(--primary); }

.prefs-footer {
    display: flex; justify-content: space-between; gap: .75rem;
    padding: 1rem 1.5rem;
    border-top: 1px solid var(--border);
    background: var(--surface);
}
.prefs-footer .btn { font-weight: 600; }

/* Density: compact */
body.density-compact .mail-row { padding: .35rem 1.25rem; }
body.density-compact .mail-row .mail-snippet { display: inline; }
body.hide-snippet .mail-row .mail-snippet { display: none; }

/* Header meta row: labels + notes count pill */
/* Message header metadata row: action chips + assignee pill + labels
   flow tightly together on the LEFT (they're related and should read
   as one cluster); the notes count pill (if any) gets pushed right
   via margin-left:auto so it reads as the secondary "X notes" badge.
   Children that carry their own bottom-margin (assignee, labels) are
   normalized to 0 here so the flex row's gap controls the spacing. */
.msg-header-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem .65rem;
    margin-bottom: 1.25rem;
}
/* The .notes-count-pill formerly sat as a direct child of
   .msg-header-meta with margin-left:auto. It now lives inside
   .msg-badge-cluster which carries the right-pinning, so when it's
   nested we cancel this rule to avoid a double-push. */
.msg-header-meta > .notes-count-pill { margin-left: auto; }
.msg-badge-cluster > .notes-count-pill { margin-left: 0; }
.msg-header-meta .msg-labels { margin-bottom: 0; }
.msg-badge-cluster .msg-labels { margin-bottom: 0; }
.msg-header-meta .msg-assignee { margin-bottom: 0; }
.msg-header-meta .msg-trash-btn { margin-bottom: 0; margin-right: 0; }
.notes-count-pill {
    display: inline-flex; align-items: center; gap: .4rem;
    padding: .3rem .75rem;
    background: linear-gradient(135deg, #10b981, #06b6d4);
    color: #fff !important;
    font-size: .78rem; font-weight: 600;
    border-radius: 999px; text-decoration: none;
    box-shadow: 0 2px 8px rgba(16,185,129,.25);
    transition: transform .12s ease, box-shadow .12s ease;
}
.notes-count-pill:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(16,185,129,.35); }

/* Premium team notes panel (prominent, above attachments + body) */
.notes-panel {
    margin-bottom: 1rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid #10b981;
    border-radius: 14px;
    box-shadow: 0 1px 2px rgba(15,23,42,.03);
    overflow: hidden;
}
.notes-panel.has-notes { border-left-color: #10b981; }
.notes-panel.empty {
    border-left-color: var(--border-strong);
    /* Empty state is a single-line affordance - no big header, no icon
       tile, no subtitle. The collapsed trigger alone says "add a note"
       and is already self-explanatory. */
    margin-bottom: .75rem;
}
/* Hide the full header when there are no notes - the trigger below IS
   the affordance. Users don't need a header to tell them a panel with
   one line of text is a "notes panel". */
.notes-panel.empty .notes-panel-head { display: none; }

.notes-panel-head {
    display: flex; justify-content: space-between; align-items: center;
    padding: .85rem 1.25rem;
    background: linear-gradient(90deg, rgba(16,185,129,.06) 0%, rgba(6,182,212,.03) 100%);
    border-bottom: 1px solid var(--border);
    cursor: pointer; user-select: none;
    transition: background .12s ease;
}
.notes-panel-head:hover { filter: brightness(1.03); }
.notes-panel-head:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.notes-panel-right { display: flex; align-items: center; gap: .75rem; }
/* Chevron - rotates to indicate collapse state. */
.notes-panel-chev {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px;
    border-radius: 6px;
    color: var(--text-muted);
    transition: transform .2s ease, background .12s ease;
}
.notes-panel-head:hover .notes-panel-chev { background: rgba(15,23,42,.05); color: var(--text); }
.notes-panel.collapsed .notes-panel-chev { transform: rotate(-90deg); }
/* Body - max-height transition keeps the collapse animated without
   needing to measure content height up-front. 2000px is a generous
   ceiling that accommodates even very long note threads. */
.notes-panel-body {
    max-height: 2000px;
    overflow: hidden;
    transition: max-height .24s ease;
}
.notes-panel.collapsed .notes-panel-body {
    max-height: 0;
}
.notes-panel.collapsed .notes-panel-head { border-bottom-color: transparent; }
.notes-panel-title { display: flex; align-items: center; gap: .75rem; }
.notes-panel-icon {
    width: 34px; height: 34px; border-radius: 10px;
    background: linear-gradient(135deg, #10b981, #06b6d4);
    color: #fff; display: inline-flex; align-items: center; justify-content: center;
    flex: 0 0 auto;
}
.notes-panel.empty .notes-panel-icon { background: var(--border-strong); }
.notes-panel-label { font-size: .95rem; font-weight: 700; color: var(--text); letter-spacing: -.01em; }
.notes-panel-sub { font-size: .78rem; color: var(--text-muted); margin-top: .1rem; }

/* Overlapping avatar stack of contributors */
.notes-avatar-stack { display: flex; align-items: center; }
.notes-avatar-stack .notes-avatar {
    width: 28px; height: 28px; border-radius: 50%;
    background: linear-gradient(135deg, #10b981, #06b6d4);
    color: #fff; display: inline-flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: .72rem; text-transform: uppercase;
    border: 2px solid var(--surface);
    margin-left: -6px; flex: 0 0 auto;
}
.notes-avatar-stack .notes-avatar:first-child { margin-left: 0; }

/* Notes list */
.notes-list { padding: .75rem 1.25rem .25rem; display: flex; flex-direction: column; gap: .85rem; }
/* Notion-style thread group: a root note and its replies share one
   visual container. The replies are indented with a left rule so the
   eye can follow the chain back to its root, and the per-thread reply
   form sits at the bottom of the group. */
.note-thread {
    display: flex; flex-direction: column; gap: .45rem;
    padding: 0;
}
.note-thread .note.is-reply {
    margin-left: 1.25rem;
    padding-left: .75rem;
    border-left: 2px solid var(--border);
    position: relative;
}
.note-thread .note.is-reply::before {
    content: ""; position: absolute;
    left: -2px; top: 0;
    width: 2px; height: 12px;
    background: var(--primary);
    border-radius: 1px;
    opacity: .35;
}
.note-reply-compose { margin-left: 1.25rem; }
.note-reply-btn {
    width: 22px; height: 22px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    color: var(--text-muted);
    cursor: pointer;
    padding: 0;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.note-reply-btn:hover { background: var(--surface-2); color: var(--primary); border-color: var(--border); }
.note {
    display: flex; gap: .75rem; padding: .75rem .9rem;
    background: var(--surface-2); border-radius: 10px;
    border: 1px solid transparent;
    transition: border-color .12s ease, background .12s ease;
}
.note:hover { border-color: var(--border); background: var(--surface); }
.note.mine { background: rgba(16, 185, 129, .06); border-color: rgba(16, 185, 129, .2); }
.note.mine:hover { background: rgba(16, 185, 129, .08); }
.note-avatar {
    width: 30px; height: 30px; border-radius: 50%;
    background: linear-gradient(135deg, #10b981, #06b6d4);
    color: #fff; display: inline-flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: .78rem; flex: 0 0 auto; text-transform: uppercase;
}
.note-body { flex: 1 1 auto; min-width: 0; }
.note-meta { display: flex; align-items: center; gap: .5rem; margin-bottom: .2rem; }
.note-author { font-size: .85rem; color: var(--text); font-weight: 600; }
.note-date { color: var(--text-muted); font-size: .72rem; font-weight: normal; }
.note-actions { margin-left: auto; display: inline-flex; align-items: center; gap: .15rem; }
.note-actions form { margin: 0; }
.note-del-form { display: inline-flex; }
.note-del,
.note-resolve,
.note-react-btn {
    background: transparent; border: 0;
    color: var(--text-subtle); cursor: pointer;
    padding: 3px 5px; border-radius: 4px;
    display: inline-flex; align-items: center;
    opacity: 0; transition: opacity .12s ease, color .12s ease, background .12s ease;
}
.note:hover .note-del,
.note:hover .note-resolve,
.note:hover .note-react-btn { opacity: 1; }
.note-del:hover { color: var(--danger); background: var(--danger-bg); }
.note-resolve:hover { color: var(--success); background: var(--success-bg); }
.note-react-btn:hover { color: var(--primary); background: var(--primary-bg); }
/* When the note is already resolved, the resolve button stays visible
   so people remember they can re-open it. Same color treatment as the
   resolved-tag for consistency. */
.note.is-resolved .note-resolve { opacity: 1; color: var(--success); }

/* Resolved-thread visual differentiation.
   - Active threads use the default surface (no extra tint), so they
     read as the working space.
   - Resolved threads pick up a clearly-green tinted background +
     border so they look "wrapped up" at a glance. NO strikethrough
     on the body - the user found it harsh and hard to read; the
     green chrome + the explicit "Resolved" tag carries the meaning.
   - Replies in a resolved thread lose hover-revealed action chrome. */
.note.is-thread-resolved {
    background: color-mix(in srgb, var(--success, #10b981) 14%, var(--surface));
    border-color: color-mix(in srgb, var(--success, #10b981) 32%, var(--border));
}
.note.is-thread-resolved .note-author { color: var(--text-muted); }
.note.is-thread-resolved .note-text { color: var(--text); }
html[data-theme="dark"] .note.is-thread-resolved {
    background: color-mix(in srgb, var(--success, #10b981) 22%, var(--surface));
}
.note.is-thread-resolved .note-reaction-add { display: none; }

/* Collapsed resolved thread: replies hidden, toggle button reveals
   them. The toggle sits between the root note and the (hidden)
   replies; default state for newly-resolved threads with replies
   is collapsed so the panel stays scannable. */
.note-thread.is-collapsed .note.is-reply { display: none; }
.note-thread-toggle {
    display: inline-flex; align-items: center; gap: .35rem;
    margin: .15rem 0 .25rem 2.5rem;
    padding: .15rem .55rem;
    background: transparent;
    border: 0;
    border-radius: 999px;
    font-size: .75rem; font-weight: 600;
    color: var(--success, #10b981);
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.note-thread-toggle:hover {
    background: color-mix(in srgb, var(--success, #10b981) 14%, transparent);
}
.note-thread-toggle .chev {
    transition: transform .15s ease;
    width: 12px; height: 12px;
}
.note-thread:not(.is-collapsed) .note-thread-toggle .chev {
    transform: rotate(180deg);
}
/* Toggle label swap - one rendered at a time based on the
   wrapper's collapsed state. Saves a JS textContent flip. */
.note-thread.is-collapsed .note-thread-toggle-hide,
.note-thread:not(.is-collapsed) .note-thread-toggle-show {
    display: none;
}
.note-resolved-tag {
    display: inline-flex; align-items: center; gap: .25rem;
    padding: 1px 7px; border-radius: 999px;
    background: var(--success-bg); color: var(--success);
    font-size: .68rem; font-weight: 600; letter-spacing: .02em;
    text-transform: uppercase;
}

/* Reaction chips: little Notion-style emoji + count pills. Mine-toggled
   gets a primary-tinted ring so the user knows their click registered. */
.note-reactions {
    display: flex; flex-wrap: wrap; align-items: center; gap: .25rem;
    margin-top: .35rem;
}
.note-reaction-form { margin: 0; }
.note-reaction-chip {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: 2px 8px; border-radius: 999px;
    background: var(--surface-2); border: 1px solid transparent;
    cursor: pointer;
    font-size: .78rem; line-height: 1.3;
    transition: background .12s ease, border-color .12s ease, transform .08s ease;
}
.note-reaction-chip:hover { background: var(--surface); border-color: var(--border); }
.note-reaction-chip:active { transform: scale(.96); }
.note-reaction-chip.is-mine {
    background: var(--primary-bg); border-color: var(--primary);
    color: var(--primary);
}
.note-reaction-emoji { font-size: .9rem; line-height: 1; }
.note-reaction-count { font-variant-numeric: tabular-nums; font-weight: 600; }
/* "Add another reaction" chip. Sits next to the existing
   reaction chips so it has to read like another chip, not like
   a generic "+" button. Solid pill (no dashed border), a
   smiley-face glyph + a small superscript "+" badge. Matches
   the chip footprint and tone, scales subtly on hover. */
.note-reaction-add {
    position: relative;
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 24px;
    padding: 0;
    border-radius: 999px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    color: var(--text-subtle);
    cursor: pointer !important;
    user-select: none;
    -webkit-user-select: none;
    opacity: 0;
    transition: opacity .12s ease, color .12s ease, border-color .12s ease,
                background .12s ease, transform .08s ease;
}
/* Force pointer cursor + non-select on every descendant. The
   "+" badge is a text node and the smiley SVG is its own
   element - both inherited the I-beam from somewhere up the
   tree and overrode the button's pointer. Setting them
   explicitly here makes the whole chip behave as one
   clickable unit. */
.note-reaction-add * {
    cursor: pointer !important;
    user-select: none;
    -webkit-user-select: none;
}
.note:hover .note-reaction-add { opacity: 1; }
.note-reaction-add:hover {
    color: var(--primary);
    background: var(--primary-bg);
    border-color: var(--primary);
    transform: scale(1.05);
}
.note-reaction-add-face { display: block; }
.note-reaction-add-plus {
    position: absolute;
    top: -3px; right: -3px;
    width: 12px; height: 12px;
    border-radius: 50%;
    background: var(--primary);
    color: #fff;
    font-size: 9px;
    font-weight: 700;
    line-height: 12px;
    text-align: center;
    box-shadow: 0 0 0 1.5px var(--surface);
    pointer-events: none;
}

/* The picker is a single hidden element per panel; JS positions it
   underneath the note whose react-button was clicked. Absolute so it
   doesn't disturb layout when shown. */
.note-reaction-picker {
    position: absolute;
    z-index: 30;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 12px; box-shadow: 0 12px 32px rgba(15,23,42,.18);
    padding: 6px;
    display: grid;
    grid-template-columns: repeat(6, 32px);
    gap: 2px;
    max-width: 232px;
}
.note-reaction-picker[hidden] { display: none; }
.note-reaction-pick {
    width: 32px; height: 32px;
    background: transparent; border: 0; border-radius: 6px;
    cursor: pointer;
    font-size: 1.15rem; line-height: 1;
    transition: background .1s ease, transform .08s ease;
}
.note-reaction-pick:hover { background: var(--primary-bg); }
.note-reaction-pick:active { transform: scale(.92); }
.note-text { font-size: .88rem; color: var(--text); white-space: pre-wrap; word-wrap: break-word; line-height: 1.55; }
.note-text .mention, .mention {
    display: inline-block; padding: 0 6px; border-radius: 4px;
    background: var(--primary-bg); color: var(--primary); font-weight: 600;
    text-decoration: none;
}
/* Resolved mentions are clickable - they open the profile-card modal
   via the global avatar-click handler. The hover state matches the
   workspace pip's hover (subtle lift + saturated bg) so the
   affordance reads consistently across surfaces. */
.mention--clickable {
    cursor: pointer;
    transition: background .12s ease, color .12s ease, transform .08s ease;
}
.mention--clickable:hover {
    background: color-mix(in srgb, var(--primary) 22%, var(--surface));
    color: var(--primary);
}
.mention--clickable:active { transform: translateY(1px); }
.mention--clickable:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}
/* Mention autocomplete popup under the note textarea */
.note-compose-input { position: relative; }
.mention-pop {
    position: absolute; top: 100%; left: 0; z-index: 40;
    margin-top: 6px; background: var(--surface); border: 1px solid var(--border);
    border-radius: 10px; box-shadow: 0 12px 32px rgba(15,23,42,.18);
    min-width: 200px; padding: 4px;
    display: flex; flex-direction: column;
}
/* Portaled variant: appended to <body> instead of inside the notes
   panel (whose overflow:hidden was clipping the popup). Coordinates
   are computed at open time, so the rule just bumps the z-index above
   any modal/sticky element and resets margin since "top" is set
   absolutely from JS. */
.mention-pop.is-portal {
    position: absolute;
    z-index: 1080;
    margin: 0;
    max-width: 360px;
}
.mention-pop[hidden] { display: none; }
.mention-pop button {
    display: flex; align-items: center; gap: 0.6rem;
    text-align: left; background: transparent; border: 0;
    padding: .4rem .55rem; border-radius: 6px; cursor: pointer;
    font-size: .85rem; color: var(--text);
    min-width: 220px;
}
.mention-pop button.active, .mention-pop button:hover { background: var(--primary-bg); color: var(--primary); }
.mention-pop-avatar {
    flex: 0 0 auto;
    width: 26px; height: 26px; border-radius: 50%;
    background: var(--surface-2); color: var(--text-muted);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .72rem; font-weight: 600; overflow: hidden;
}
img.mention-pop-avatar { object-fit: cover; }
.mention-pop-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.mention-pop-label { font-weight: 600; font-size: .85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mention-pop-sub { color: var(--muted); font-size: .72rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Team row in the mention popup. Same shape as a user row but the
   avatar slot is a team-icon glyph and a small "team" tag sits next
   to the name so the user knows picking expands to all members. */
.mention-pop button.is-team .mention-pop-team-icon {
    background: var(--primary-bg); color: var(--primary);
    display: inline-flex; align-items: center; justify-content: center;
}
.mention-pop-team-tag {
    margin-left: .4rem;
    font-size: .58rem; font-weight: 600;
    color: var(--primary); background: var(--primary-bg);
    padding: 1px 5px; border-radius: 999px;
    letter-spacing: .04em; text-transform: uppercase;
    vertical-align: middle;
}

/* Compose: slim by default, expands on focus */
/* Collapsed compose trigger - a single-line affordance that takes the
   place of the full form when no one's writing. Keeps the Team notes
   section unobtrusive so message body stays visible without scrolling. */
.note-compose-trigger {
    display: flex; align-items: center; gap: .75rem;
    width: 100%; padding: .55rem 1.25rem;
    background: var(--surface);
    border: 0;
    border-top: 1px solid var(--border);
    color: var(--text-subtle, var(--text-muted));
    font: inherit; font-size: .85rem;
    cursor: pointer;
    text-align: left;
    transition: background .1s ease, color .1s ease;
}
/* Empty-state trigger stands alone - no border to separate from a header
   that isn't there, slightly tighter vertical padding. */
.notes-panel.empty .note-compose-trigger {
    border-top: 0;
    padding: .7rem 1.25rem;
}
.note-compose-trigger:hover { background: var(--surface-2); color: var(--text); }
.note-compose-trigger:hover .note-compose-avatar { box-shadow: 0 0 0 2px var(--primary-bg); }
.note-compose-trigger .note-compose-avatar { width: 26px; height: 26px; font-size: .72rem; }
.note-compose-cta { flex: 1 1 auto; }
.note-compose-kbd {
    flex: 0 0 auto;
    font-family: ui-monospace, monospace;
    font-size: .68rem;
    padding: 1px 6px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--text-muted);
    line-height: 1.2;
}

.note-compose { display: flex; gap: .75rem; padding: .9rem 1.25rem; border-top: 1px solid var(--border); background: var(--surface); align-items: flex-start; }
.notes-panel.empty .note-compose { border-top: 0; }
.note-compose-avatar {
    width: 30px; height: 30px; border-radius: 50%;
    background: linear-gradient(135deg, var(--primary), #8b5cf6);
    color: #fff; display: inline-flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: .78rem; flex: 0 0 auto; text-transform: uppercase;
}
.note-compose-input { flex: 1 1 auto; min-width: 0; }
.note-compose textarea {
    width: 100%; padding: .55rem .85rem;
    border: 1px solid var(--border-strong); border-radius: 10px;
    background: var(--surface); color: var(--text);
    font-size: .88rem; font-family: inherit; line-height: 1.5;
    resize: none; overflow: hidden;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.note-compose textarea::placeholder { color: var(--text-subtle); }
.note-compose textarea:focus {
    outline: none; border-color: #10b981;
    box-shadow: 0 0 0 3px rgba(16,185,129,.15);
}
.note-compose-actions { display: flex; justify-content: space-between; align-items: center; margin-top: .55rem; gap: .75rem; opacity: 0; max-height: 0; overflow: hidden; transition: opacity .15s ease, max-height .2s ease; }
.note-compose.focused .note-compose-actions, .note-compose textarea:not(:placeholder-shown) ~ .note-compose-actions { opacity: 1; max-height: 60px; }
.note-compose-hint { font-size: .72rem; color: var(--text-subtle); }

/* Topbar .eml action */
.msg-topbar .topbar-action { display: inline-flex; align-items: center; gap: .35rem; padding: .35rem .65rem; font-size: .8rem; color: var(--text-muted); text-decoration: none; border-radius: 6px; border: 1px solid var(--border); }
.msg-topbar .topbar-action:hover { background: var(--primary-bg); color: var(--primary); border-color: var(--primary); }

/* Saved searches */
/* Views (saved searches). Container = flex row with a single <a> link for
   navigation + a sibling actions block (real <form>s, outside the anchor so
   the HTML is valid). Count and actions share the trailing slot: count shows
   at rest, actions fade in on hover. Keeps clicks on action buttons cleanly
   scoped to their own forms - no ghost links, no invalid nesting. */
.view-item {
    position: relative;          /* anchor for the absolute .view-actions */
    display: flex;
    align-items: stretch;
    gap: 0;
    padding: 0;
    overflow: hidden;
    border-radius: 8px;
    transition: background .12s ease, color .12s ease, box-shadow .12s ease;
}
.view-item + .view-item { margin-top: .1rem; }

/* View-link owns the full row width so its count pill aligns flush to the
   right edge - the same visual column as system-folder and label rows.
   Padding matches .list-group-item exactly (.5rem .8rem) so all three
   sidebar row types share one measurement and the count pills line up. */
.view-item .view-link {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: .5rem .8rem;
    color: inherit;
    text-decoration: none;
}
.view-item .view-icon {
    flex: 0 0 auto;
    width: 16px; height: 16px;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--text-subtle);
    transition: color .12s ease;
}
.view-item.is-shared .view-icon { color: #8b5cf6; }
.view-item.active .view-icon,
.view-item:hover .view-icon { color: var(--primary); }
.view-item .view-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.view-item .view-count {
    flex: 0 0 auto;
    transition: opacity .12s ease;
}

/* Actions sit outside the link - clicks here never navigate. Positioned
   ABSOLUTE so they don't steal flex space from the link. This keeps the
   count pill aligned with the right edge, matching system-folder rows. On
   hover, the buttons overlay the count (which fades) so both can occupy
   the same visual slot without fighting for space. */
.view-item .view-actions {
    position: absolute;
    top: 50%;
    right: .4rem;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    gap: .15rem;
    background: var(--surface);
    border-radius: 6px;
    padding: .1rem;
    opacity: 0;
    pointer-events: none;
    transition: opacity .12s ease;
    z-index: 1;
}
.view-item:hover .view-actions { opacity: 1; pointer-events: auto; }
/* On hover the action overlay takes over the right-hand slot, so fade the
   count underneath - they'd otherwise fight for the same pixels. Scoped
   to .is-mutable because read-only views (no .view-actions emitted) have
   nothing to overlay the count, so fading it just makes the row look
   empty on hover. */
.view-item.is-mutable:hover .view-count { opacity: 0; }
/* When actions appear on an active view, give them the active-state bg so
   they read as part of the same row rather than floating on white. */
.view-item.active .view-actions { background: var(--primary-bg); }
html[data-theme="dark"] .view-item.active .view-actions { background: transparent; }

.view-item .view-action-form { margin: 0; padding: 0; display: inline-flex; }
.view-item .view-action {
    width: 26px; height: 26px;
    padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    color: var(--text-muted);
    cursor: pointer;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.view-item .view-action:hover {
    background: var(--surface);
    color: var(--primary);
    border-color: var(--border);
}
.view-item .view-action-danger:hover {
    color: var(--danger);
    border-color: #fecaca;
    background: var(--surface);
}

/* Rest / hover / active - active is distinctly primary-tinted with a left
   accent bar; hover is a subtle surface-2 gray so the two states don't
   look alike. */
.view-item:hover { background: var(--surface-2); }
.view-item.active {
    background: var(--primary-bg);
    color: var(--primary);
    font-weight: 600;
    box-shadow: inset 3px 0 0 var(--primary);
}
.view-item.active .view-count { background: var(--primary); color: #fff; }

html[data-theme="dark"] .view-item:hover { background: rgba(148, 163, 184, .08); }
html[data-theme="dark"] .view-item.active { background: rgba(129, 140, 248, .14); }
html[data-theme="dark"] .view-item.is-shared .view-icon { color: #a78bfa; }
html[data-theme="dark"] .view-item .view-action:hover { background: #1e293b; }
html[data-theme="dark"] .view-item .view-action-danger:hover { border-color: rgba(248, 113, 113, .3); }
/* Save-as-view modal - polished dialog matching the Preferences aesthetic. */
.view-dialog-backdrop {
    position: fixed; inset: 0; z-index: 9999;
    background: rgba(15, 23, 42, .58);
    backdrop-filter: blur(8px) saturate(1.1);
    -webkit-backdrop-filter: blur(8px) saturate(1.1);
    display: flex; align-items: center; justify-content: center;
    padding: 1rem;
    animation: fade-in .16s ease;
}
.view-dialog-backdrop[hidden] { display: none; }
.view-dialog {
    width: min(460px, 100%);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 1.6rem 1.75rem 1.4rem;
    box-shadow: 0 30px 80px -20px rgba(15, 23, 42, .4),
                0 10px 30px -10px rgba(15, 23, 42, .25);
    animation: prefs-rise .22s cubic-bezier(.2, .9, .3, 1);
}
html[data-theme="dark"] .view-dialog {
    box-shadow: 0 30px 80px -20px rgba(0, 0, 0, .6),
                0 10px 30px -10px rgba(0, 0, 0, .45);
}
.view-dialog h4 {
    margin: 0 0 .3rem;
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: -.01em;
    color: var(--text);
}
.view-dialog-intro {
    margin: 0 0 1.1rem;
    font-size: .82rem;
    color: var(--text-muted);
    line-height: 1.5;
}
.view-dialog label {
    display: block;
    font-size: .78rem;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 1rem;
    letter-spacing: .01em;
}
.view-dialog label input[type="text"] {
    display: block;
    width: 100%;
    padding: .65rem .85rem;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    font-size: .925rem;
    font-weight: 500;
    background: var(--surface);
    color: var(--text);
    margin-top: .4rem;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.view-dialog label input[type="text"]:hover { border-color: var(--text-muted); }
.view-dialog label input[type="text"]:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-bg);
}
.view-dialog label.chk {
    display: flex;
    align-items: flex-start;
    gap: .75rem;
    font-weight: 500;
    font-size: .875rem;
    padding: .75rem .85rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 10px;
    cursor: pointer;
    transition: border-color .12s ease, background .12s ease;
}
.view-dialog label.chk:hover { border-color: var(--border-strong); }
.view-dialog label.chk:has(input:checked) {
    background: var(--primary-bg);
    border-color: var(--primary);
}
.view-dialog label.chk > span { display: block; flex: 1 1 auto; line-height: 1.4; }
.view-dialog label.chk strong { display: block; color: var(--text); font-weight: 600; font-size: .875rem; }
.view-dialog label.chk em { display: block; font-style: normal; font-weight: 400; font-size: .78rem; color: var(--text-muted); margin-top: .15rem; }

/* Toggle switch to match Preferences. Replaces the chunky purple checkbox. */
.view-dialog label.chk input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    position: relative;
    width: 36px; height: 20px;
    flex: 0 0 auto;
    margin: 0;
    margin-top: 2px;
    border: 0;
    border-radius: 999px;
    background: var(--border-strong);
    cursor: pointer;
    transition: background .18s ease;
}
.view-dialog label.chk input[type="checkbox"]::after {
    content: '';
    position: absolute;
    top: 2px; left: 2px;
    width: 16px; height: 16px;
    background: #fff;
    border-radius: 50%;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .15), 0 1px 3px rgba(15, 23, 42, .2);
    transition: transform .18s cubic-bezier(.3, 1.2, .5, 1);
}
.view-dialog label.chk input[type="checkbox"]:checked { background: var(--primary); }
.view-dialog label.chk input[type="checkbox"]:checked::after { transform: translateX(16px); }
html[data-theme="dark"] .view-dialog label.chk input[type="checkbox"] { background: #475569; }
html[data-theme="dark"] .view-dialog label.chk input[type="checkbox"]::after { background: #f1f5f9; }
html[data-theme="dark"] .view-dialog label.chk input[type="checkbox"]:checked { background: var(--primary); }

.view-dialog-actions {
    display: flex;
    justify-content: flex-end;
    gap: .5rem;
    margin-top: 1.2rem;
}
.view-dialog-actions .btn { font-weight: 600; }

/* Mobile responsive */
@media (max-width: 820px) {
    .archive-toolbar { grid-template-columns: 1fr; gap: .5rem; padding: .65rem .85rem; }
    .archive-search { grid-column: 1; }
    .toolbar-actions { grid-column: 1; flex-wrap: wrap; }
    /* On narrow screens the sidebar becomes a slide-in drawer instead of
       stacking on top of the list, which would otherwise eat half the viewport. */
    .archive-body { flex-direction: column; position: relative; }
    .archive-sidebar {
        position: fixed; top: 0; left: 0; bottom: 0;
        width: 280px; max-width: 85vw;
        background: var(--surface);
        border-right: 1px solid var(--border);
        box-shadow: 8px 0 24px rgba(15,23,42,.18);
        z-index: 2500;
        transform: translateX(-100%);
        transition: transform .2s ease;
        overflow-y: auto;
    }
    .archive-sidebar.is-open { transform: translateX(0); }
    .archive-sidebar-backdrop {
        display: block;
        position: fixed; inset: 0; background: rgba(15,23,42,.35);
        z-index: 2499; opacity: 0; pointer-events: none;
        transition: opacity .2s ease;
    }
    .archive-sidebar-backdrop.is-open { opacity: 1; pointer-events: auto; }
    .sidebar-toggle-btn {
        display: inline-flex; align-items: center; gap: .35rem;
        padding: 0 .7rem; height: 34px;
        background: var(--surface); border: 1px solid var(--border);
        border-radius: 8px; cursor: pointer; color: var(--text);
        font-size: .82rem; font-weight: 500; flex: 0 0 auto;
    }
    .sidebar-toggle-btn:hover { border-color: var(--primary); color: var(--primary); }
    .sidebar-toggle-btn { display: inline-flex; }
    /* Narrow-screen tweak: tighten the left cluster, shrink typography a
       notch. The row is already two-line; on mobile it stays two-line,
       just more compact. */
    .mail-row { grid-template-columns: 30px 18px 24px minmax(0, 1fr); gap: 0; padding: .55rem .5rem .55rem .65rem; }
    .read-btn { height: 20px; }
    .mail-row > .mail-content { padding-left: .5rem; padding-right: 0; cursor: pointer; }
    .mail-from { font-size: .85rem; }
    .mail-body { font-size: .8rem; }
    .mail-date { font-size: .7rem; }
    .msg-topbar { padding: .5rem .85rem; flex-wrap: wrap; }
    .msg-container { padding: 1rem .85rem 2rem; }
    .msg-subject { font-size: 1.2rem; }
    .msg-body { padding: 1rem 1.1rem; }
    .msg-notes { padding: 1rem; margin-top: 1rem; }
    .preview-panel.open { width: 100% !important; position: fixed; inset: 0; z-index: 500; }
    .preview-handle { display: none; }
    .pagination-bar { flex-direction: column; gap: .5rem; padding: .5rem .85rem; }
    .pagination-range, .pagination-jump { order: 2; }
    .pagination-controls { order: 1; flex-wrap: wrap; justify-content: center; }
    .add-grid { grid-template-columns: 1fr !important; }
    .app-nav .user-email { display: none; }
    .app-nav .nav-tools { padding-left: .35rem; margin-left: .35rem; }
    .adv-panel .adv-row-2 { grid-template-columns: 1fr; }
    .prefs-panel { width: 94vw; max-height: 90vh; }
    .cmdk-panel { width: 94vw; }
}

/* New-messages banner */
.new-msg-banner {
    position: fixed; top: 70px; left: 50%; transform: translateX(-50%);
    background: linear-gradient(135deg, var(--primary), #8b5cf6); color: #fff;
    padding: .45rem .5rem .45rem 1.1rem; border-radius: 999px;
    box-shadow: 0 6px 20px rgba(79,70,229,.35);
    font-size: .85rem; font-weight: 500;
    z-index: 1000; animation: slide-down .25s ease;
    display: inline-flex; align-items: center; gap: .35rem;
}
.new-msg-banner .nmb-link { color: #fff; text-decoration: none; padding: .05rem .35rem; border-radius: 999px; }
.new-msg-banner .nmb-link:hover { background: rgba(255,255,255,.15); }
.new-msg-banner .nmb-close {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 50%;
    background: rgba(255,255,255,.18); border: 0; color: #fff;
    font-size: 14px; line-height: 1; cursor: pointer;
    margin-left: .2rem; transition: background .12s ease;
}
.new-msg-banner .nmb-close:hover { background: rgba(255,255,255,.3); }
@keyframes slide-down { from { transform: translate(-50%, -20px); opacity: 0; } to { transform: translate(-50%, 0); opacity: 1; } }
.adv-panel label { display: flex; flex-direction: column; gap: .25rem; margin: 0; }
.adv-panel label span { font-size: .78rem; color: var(--text-muted); font-weight: 500; }
.adv-panel input[type="text"], .adv-panel input[type="date"], .adv-panel label input:not([type="checkbox"]) {
    border: 1px solid var(--border-strong); border-radius: 8px;
    padding: .45rem .65rem; font-size: .88rem;
    background: var(--surface); color: var(--text);
}
.adv-panel input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(79,70,229,.15); }
.adv-panel .adv-checks { display: flex; flex-wrap: wrap; gap: 1.5rem; padding: .5rem 0; border-top: 1px solid var(--border); margin-top: .25rem; }
.adv-panel .adv-checks .chk { flex-direction: row; align-items: center; gap: .45rem; font-size: .85rem; color: var(--text); cursor: pointer; }
.adv-panel .adv-actions { display: flex; justify-content: flex-end; gap: .5rem; margin-top: .5rem; }

/* Filter pills */
.filter-pills {
    display: flex; flex-wrap: wrap; gap: .45rem; align-items: center;
    padding: .75rem 1.5rem;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
    font-size: .82rem;
}
/* "Filters" lead-in - quieter than the chips themselves, with a small funnel
   icon so the bar immediately reads as "active filters" even when a user has
   only one chip. */
.filter-pills-label {
    display: inline-flex; align-items: center; gap: .35rem;
    color: var(--text-muted); font-weight: 600;
    font-size: .72rem; letter-spacing: .04em; text-transform: uppercase;
    margin-right: .25rem;
}
.filter-pills-label svg { opacity: .75; }

/* Filter pill - structured as: [icon] [label] [value] [×].
   label is muted, value is semibold - reads like "from: **alice**" without
   the old awkward ":value" rendering. Body of the pill is informational
   (span), only the × removes. */
.filter-pill {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .25rem .35rem .25rem .55rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--text);
    text-decoration: none; font-size: .78rem;
    line-height: 1.15;
    cursor: default;
    box-shadow: 0 1px 1px rgba(15, 23, 42, .03);
    transition: border-color .12s ease, box-shadow .12s ease;
    max-width: 320px;
}
.filter-pill:hover { border-color: var(--border-strong, var(--border)); box-shadow: 0 1px 3px rgba(15, 23, 42, .06); }

.filter-pill-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 16px; height: 16px;
    font-size: .85rem; line-height: 1;
    flex: 0 0 auto;
}
.filter-pill-icon--star { color: #eab308; }

.filter-pill-label {
    color: var(--text-muted);
    font-weight: 500;
    flex: 0 0 auto;
}
.filter-pill-value {
    color: var(--text);
    font-weight: 600;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    max-width: 200px;
    min-width: 0;
}
/* Toggle-style chips (has attachment, unread only, starred only) have no
   value - bump the label weight so it reads as the chip's identity. */
.filter-pill--toggle .filter-pill-label {
    color: var(--text); font-weight: 600;
}

.filter-pill-remove {
    display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px;
    border-radius: 4px;
    border: 0; background: transparent; padding: 0;
    color: var(--text-subtle, var(--text-muted));
    cursor: pointer;
    text-decoration: none;
    transition: background .12s ease, color .12s ease;
    margin-left: .1rem;
    flex: 0 0 auto;
}
.filter-pill-remove svg { width: 12px; height: 12px; display: block; }
.filter-pill-remove:hover { background: var(--danger-bg); color: var(--danger); }
.filter-pill-remove:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 1px;
}

/* "Clear all" is a subtle trailing link-button - not a chip, since it IS
   the wrap-up action, not a filter. */
.filter-pill.clear-all {
    background: transparent; border-color: transparent; box-shadow: none;
    color: var(--text-muted); text-decoration: underline;
    cursor: pointer; padding: .25rem .7rem;
    font-size: .78rem; font-weight: 500;
    margin-left: .15rem;
}
.filter-pill.clear-all:hover { color: var(--danger); background: transparent; }

/* "Save as view" CTA inside the filter bar - primary-tinted pill so the
   user immediately grasps it's an action on the active filters, not just a
   chip. Placed inline right after the filter pills so it's visually attached
   to "these filters". */
.filter-pill.save-view-pill {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .3rem .85rem;
    background: var(--primary);
    border: 1px solid var(--primary);
    color: #fff;
    font-weight: 600;
    font-size: .78rem;
    border-radius: 999px;
    cursor: pointer;
    transition: background .12s ease, color .12s ease, border-color .12s ease, box-shadow .12s ease;
    box-shadow: 0 1px 2px rgba(79, 70, 229, .25);
    margin-left: .3rem;
}
.filter-pill.save-view-pill:hover {
    background: var(--primary-hover);
    border-color: var(--primary-hover);
    color: #fff;
    box-shadow: 0 2px 6px rgba(79, 70, 229, .35);
}
.filter-pill.save-view-pill svg { color: currentColor; }
html[data-theme="dark"] .filter-pill.save-view-pill {
    background: var(--primary);
    color: #0f172a;
    box-shadow: 0 1px 3px rgba(129, 140, 248, .4);
}
html[data-theme="dark"] .filter-pill.save-view-pill:hover {
    background: var(--primary-hover);
    color: #0f172a;
}

/* Keyboard help dialog */
dialog.kbd-help {
    border: 1px solid var(--border); border-radius: 14px; padding: 1.5rem 1.75rem;
    background: var(--surface); color: var(--text); box-shadow: var(--shadow);
    max-width: 420px; width: 90vw;
}
dialog.kbd-help::backdrop { background: rgba(15,23,42,.5); backdrop-filter: blur(2px); }
dialog.kbd-help h3 { font-size: 1.1rem; font-weight: 700; margin: 0 0 1rem; }
dialog.kbd-help dl { display: grid; grid-template-columns: auto 1fr; gap: .5rem 1rem; margin: 0 0 1rem; }
dialog.kbd-help dt { font-family: inherit; }
dialog.kbd-help dd { margin: 0; color: var(--text-muted); font-size: .88rem; }
dialog.kbd-help kbd {
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: 4px; padding: 1px 6px; font-family: ui-monospace, monospace;
    font-size: .78rem; color: var(--text);
}

/* ── Command palette ──
   Linear-/Raycast-style quick-action dialog. Sections group related
   commands, each row has a consistent icon + label + optional status
   badge. Keyboard and mouse drive the same active-index so hover and
   arrow-keys feel unified. */
.cmdk-backdrop {
    position: fixed; inset: 0; z-index: 9999;
    background: rgba(15,23,42,.5); backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    display: flex; align-items: flex-start; justify-content: center;
    padding-top: 10vh;
    animation: cmdk-fade-in .12s ease;
}
.cmdk-backdrop[hidden] { display: none; }
@keyframes cmdk-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes cmdk-pop { from { opacity: 0; transform: translateY(-6px) scale(.98); } to { opacity: 1; transform: none; } }

.cmdk-panel {
    width: min(680px, 92vw);
    max-height: 72vh;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: 0 30px 80px rgba(15, 23, 42, .35), 0 2px 8px rgba(15, 23, 42, .1);
    overflow: hidden;
    display: flex; flex-direction: column;
    animation: cmdk-pop .14s ease;
}

/* Header: search input + Esc kbd + explicit close button */
.cmdk-inputwrap {
    display: flex; align-items: center; gap: .65rem;
    padding: .9rem 1rem;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
}
.cmdk-icon { width: 18px; height: 18px; color: var(--text-muted); flex: 0 0 auto; }
.cmdk-inputwrap input {
    flex: 1 1 auto; min-width: 0;
    border: 0; outline: 0;
    background: transparent; color: var(--text);
    font-size: 1rem; letter-spacing: -.005em;
}
.cmdk-inputwrap input::placeholder { color: var(--text-subtle); }
.cmdk-esc {
    flex: 0 0 auto;
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: 5px; padding: 1px 6px;
    font-family: ui-monospace, monospace; font-size: .72rem;
    color: var(--text-muted);
}
.cmdk-close {
    flex: 0 0 auto;
    width: 28px; height: 28px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: 0; border-radius: 6px;
    color: var(--text-muted); cursor: pointer;
    transition: background .1s ease, color .1s ease;
}
.cmdk-close:hover { background: var(--surface-2); color: var(--text); }

/* Results: scrollable section list */
.cmdk-results {
    flex: 1 1 auto;
    min-height: 200px;
    max-height: 56vh;
    overflow-y: auto;
    padding: .3rem .45rem .45rem;
}

/* Section header - small uppercase label, sits above a contiguous
   group of items of that section. */
.cmdk-section {
    padding: .6rem .75rem .25rem;
    font-size: .66rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: .08em;
    color: var(--text-subtle, var(--text-muted));
}

/* Row - icon + label + optional status badge */
.cmdk-item {
    display: flex; align-items: center; gap: .7rem;
    padding: .5rem .75rem;
    border-radius: 8px;
    cursor: pointer;
    font-size: .9rem; color: var(--text);
    line-height: 1.2;
    user-select: none;
    transition: background .08s ease, color .08s ease;
}
.cmdk-item.active {
    background: var(--primary-bg);
    color: var(--primary);
}
.cmdk-item-icon {
    flex: 0 0 auto;
    width: 18px; height: 18px;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--text-muted);
}
.cmdk-item-icon svg {
    width: 16px; height: 16px;
    fill: none; stroke: currentColor; stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round;
}
.cmdk-item.active .cmdk-item-icon { color: var(--primary); }
.cmdk-item-label {
    flex: 1 1 auto;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-weight: 500;
}

/* Status badge - "active", "on", "current" - tiny pill on the right
   so users see at a glance what's applied without reading the URL. */
.cmdk-badge {
    flex: 0 0 auto;
    font-size: .64rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: .05em;
    padding: 2px 7px;
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--text-muted);
}
.cmdk-badge-on,
.cmdk-badge-active,
.cmdk-badge-current {
    background: var(--primary); color: #fff;
}
.cmdk-item.active .cmdk-badge {
    background: var(--primary); color: #fff;
}

/* Empty-state when search returns nothing */
.cmdk-empty {
    padding: 2rem 1rem; text-align: center;
    color: var(--text-muted); font-size: .85rem;
}

/* Footer - legend + subtle brand mark */
.cmdk-footer {
    display: flex; align-items: center; gap: 1rem;
    padding: .5rem 1rem;
    border-top: 1px solid var(--border);
    background: var(--surface-2);
    font-size: .7rem; color: var(--text-muted);
}
.cmdk-footer-keys { display: inline-flex; align-items: center; gap: .25rem; }
.cmdk-footer-brand { margin-left: auto; font-weight: 600; letter-spacing: .02em; color: var(--text-subtle); }
.cmdk-footer kbd {
    background: var(--surface); border: 1px solid var(--border);
    padding: 1px 5px; border-radius: 4px;
    font-family: ui-monospace, monospace; font-size: .68rem;
    color: var(--text-muted);
}

/* Typography */
h1, h2, h3, h4, h5, h6 { letter-spacing: -.015em; color: var(--text); }
.page-title,
.page-subtitle,
.section-title { cursor: default; user-select: none; }
.page-title { font-size: 1.5rem; font-weight: 700; margin: 0; }
.page-subtitle { font-size: .875rem; color: var(--text-muted); margin-top: .25rem; }
.section-title { font-size: 1rem; font-weight: 600; color: var(--text); margin: 0 0 .75rem; }

/* Cards */
.card.surface {
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
    box-shadow: var(--shadow-sm);
    color: var(--text);
}
html[data-theme="dark"] .card.surface,
html[data-theme="dark"] .card { background: var(--surface); color: var(--text); }
.card.surface .card-body { padding: 1.25rem 1.5rem; }

/* Buttons */
.btn { border-radius: 8px; font-weight: 500; font-size: .875rem; padding: .5rem 1rem; }
.btn-primary { background: var(--primary); border-color: var(--primary); }
.btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
/* Cleaner disabled state than Bootstrap's washed-out violet (which
   reads as "broken primary" rather than "not yet ready"). Neutral
   greys read as a clearly inactive surface; a faint dotted border
   keeps the affordance shape so the button doesn't disappear. */
.btn-primary:disabled,
.btn-primary.disabled,
.btn-primary.is-disabled {
    background: var(--surface-2) !important;
    border-color: var(--border) !important;
    color: var(--text-subtle, var(--text-muted)) !important;
    box-shadow: none !important;
    opacity: 1 !important;
    cursor: not-allowed;
}
.btn-primary:disabled svg,
.btn-primary.disabled svg,
.btn-primary.is-disabled svg { opacity: .55; }
.btn-lg { padding: .65rem 1.5rem; font-size: 1rem; border-radius: 10px; }
.btn-sm { padding: .3rem .7rem; font-size: .8rem; border-radius: 6px; }
.btn-outline-secondary { color: var(--text-muted); border-color: var(--border-strong); background: var(--surface); }
.btn-outline-secondary:hover { background: var(--surface-2); color: var(--text); border-color: var(--border-strong); }
.btn-outline-primary { color: var(--primary); border-color: var(--primary); background: var(--surface); }
.btn-outline-primary:hover { background: var(--primary-bg); color: var(--primary); border-color: var(--primary); }

/* Forms */
.form-control { border: 1px solid var(--border-strong); border-radius: 8px; font-size: .9rem; padding: .55rem .85rem; }
.form-control:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(79, 70, 229, .15); }
.form-control-lg { padding: .75rem 1rem; font-size: 1rem; }
.form-label { font-size: .825rem; font-weight: 500; color: var(--text); margin-bottom: .35rem; }
.form-text { color: var(--text-muted); font-size: .8rem; }

/* Progress */
.progress { background: var(--surface-2); border-radius: 999px; overflow: hidden; }
.progress-bar { background: var(--primary); transition: width .3s ease; }

/* Badges & pills */
.pill { display: inline-flex; align-items: center; padding: .2rem .6rem; font-size: .75rem; font-weight: 500; border-radius: 999px; background: var(--surface-2); color: var(--text-muted); }
.pill.pill-primary { background: var(--primary-bg); color: var(--primary); }
.pill.pill-success { background: var(--success-bg); color: var(--success); }
.pill.pill-warning { background: var(--warning-bg); color: var(--warning); }
.pill.pill-danger { background: var(--danger-bg); color: var(--danger); }

/* Alerts */
.alert { border-radius: 8px; border: 1px solid transparent; font-size: .875rem; padding: .75rem 1rem; }
.alert-danger { background: var(--danger-bg); border-color: #fecaca; color: var(--danger); }
.alert-info { background: var(--primary-bg); border-color: #c7d2fe; color: var(--primary); }

/* Hero (legacy - used in a few places) */
.hero { padding: 4rem 1.5rem 3rem; text-align: center; }
.hero h1 { font-size: 2.5rem; font-weight: 800; letter-spacing: -.03em; margin-bottom: .75rem; }
.hero p { font-size: 1.05rem; color: var(--text-muted); max-width: 560px; margin: 0 auto 1.5rem; }

/* ==============================================================
   Home landing - hero + stats + recent workspaces + activity feed.
   Width widens past the empty-state max so the two-column dashboard
   doesn't feel cramped at desktop sizes; collapses to a single column
   on narrow viewports below.
   ============================================================== */
.home-landing { max-width: 1080px; margin: 0 auto; padding: 0 1.5rem 4rem; }

.home-hero { text-align: center; padding: 4rem 0 2rem; }
.home-hero-compact { padding: 3rem 0 1.5rem; }
/* Lock the rotating-welcome hero to a fixed minimum height. The
   typewriter clears + retypes the headline/subtitle on JS load, and
   different templates wrap to different line counts; min-height on the
   inner h1/p alone wasn't enough because their `display: inline-flex`
   nested inside an inline parent created subtle baseline shifts that
   the user still saw as CLS. Reserving the worst-case height on the
   <section> itself takes that variability out of the layout entirely -
   nothing below the hero can ever shift due to the welcome message. */
.home-hero-compact.home-hero-rotating {
    min-height: 9.5rem;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
}
.home-hero h1 {
    font-size: 2.25rem; font-weight: 700; letter-spacing: -.03em;
    color: var(--text); margin: 0 0 .6rem; line-height: 1.15;
    /* No min-height here. The outer .home-hero-compact.home-hero-rotating
       block already reserves 9.5rem total for CLS protection - padding
       this h1 out to 2.3em on top of that left a visible gap below
       single-line headlines like "Stay informed." which the user
       flagged as "big gap between these two lines". */
}
.home-hero-sub {
    font-size: .95rem; color: var(--text-muted);
    max-width: 520px; margin: 0 auto 1.5rem; line-height: 1.55;
    /* No subtitle min-height either - same reasoning. The outer
       hero block holds layout stable; padding the subtitle box out
       to 3 lines just for "Click a workspace below to jump into
       its inbox." opened a phantom gap below the hero. */
}

/* ── Birthday celebration banner ═══════════════════════════════════
   Pinned above the Friday banner whenever someone in the user's circle
   has a birthday today. Visual goes louder than the rest of the home
   page on purpose - this is a once-a-year moment per teammate; the
   page should feel like a tiny celebration. Multi-color confetti
   speckle, animated cake mark with flickering candles, gradient
   border that subtly shimmers. */
.bday-banner {
    position: relative;
    display: flex; align-items: center; gap: 1.1rem;
    padding: 1rem 1.25rem 1rem 1.15rem;
    margin: 0 0 1.25rem;
    border-radius: 16px;
    background:
        radial-gradient(1200px circle at 0% 0%, color-mix(in srgb, #ec4899 14%, transparent), transparent 40%),
        radial-gradient(1200px circle at 100% 100%, color-mix(in srgb, #06b6d4 16%, transparent), transparent 40%),
        linear-gradient(135deg, #fff7ed, #fdf2f8 50%, #f0f9ff);
    border: 1px solid color-mix(in srgb, #ec4899 22%, var(--border));
    overflow: hidden;
    box-shadow: 0 6px 24px -10px rgba(236, 72, 153, .35), 0 2px 6px -2px rgba(15, 23, 42, .08);
    animation: bday-banner-in .6s cubic-bezier(.2,.7,.3,1.2);
}
@keyframes bday-banner-in {
    from { opacity: 0; transform: translateY(-6px) scale(.98); }
    to   { opacity: 1; transform: none; }
}
/* Subtle gradient ring around the banner that gently rotates - reads as
   "wrapping paper shimmer" without pulling focus from the message. */
.bday-banner::before {
    content: "";
    position: absolute; inset: -1px;
    border-radius: inherit;
    padding: 1px;
    background: conic-gradient(from var(--bday-angle, 0deg), #06b6d4, #8b5cf6, #ec4899, #f59e0b, #06b6d4);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    pointer-events: none;
    animation: bday-shimmer 6s linear infinite;
}
@property --bday-angle { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
@keyframes bday-shimmer { to { --bday-angle: 360deg; } }

/* Confetti speckle backdrop - 18 random-color dots that drift gently
   from the top to settle inside the banner. Each speck has its own
   delay via --bday-i so they cascade. */
.bday-confetti {
    position: absolute; inset: 0;
    overflow: hidden;
    pointer-events: none;
}
.bday-confetti-speck {
    position: absolute;
    width: 8px; height: 12px;
    border-radius: 2px;
    top: -16px;
    left: calc(8% + (var(--bday-i, 0) * 5%));
    opacity: .7;
    transform: rotate(calc(var(--bday-i, 0) * 23deg));
    animation: bday-fall calc(4s + (var(--bday-i, 0) * .25s)) ease-in-out infinite;
    animation-delay: calc(var(--bday-i, 0) * -.4s);
}
.bday-confetti-speck-0 { background: #ec4899; }
.bday-confetti-speck-1 { background: #f59e0b; }
.bday-confetti-speck-2 { background: #06b6d4; }
.bday-confetti-speck-3 { background: #8b5cf6; }
.bday-confetti-speck-4 { background: #10b981; }
.bday-confetti-speck-5 { background: #ef4444; }
@keyframes bday-fall {
    0%   { transform: translateY(0) rotate(0deg); opacity: 0; }
    15%  { opacity: .9; }
    100% { transform: translateY(120px) rotate(540deg); opacity: 0; }
}

.bday-banner-close {
    position: absolute;
    top: 8px; right: 8px;
    width: 26px; height: 26px;
    z-index: 2;
    display: inline-flex; align-items: center; justify-content: center;
    border: 0;
    border-radius: 999px;
    background: rgba(255, 255, 255, .55);
    color: #831843;
    cursor: pointer;
    backdrop-filter: blur(2px);
    transition: background .12s ease, color .12s ease, transform .12s ease;
}
.bday-banner-close:hover {
    background: #fff;
    color: #831843;
    transform: scale(1.05);
}
.bday-banner-close:focus-visible {
    outline: 2px solid #ec4899;
    outline-offset: 1px;
}
.bday-banner.is-closing {
    animation: bday-banner-out .24s ease forwards;
}
@keyframes bday-banner-out {
    to {
        opacity: 0;
        transform: translateY(-4px) scale(.98);
        max-height: 0;
        margin-top: 0;
        margin-bottom: 0;
        padding-top: 0;
        padding-bottom: 0;
        border-width: 0;
    }
}

.bday-banner-mark {
    flex: 0 0 auto;
    width: 64px; height: 64px;
    display: inline-flex; align-items: center; justify-content: center;
    background: #fff;
    border: 1px solid color-mix(in srgb, #ec4899 30%, var(--border));
    border-radius: 14px;
    box-shadow: 0 4px 12px -4px rgba(236, 72, 153, .25);
    z-index: 1;
}
.bday-banner-mark svg { display: block; }
.bday-flame {
    transform-origin: center bottom;
    animation: bday-flicker 1.4s ease-in-out infinite alternate;
}
.bday-flame-tall { animation-duration: 1.7s; animation-delay: -.4s; }
.bday-banner-mark svg .bday-flame:nth-of-type(3) { animation-duration: 1.2s; animation-delay: -.8s; }
@keyframes bday-flicker {
    from { transform: scaleY(.92) translateY(1px); }
    to   { transform: scaleY(1.05) translateY(-1px); }
}

.bday-banner-body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex; flex-direction: column; gap: .15rem;
    z-index: 1;
}
.bday-banner-headline {
    font-size: 1.05rem;
    font-weight: 800;
    color: #831843;
    letter-spacing: -.01em;
    line-height: 1.25;
}
.bday-banner-sub {
    font-size: .85rem;
    color: #4a044e;
    line-height: 1.45;
}
.bday-banner-faces {
    display: inline-flex;
    margin-top: .45rem;
    align-items: center;
}
.bday-banner-avatar {
    width: 28px; height: 28px;
    border-radius: 50%;
    border: 2px solid #fff;
    box-shadow: 0 1px 3px rgba(15, 23, 42, .12);
    margin-left: -8px;
    background: linear-gradient(135deg, var(--primary), #8b5cf6);
    color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .72rem; font-weight: 700;
    text-transform: uppercase;
    flex: 0 0 auto;
}
.bday-banner-avatar:first-child { margin-left: 0; }
.bday-banner-avatar.is-self {
    box-shadow: 0 0 0 2px #ec4899, 0 1px 3px rgba(15, 23, 42, .15);
}
.bday-banner-overflow {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px;
    margin-left: -8px;
    border-radius: 50%;
    border: 2px solid #fff;
    background: rgba(15, 23, 42, .85);
    color: #fff;
    font-size: .68rem; font-weight: 700;
}
@media (max-width: 640px) {
    .bday-banner { flex-direction: column; align-items: flex-start; gap: .7rem; padding: .85rem .95rem; }
    .bday-banner-headline { font-size: .98rem; }
}

/* Friday celebration banner. Sits between the welcome hero and the
   dashboard. Quiet, on-brand card - the 3-bar gradient mark on the
   left, an inline tagline + joke in the body. No confetti grid
   (felt circus-y), no refresh button (joke is deterministic per
   day so every user sees the same line). The decorative tint on the
   left edge picks up the brand cyan-violet-pink wash. */
.friday-banner {
    display: flex; align-items: center; gap: 1rem;
    padding: .85rem 1.1rem;
    margin: 0 0 1.25rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    position: relative;
    overflow: hidden;
    box-shadow: 0 4px 14px rgba(15, 23, 42, .04);
}
/* Soft brand-color glow on the leading edge. Feels like the 3-bar
   mark is "casting" a tint into the card without painting the whole
   surface. Falls off quickly so the body text reads on a near-white
   background. */
.friday-banner::before {
    content: "";
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 35%;
    background: linear-gradient(90deg,
        rgba(6, 182, 212, .10),
        rgba(139, 92, 246, .06) 50%,
        transparent);
    pointer-events: none;
}
html[data-theme="dark"] .friday-banner::before {
    background: linear-gradient(90deg,
        rgba(6, 182, 212, .18),
        rgba(139, 92, 246, .12) 50%,
        transparent);
}
.friday-banner.is-fresh { animation: friday-pop .35s ease-out; }
@keyframes friday-pop {
    0%   { transform: translateY(-4px); opacity: 0; }
    100% { transform: translateY(0);    opacity: 1; }
}
.friday-banner-mark {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px;
    border-radius: 10px;
    background: var(--surface-2);
    position: relative;
    z-index: 1;
}
.friday-banner-body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex; align-items: baseline; gap: .65rem;
    flex-wrap: wrap;
    position: relative;
    z-index: 1;
}
.friday-banner-tagline {
    flex: 0 0 auto;
    font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
    font-weight: 700;
    font-size: .92rem;
    color: var(--text);
    letter-spacing: -.005em;
    white-space: nowrap;
}
.friday-banner-joke {
    flex: 1 1 auto;
    font-size: .9rem;
    color: var(--text-muted);
    line-height: 1.5;
    overflow-wrap: anywhere;
}
@media (prefers-reduced-motion: reduce) {
    .friday-banner.is-fresh { animation: none; }
}
@media (max-width: 600px) {
    .friday-banner { padding: .75rem .9rem; }
    .friday-banner-body { flex-direction: column; gap: .15rem; align-items: flex-start; }
    .friday-banner-tagline { font-size: .88rem; }
    .friday-banner-joke { font-size: .85rem; }
}

/* Rotating welcome hero - typewriter effect. The headline text and
   the blinking caret sit on the same baseline; the caret keeps a
   fixed width via inline-block + a 2px wedge so the layout doesn't
   reflow as characters are added. The wrapper reserves room for the
   tallest possible single-line headline so the dashboard below
   doesn't jiggle while typing. */
.home-hero-rotating .home-hero-headline {
    display: inline-flex; align-items: baseline; gap: 1px;
    /* CLS protection comes from the outer .home-hero-compact.home-hero-rotating
       block (min-height: 9.5rem with flex centering) - that's what keeps
       content BELOW the hero from jumping when the typewriter clears
       and re-types. The headline itself doesn't need its own min-height
       (an earlier 2.3em pad here added a visible gap between the title
       and the subtitle on single-line headlines, which the user
       flagged as too much spacing). */
}
.home-hero-text { white-space: pre-wrap; }
.home-hero-caret {
    display: inline-block;
    width: 3px; height: 1.05em;
    background: var(--primary);
    border-radius: 1px;
    transform: translateY(.12em);
    animation: home-hero-caret-blink .9s steps(2, end) infinite;
}
/* When the typewriter finishes the caret fades to opacity:0 then gets
   .is-hidden for "stop the blink animation". `display:none` would
   recover its 3px slot - and the inline-flex parent has `text-align:
   center` upstream, so the headline would re-center and visibly shift
   the whole text by ~2px. visibility:hidden keeps the slot reserved,
   no layout move, just an invisible (and now still) caret. */
.home-hero-caret.is-hidden {
    visibility: hidden;
    animation: none;
}
@keyframes home-hero-caret-blink {
    0%, 50% { opacity: 1; }
    51%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
    .home-hero-caret { animation: none; }
}

/* Empty-state ordered list - plain numbers, no cards */
.home-ol {
    list-style: none; padding: 0; margin: 2rem auto 0;
    max-width: 520px; counter-reset: step;
}
.home-ol li {
    counter-increment: step;
    padding: .75rem 0 .75rem 2.5rem;
    position: relative;
    font-size: .95rem; color: var(--text-muted); line-height: 1.5;
    border-bottom: 1px solid var(--border);
}
.home-ol li:last-child { border-bottom: 0; }
.home-ol li::before {
    content: counter(step);
    position: absolute; left: 0; top: .7rem;
    width: 26px; height: 26px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .82rem; font-weight: 600;
    color: var(--text-subtle); border: 1px solid var(--border-strong);
}
.home-ol li span { color: var(--text); font-weight: 600; }

/* Stats - large numbers, minimal chrome */
.home-stats {
    display: flex; justify-content: center; gap: 3rem;
    margin: 0 0 2rem; flex-wrap: wrap;
}
/* Stats are now <a> elements. Cursor goes pointer, no underline, but
   hover lifts the value subtly so the affordance reads. is-static is
   the fallback for stats that have no destination (viewer's view of
   the messages stat with no recent workspace, etc.). */
.home-stat {
    display: flex; flex-direction: column; align-items: center; gap: .25rem;
    text-decoration: none; color: inherit;
    padding: .25rem .65rem;
    border-radius: 8px;
    transition: background .12s ease, transform .08s ease;
}
a.home-stat { cursor: pointer; }
a.home-stat:hover { background: var(--surface-2); }
a.home-stat:hover .home-stat-value { color: var(--primary); }
a.home-stat:active { transform: translateY(1px); }
.home-stat.is-static { cursor: default; }
.home-stat-value {
    font-size: 1.75rem; font-weight: 700; color: var(--text);
    letter-spacing: -.02em; line-height: 1;
    font-variant-numeric: tabular-nums;
    transition: color .12s ease;
}
.home-stat-label { font-size: .75rem; color: var(--text-subtle); display: inline-flex; align-items: center; gap: .35rem; }
.home-stat-delta {
    font-size: .68rem; font-weight: 600; color: var(--primary);
    background: var(--primary-bg); border-radius: 999px;
    padding: 1px 7px; letter-spacing: .01em;
}

/* Mailbox cards */
.quick-open-grid {
    display: flex; flex-direction: column; gap: .5rem;
    margin: 0;
}
.quick-open-card {
    display: flex; align-items: center; gap: .85rem;
    padding: .75rem .9rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    text-decoration: none;
    transition: border-color .12s ease, background .12s ease;
}
.quick-open-card:hover { border-color: var(--border-strong); background: var(--surface-2); }
.quick-open-card:hover .quick-open-arrow { color: var(--primary); transform: translateX(2px); }
.quick-open-avatar {
    width: 30px; height: 30px; border-radius: 50%;
    background: var(--primary-bg); color: var(--primary);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 600; font-size: .82rem; flex: 0 0 auto;
}
.quick-open-avatar.has-emoji {
    background: var(--surface-2); font-size: 1.05rem;
    border: 1px solid var(--border);
}

/* Workspace icon button on the /Workspaces page. Strips button chrome
   so the avatar bubble looks identical to the static viewer-side render
   - hover just adds a subtle ring to invite the click. */
/* Workspace icon affordance on the row header. Renamed to .ws-glyph
   to avoid colliding with the existing .account-icon used by the sync
   indicator (also in this card) - the collision was making owners see
   the wrong glyph because both elements inherited the same styling. */
.account-icon-btn {
    position: relative;
    background: transparent; border: 0; padding: 0;
    flex: 0 0 auto;
    cursor: pointer; line-height: 0;
    border-radius: 50%;
    transition: box-shadow .15s ease, transform .08s ease;
}
.account-icon-btn:hover { box-shadow: 0 0 0 3px var(--primary-bg); transform: translateY(-1px); }
.account-icon-btn:active { transform: translateY(0); }
.account-icon-btn::after {
    /* Tiny pencil overlay - shown on hover to confirm "click to edit". */
    content: ""; position: absolute; right: -2px; bottom: -2px;
    width: 18px; height: 18px; border-radius: 50%;
    background: var(--primary) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><path d='M12 20h9'/><path d='M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z'/></svg>") center/11px no-repeat;
    box-shadow: 0 0 0 2px var(--surface);
    opacity: 0; transition: opacity .15s ease;
    z-index: 2;
}
.account-icon-btn:hover::after { opacity: 1; }
/* Pencil overlay is for owners only - viewers can't change the icon, so
   the affordance shouldn't pretend it's clickable. The non-owner render
   uses a <span class="account-icon-btn"> so the status dot still anchors,
   but the pencil pseudo-element is suppressed. */
span.account-icon-btn::after { content: none; }
span.account-icon-btn:hover { box-shadow: none; transform: none; }

/* Sync-state badge: a small colored dot in the bottom-right of the
   workspace icon, ALWAYS visible. Reads at a glance:
     green   = idle / up-to-date
     blue    = syncing (pulsing)
     amber   = paused
     red     = error
   The dot replaces the big "Up to date" stripe below the header for
   the idle case (the stripe is now hidden when nothing is happening). */
.ws-status-dot {
    position: absolute; right: -2px; bottom: -2px;
    width: 14px; height: 14px; border-radius: 50%;
    background: var(--success);
    box-shadow: 0 0 0 2px var(--surface);
    z-index: 1;
}
.ws-status-dot[data-state="syncing"] {
    background: var(--primary);
    animation: ws-status-pulse 1.4s ease-in-out infinite;
}
.ws-status-dot[data-state="paused"] { background: var(--warning); }
.ws-status-dot[data-state="error"]  { background: var(--danger); }

/* Mini inline progress sliver. Replaces the old "Up to date / 100%"
   stripe that duplicated info already in the header status line. Only
   visible while an active sync runs; idle workspaces hide it via the
   [hidden] attribute. Slim and unobtrusive - the header status text is
   what reads first; this just adds visual confirmation that something
   is happening. */
.ws-mini-progress {
    display: flex; align-items: center; gap: .65rem;
    margin-top: .35rem;
    font-variant-numeric: tabular-nums;
}
.ws-mini-progress[hidden] { display: none; }
.ws-mini-progress-bar {
    flex: 1 1 auto;
    height: 4px;
    background: var(--surface-2);
    border-radius: 999px;
    overflow: hidden;
}
.ws-mini-progress-bar .account-bar {
    height: 100%;
    background: var(--primary);
    border-radius: 999px;
    transition: width .35s ease;
}
.ws-mini-progress .account-pct {
    flex: 0 0 auto;
    font-size: .72rem; font-weight: 700;
    color: var(--text-muted);
    min-width: 2.4rem;
    text-align: right;
}

/* The inline header status sub-line picks up a state-tinted dot. Using
   data-state instead of class swaps so the JS tick can update both the
   text content and the visual treatment in one assignment. */
.account-state-sub[data-state="syncing"]::before { background: var(--primary); }
.account-state-sub[data-state="paused"]::before { background: var(--warning); }
.account-state-sub[data-state="error"]::before { background: var(--danger); }
.account-state-sub[data-state="error"] { color: var(--danger) !important; }
@keyframes ws-status-pulse {
    0%, 100% { box-shadow: 0 0 0 2px var(--surface), 0 0 0 0 color-mix(in srgb, var(--primary) 40%, transparent); }
    50%      { box-shadow: 0 0 0 2px var(--surface), 0 0 0 6px color-mix(in srgb, var(--primary) 0%, transparent); }
}
.account-icon-btn:hover .ws-status-dot,
.account-icon-btn::after { /* intentional: pencil sits ABOVE the status dot on hover */ }

.ws-glyph {
    width: 52px; height: 52px; border-radius: 14px;
    background: var(--avatar-bg);
    color: var(--avatar-color);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: 1.25rem;
    box-shadow: 0 6px 16px color-mix(in srgb, var(--primary) 30%, transparent);
    flex: 0 0 auto;
    transition: transform .15s ease;
}
.ws-glyph.has-emoji {
    background: var(--surface-2); font-size: 1.7rem;
    border: 1px solid var(--border);
    box-shadow: 0 4px 10px rgba(15, 23, 42, .08);
}
.ws-glyph.has-svg {
    background: var(--primary-bg); color: var(--primary);
    border: 1px solid color-mix(in srgb, var(--primary) 28%, var(--border));
    box-shadow: 0 4px 10px color-mix(in srgb, var(--primary) 14%, transparent);
}
.ws-glyph.has-svg svg { display: block; width: 26px; height: 26px; }

/* Icon picker grid. .icon-grid is the legacy class (kept so any inline
   uses still work); .icon-picker-grid is the partial's class - both
   share the same 8-column layout and tile shell. The previous version
   only declared the grid on .icon-grid, so when the partial moved to
   .icon-picker-grid every tile fell to width:100% aspect-ratio:1 and
   filled the entire modal vertically with a single square. */
.icon-grid,
.icon-picker-grid {
    display: grid;
    /* minmax(0, 1fr) breaks the default min-content sizing - without it
       a tile with an emoji glyph that has a wide intrinsic font-metric
       can blow the column past 1fr and the whole grid overflows the
       modal. The user reported the picker bleeding outside the modal
       when packs were full; this is the load-bearing fix. */
    grid-template-columns: repeat(8, minmax(0, 1fr));
    gap: .4rem;
}
.icon-grid-svg { grid-template-columns: repeat(8, minmax(0, 1fr)); }
.icon-tile {
    width: 100%;
    /* min-width:0 tells the layout engine the tile may shrink below its
       intrinsic content - belt-and-suspenders alongside minmax(0,1fr). */
    min-width: 0;
    aspect-ratio: 1;
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: 10px;
    font-size: 1.4rem; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
    color: var(--text-muted);
    transition: border-color .12s ease, transform .08s ease, background .12s ease, color .12s ease, box-shadow .12s ease;
}
.icon-tile:hover {
    border-color: var(--primary);
    background: var(--primary-bg);
    color: var(--primary);
}
.icon-tile:active { transform: scale(.94); }
/* Premium selection style: glyph stays itself (no aggressive fill that
   recolors emojis into pure-white silhouettes). The selected tile gets
   a subtle primary-tinted backdrop, a 2px primary border, a corner
   check badge, and a soft elevation lift. Reads as "selected" without
   shouting. */
.icon-tile.is-selected {
    border-color: var(--primary);
    background: var(--primary-bg);
    color: var(--primary);
    box-shadow:
        0 0 0 1px var(--primary),
        0 8px 20px color-mix(in srgb, var(--primary) 22%, transparent);
    transform: translateY(-1px);
}
.icon-tile.is-selected::after {
    content: "";
    position: absolute;
    top: 5px; right: 5px;
    width: 16px; height: 16px;
    border-radius: 50%;
    background: var(--primary) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>") center/10px no-repeat;
    box-shadow: 0 0 0 2px var(--surface);
}
.icon-tile { position: relative; }
.icon-tile svg { display: block; }
/* Emoji tile uses the same shell but a bigger glyph - emoji intrinsically
   carry color, so we drop the monochrome stroke treatment. */
.icon-tile-emoji-glyph {
    font-size: 1.55rem;
    line-height: 1;
    /* Don't let the emoji's intrinsic font-metric set the tile width. */
    min-width: 0;
    overflow: hidden;
}
/* Photo tiles: full-bleed image cropped to the rounded tile shape.
   The "no padding" treatment gives photos as much pixel area as the
   tile allows; the tile's existing border + selection ring still
   read clearly because they're drawn ON TOP of the image. */
.icon-tile-photo {
    padding: 0;
    overflow: hidden;
    background: var(--surface-2);
}
.icon-tile-photo-img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
    border-radius: 9px; /* match tile border-radius minus the 1px border */
}
/* Avatar bubble showing a photo (catalog photo token). Class-agnostic:
   ANY surface that wraps the icon (workspace card .ws-glyph, home page
   .quick-open-avatar, dropdown entries, ...) gets the photo treatment
   automatically. The wrapper supplies the size and round shape; the
   inner img is forced to fill exactly its container so a 256x256
   source can't blow out a 30x30 avatar slot. */
.has-photo {
    background: var(--surface-2);
    color: transparent;
    overflow: hidden;
    padding: 0;
}
.has-photo > img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
    border-radius: inherit;
}
.icon-tile.is-selected .icon-tile-emoji-glyph { /* stays full color on
    selection - the primary fill behind it is the selection cue. */ }

/* ═══════════════════════════════════════════════════════════════════
   Icon picker - sidebar layout (Apr 2026 redesign).
   ═══════════════════════════════════════════════════════════════════
   Modal is a fixed-height flex column. Body splits into:
     ┌─ shell ──────────────────────────────────────────┐
     │ [sidebar:packs] │ [grid + search]                 │
     └──────────────────────────────────────────────────┘
     ┌─ extras (paste emoji + upload) ──────────────────┐
     └──────────────────────────────────────────────────┘
   Footer (Reset / Cancel / Save).

   Why sidebar over the previous horizontal tab strip:
     · 14 packs no longer overflow off-screen.
     · Active pack is unmistakable (primary accent left edge).
     · Each pack name + count readable in a single column scan.
     · No tab clicks resize the modal - the right grid scrolls.

   On narrow viewports the sidebar collapses to a horizontal scroll
   strip stacked above the grid (same data, mobile-friendly form). */

.icon-picker-dialog {
    height: min(740px, 92vh);
    max-height: 92vh;
}
.icon-picker-content { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.icon-picker-form { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.icon-picker-head { flex: 0 0 auto; }
.icon-picker-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    padding: 0;
    gap: 0;
}

/* ── Shell: sidebar + grid region ─────────────────────────────── */
.picker-shell {
    display: grid;
    grid-template-columns: 220px minmax(0, 1fr);
    flex: 1 1 auto;
    min-height: 0;
    border-bottom: 1px solid var(--border);
}

/* ── Sidebar (left column) ─────────────────────────────────────
   Each pack is a full-width clickable row. Active state uses a
   left-edge accent + tinted background so it pops without being
   noisy. The whole list scrolls vertically if the user adds enough
   packs to overflow (14 fits at default modal height; not a
   concern today but the sidebar is robust to growth). */
.picker-sidebar {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: .5rem .35rem;
    background: var(--surface-2);
    border-right: 1px solid var(--border);
    overflow-y: auto;
    min-height: 0;
}
.picker-sidebar::-webkit-scrollbar { width: 6px; }
.picker-sidebar::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--text-subtle) 35%, transparent); border-radius: 3px; }
.picker-nav-item {
    display: flex; align-items: center;
    width: 100%;
    padding: .5rem .75rem;
    background: transparent;
    border: 0;
    border-left: 3px solid transparent;
    border-radius: 0 7px 7px 0;
    font-size: .85rem; font-weight: 500;
    color: var(--text-muted);
    text-align: left;
    cursor: pointer;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
    white-space: nowrap;
}
.picker-nav-item:hover {
    background: color-mix(in srgb, var(--primary) 6%, transparent);
    color: var(--text);
}
.picker-nav-item.is-active {
    background: color-mix(in srgb, var(--primary) 12%, var(--surface));
    color: var(--primary);
    border-left-color: var(--primary);
    font-weight: 600;
}
.picker-nav-label {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
}
.picker-nav-count {
    flex: 0 0 auto;
    margin-left: .5rem;
    font-size: .7rem;
    font-weight: 600;
    color: var(--text-subtle);
    background: var(--surface);
    padding: 1px 7px;
    border-radius: 999px;
    line-height: 1.45;
    font-variant-numeric: tabular-nums;
}
.picker-nav-item.is-active .picker-nav-count {
    background: var(--primary);
    color: #fff;
}

/* ── Grid region (right column) ────────────────────────────────
   Search input pinned to the top, then the grid stack below it.
   The whole region is the scroll viewport so the search bar stays
   visible while the user pages through 90+ creatures. */
.picker-grid-region {
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 1rem 1.1rem 1.1rem;
    scrollbar-gutter: stable;
}
.picker-grid-region::-webkit-scrollbar { width: 8px; }
.picker-grid-region::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--text-subtle) 35%, transparent); border-radius: 4px; }

.picker-search-row {
    position: sticky; top: 0;
    z-index: 1;
    display: flex; align-items: center;
    gap: .5rem;
    margin: -.5rem -.4rem .9rem;
    padding: .5rem .65rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 9px;
}
.picker-search-icon {
    color: var(--text-subtle);
    flex: 0 0 auto;
}
.picker-search-input {
    flex: 1 1 auto;
    border: 0; outline: 0; background: transparent;
    font-size: .88rem;
    color: var(--text);
    min-width: 0;
}
.picker-search-input::placeholder { color: var(--text-subtle); }
.picker-search-empty {
    flex: 0 0 auto;
    font-size: .78rem;
    color: var(--text-muted);
    font-style: italic;
}

.picker-grid-stack {
    flex: 0 0 auto;
}
.picker-grid {
    display: grid;
    grid-template-columns: repeat(8, minmax(0, 1fr));
    gap: .45rem;
    min-width: 0;
}
.picker-grid.is-hidden { display: none; }
.icon-tile.is-filtered-out { display: none; }

/* Narrow viewports: fewer columns so tiles stay clickable and the
   sidebar collapses to a horizontal strip above the grid. */
@media (max-width: 800px) {
    .picker-shell { grid-template-columns: minmax(0, 1fr); }
    .picker-sidebar {
        flex-direction: row;
        gap: .25rem;
        padding: .45rem;
        border-right: 0;
        border-bottom: 1px solid var(--border);
        overflow-x: auto;
        overflow-y: hidden;
    }
    .picker-nav-item {
        flex: 0 0 auto;
        width: auto;
        padding: .4rem .65rem;
        border-left: 0;
        border-bottom: 2px solid transparent;
        border-radius: 6px;
    }
    .picker-nav-item.is-active {
        border-left: 0;
        border-bottom-color: var(--primary);
    }
    .picker-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}
@media (max-width: 540px) {
    .picker-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* ── Extras row (paste emoji + upload) ─────────────────────────
   Pinned below the shell on a faint background so the user knows
   these are alternatives to the main grid above, not part of it.
   Two compact columns. */
.picker-extras {
    flex: 0 0 auto;
    display: grid;
    grid-template-columns: 1fr 1.4fr;
    gap: 1.1rem;
    padding: .85rem 1.1rem;
    background: var(--surface-2);
}
.picker-extras-label {
    display: block;
    font-size: .72rem;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: .04em;
    margin-bottom: .35rem;
}
.picker-extras-input { font-size: .9rem; }
.picker-extras-emoji,
.picker-extras-upload { min-width: 0; }
@media (max-width: 600px) {
    .picker-extras { grid-template-columns: 1fr; gap: .75rem; }
}
.icon-upload-hint { font-size: .72rem; }

/* Team icon button on the team card head - reuses the same affordance
   pattern as workspace .account-icon-btn (status-dot replaced by a
   subtle lift + ring on hover). The .team-card-icon span the button
   wraps inherits its size from the existing class. */
.team-icon-btn {
    background: transparent; border: 0; padding: 0;
    flex: 0 0 auto;
    cursor: pointer; line-height: 0;
    border-radius: 8px;
    transition: box-shadow .15s ease, transform .08s ease;
    position: relative;
}
.team-icon-btn:hover { box-shadow: 0 0 0 3px var(--primary-bg); transform: translateY(-1px); }
.team-icon-btn:active { transform: translateY(0); }
.team-icon-btn::after {
    content: ""; position: absolute; right: -3px; bottom: -3px;
    width: 16px; height: 16px; border-radius: 50%;
    background: var(--primary) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><path d='M12 20h9'/><path d='M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z'/></svg>") center/10px no-repeat;
    box-shadow: 0 0 0 2px var(--surface);
    opacity: 0; transition: opacity .15s ease;
}
.team-icon-btn:hover::after { opacity: 1; }

/* Team-card-icon variants. The shared WorkspaceIcon helper emits one of:
     .team-card-icon            (first-letter fallback - default gradient)
     .team-card-icon.has-emoji  (typed emoji - lighter background)
     .team-card-icon.has-svg    (catalog token - primary-tinted, SVG)
   Sized 28x28 to fit beside the team name. */
.team-card-icon.has-emoji {
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    font-size: 1.05rem;
    box-shadow: none;
}
.team-card-icon.has-svg {
    background: var(--surface);
    color: var(--primary);
    border: 1px solid color-mix(in srgb, var(--primary) 30%, var(--border));
    box-shadow: none;
}
.team-card-icon.has-svg svg { display: block; width: 16px; height: 16px; }

/* ================== Collaborator profile card modal ==================
   Shown when an avatar pip in a workspace's collaborator strip is
   clicked. Read-only for now: avatar + name + email + role + which
   workspace this membership refers to. Banner-with-overlap pattern
   matches modern dashboards (Linear, Notion). All data is populated
   from the clicked pip's data-* attrs - no extra fetch.
   ===================================================================== */
.collab-profile-card {
    border-radius: 16px;
    border: 1px solid var(--border);
    overflow: hidden;
    position: relative;
    box-shadow: 0 24px 60px rgba(15, 23, 42, .25);
}
/* Close affordance now sits as a proper top-right control on the
   gradient banner. The previous "faint white pill behind a tiny X"
   read as floating debris over the banner art - replaced with a
   28x28 dark-translucent disc that has visible contrast against the
   gradient AND against a future light-banner variant. */
.collab-profile-close {
    position: absolute; top: 16px; right: 16px;
    z-index: 3;
    width: 28px; height: 28px;
    padding: 0;
    border-radius: 50%;
    background: rgba(15, 23, 42, .35);
    border: 0;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    /* Bootstrap btn-close ships its own SVG via background-image. We
       swap to a white-tinted variant so it reads on the dark backdrop. */
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23ffffff'%3E%3Cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414Z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    background-size: 12px 12px;
    opacity: 1;
    transition: background-color .15s ease, transform .08s ease;
}
.collab-profile-close:hover { background-color: rgba(15, 23, 42, .55); transform: scale(1.08); }
.collab-profile-close:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.collab-profile-banner {
    /* Per-user deterministic gradient. JS sets --profile-banner-h1
       and --profile-banner-h2 from a hash of the display name so each
       person's card has its own hue. The defaults below are a sensible
       fallback for the loading / error states where JS hasn't run yet. */
    --profile-banner-h1: 260deg;
    --profile-banner-h2: 320deg;
    height: 108px;
    background:
        radial-gradient(circle at 20% 0%, rgba(255,255,255,.18) 0%, transparent 55%),
        radial-gradient(circle at 80% 100%, rgba(255,255,255,.14) 0%, transparent 50%),
        linear-gradient(135deg,
            hsl(var(--profile-banner-h1), 70%, 58%),
            hsl(var(--profile-banner-h2), 72%, 60%));
    position: relative;
}
/* Birthday ribbon overlaying the banner. Sits at the top center
   above the avatar, calls attention with a 🎂 + warm copy. The
   ribbon's pill shape and gold gradient cuts through the banner
   without needing to compete with it. */
.collab-profile-birthday {
    position: absolute;
    top: 14px; left: 50%;
    transform: translateX(-50%);
    z-index: 2;
    display: inline-flex; align-items: center; gap: .4rem;
    padding: .3rem .85rem;
    border-radius: 999px;
    background: linear-gradient(135deg, #fbbf24, #f97316);
    color: #fff;
    font-size: .78rem; font-weight: 700;
    letter-spacing: -.005em;
    box-shadow:
        0 1px 2px rgba(15, 23, 42, .15),
        0 6px 18px -4px rgba(249, 115, 22, .55);
    animation: profileBirthdayPop .35s ease-out both;
}
.collab-profile-birthday-emoji { font-size: .95rem; line-height: 1; }
@keyframes profileBirthdayPop {
    from { opacity: 0; transform: translate(-50%, -8px) scale(.92); }
    to   { opacity: 1; transform: translate(-50%, 0)    scale(1); }
}
/* Subtle dot grid layered over the gradient for visual texture - keeps
   the banner from feeling like a flat blob without competing with the
   avatar/name below. */
.collab-profile-banner::after {
    content: "";
    position: absolute; inset: 0;
    background-image: radial-gradient(rgba(255,255,255,.18) 1px, transparent 1.2px);
    background-size: 14px 14px;
    opacity: .5;
    mix-blend-mode: overlay;
    pointer-events: none;
}
.collab-profile-body {
    padding: 0 1.75rem 1.75rem;
    text-align: center;
    margin-top: -52px;
}
.collab-profile-avatar {
    width: 104px; height: 104px; border-radius: 50%;
    background: var(--avatar-bg);
    color: var(--avatar-color);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: 2.5rem;
    box-shadow:
        0 0 0 5px var(--surface),
        0 0 0 6px color-mix(in srgb, var(--primary) 18%, transparent),
        0 14px 28px rgba(15, 23, 42, .22);
    margin: 0 auto 1rem;
    overflow: hidden;
    position: relative;
}
.collab-profile-avatar img {
    width: 100%; height: 100%; object-fit: cover; border-radius: 50%;
}
/* Avatar wrapper hosts the online dot indicator. The avatar itself
   keeps its existing inline-flex centering so the letter glyph
   (e.g. "L") stays optically centered inside the circle; the
   wrapper acts only as a positioning context for the dot.
   Earlier this override forced display: block on the avatar which
   broke the align-items/justify-content centering set up at the
   base rule, leaving the letter pinned to the top-left of the box. */
.collab-profile-avatar-wrap {
    position: relative;
    display: inline-block;
    margin: 0 auto 1rem;
}
.collab-profile-avatar-wrap .collab-profile-avatar { margin: 0; }
.collab-profile-online-dot {
    position: absolute;
    right: 6px; bottom: 6px;
    width: 16px; height: 16px;
    border-radius: 50%;
    background: #22c55e;
    box-shadow:
        0 0 0 3px var(--surface),
        0 0 0 4px color-mix(in srgb, #22c55e 32%, transparent);
}

/* ── Cookie counter pill + send button (profile card) ─────────────
   The cluster sits at the bottom of the card body, BELOW bio / joke,
   as the "appreciation" footer: one row with the count chip and the
   send button. The .profile-cookie-msg sits underneath as a second
   line. The cluster is centered to match the card's overall vertical
   rhythm, and wraps on narrow widths so neither child clips. */
.profile-cookie-count {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .25rem .7rem;
    margin: 0;
    background: linear-gradient(135deg, color-mix(in srgb, #f59e0b 22%, var(--surface-2)), color-mix(in srgb, #ec4899 18%, var(--surface-2)));
    border: 1px solid color-mix(in srgb, #f59e0b 28%, var(--border));
    border-radius: 999px;
    font-size: .78rem;
    font-weight: 700;
    color: #92400e;
    line-height: 1.1;
}
.profile-cookie-emoji { font-size: .95rem; line-height: 1; }
.profile-cookie-num { font-weight: 800; }
.profile-cookie-label { font-weight: 600; opacity: .85; }
html[data-theme="dark"] .profile-cookie-count {
    background: linear-gradient(135deg, color-mix(in srgb, #f59e0b 22%, var(--surface)), color-mix(in srgb, #ec4899 18%, var(--surface)));
    color: #fbbf24;
}

.profile-cookie-actions {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: .65rem;
    /* Generous top margin + a hairline separator so the appreciation
       footer feels like its own beat, not glued to the bio/joke above
       it or the link chips below. The hairline is a thin centered
       divider that doesn't span the full width - softer than a
       hard <hr>. */
    margin: 1.25rem 0 1.25rem;
    padding-top: 1.1rem;
    position: relative;
}
.profile-cookie-actions::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 60%;
    height: 1px;
    background: var(--border);
    opacity: .7;
}
/* The inline status / error message takes its own row below the
   chips so it never squeezes the count or button. */
.profile-cookie-actions .profile-cookie-msg { flex: 1 0 100%; margin-top: .25rem; }
.profile-cookie-btn {
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .55rem 1.1rem;
    background: linear-gradient(135deg, #f59e0b, #ec4899);
    color: #fff;
    border: 0;
    border-radius: 999px;
    cursor: pointer;
    font-family: inherit;
    font-size: .87rem;
    font-weight: 700;
    letter-spacing: -.005em;
    box-shadow:
        0 1px 2px rgba(15, 23, 42, .08),
        0 4px 12px -4px color-mix(in srgb, #f59e0b 55%, transparent);
    transition: transform .12s ease, box-shadow .15s ease, filter .15s ease, opacity .15s ease;
}
.profile-cookie-btn:hover {
    transform: translateY(-1px);
    box-shadow:
        0 1px 2px rgba(15, 23, 42, .1),
        0 8px 18px -4px color-mix(in srgb, #f59e0b 65%, transparent);
    filter: brightness(1.04);
}
.profile-cookie-btn:active { transform: translateY(0); filter: brightness(.96); }
.profile-cookie-btn:disabled { cursor: default; opacity: .55; box-shadow: none; transform: none; filter: none; }
.profile-cookie-btn.is-sent {
    background: linear-gradient(135deg, #84cc16, #10b981);
    box-shadow: 0 1px 2px rgba(15, 23, 42, .08), 0 4px 12px -4px color-mix(in srgb, #10b981 55%, transparent);
    opacity: 1;
}
.profile-cookie-btn-emoji { font-size: 1.05rem; line-height: 1; }
.profile-cookie-msg {
    font-size: .76rem;
    color: var(--text-muted);
    text-align: center;
    max-width: 280px;
}
.profile-cookie-msg.is-error { color: #b91c1c; }

/* Cookie burst overlay - tiny emoji shower from the avatar on a
   successful send. Each particle gets a per-instance angle + delay
   so the burst looks organic. Removed from the DOM after 1.2s. */
.profile-cookie-burst {
    position: absolute;
    top: 50%; left: 50%;
    font-size: 1.3rem;
    line-height: 1;
    pointer-events: none;
    transform: translate(-50%, -50%) scale(.6);
    opacity: 0;
    animation: cookie-burst .9s ease-out forwards;
    animation-delay: calc(var(--burst-i, 0) * .05s);
}
@keyframes cookie-burst {
    0%   { transform: translate(-50%, -50%) scale(.6); opacity: 0; }
    20%  { opacity: 1; }
    100% {
        transform:
            translate(calc(-50% + cos(calc(var(--burst-i, 0) * 60deg)) * 90px),
                      calc(-50% + sin(calc(var(--burst-i, 0) * 60deg)) * 90px - 30px))
            scale(1) rotate(calc(var(--burst-i, 0) * 60deg));
        opacity: 0;
    }
}

.collab-profile-name {
    margin: 0 0 .25rem;
    font-size: 1.4rem; font-weight: 700; color: var(--text);
    letter-spacing: -.02em;
    overflow-wrap: anywhere;
}
/* One-line "ROLE · LOCATION" sub-meta under the name. The role gets
   a soft pill chip (it's job-title context, valuable enough to
   isolate); location stays as a faded suffix. The "·" only appears
   when both halves are present. */
.collab-profile-submeta {
    font-size: .82rem; color: var(--text);
    margin: 0 0 .4rem;
    display: inline-flex; align-items: center; gap: .5rem;
    flex-wrap: wrap; justify-content: center;
}
.collab-profile-role-chip {
    display: inline-flex; align-items: center;
    padding: .15rem .65rem;
    background: var(--primary-bg);
    color: var(--primary);
    border-radius: 999px;
    font-size: .72rem; font-weight: 700;
    letter-spacing: .015em;
    text-transform: uppercase;
    line-height: 1.4;
}
.collab-profile-location { color: var(--text-muted); }
.collab-profile-submeta-sep { color: var(--text-subtle); }
.collab-profile-tz-sep { color: var(--text-subtle); }
.collab-profile-tz {
    display: inline-flex; align-items: center; gap: 0.3rem;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}
.collab-profile-tz-icon { opacity: 0.7; flex: 0 0 auto; }
.collab-profile-tz-label {
    color: color-mix(in srgb, var(--text-muted) 80%, transparent);
}
.collab-profile-email {
    font-size: .82rem; color: var(--text-muted);
    word-break: break-all;
    margin-bottom: 1rem;
}

/* ── Quick actions row (Mention · Email) ──────────────────────────
   In-app interactions, distinct from the external link chips below.
   Centered cluster of small text + icon buttons; same shape as the
   .eml/Trash buttons in the message header so the visual language
   is consistent across the app. */
.collab-profile-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: .5rem;
    margin: 0 0 1.25rem;
}
.collab-profile-action {
    display: inline-flex; align-items: center; gap: .4rem;
    padding: .35rem .8rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text-muted);
    font-family: inherit;
    font-size: .8rem; font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: background .12s ease, color .12s ease, border-color .12s ease, transform .08s ease;
}
.collab-profile-action:hover {
    background: var(--primary-bg);
    border-color: color-mix(in srgb, var(--primary) 32%, var(--border));
    color: var(--primary);
    transform: translateY(-1px);
}
.collab-profile-action svg { flex: 0 0 auto; }

/* ── Section blocks (About / For fun / Connect / Appreciation) ────
   Each section is a wrapper that pairs an uppercase eyebrow label
   with a body block. Hidden when the body is empty; the label
   disappears with it. Aligned left so the eyebrow tracks above the
   block it labels. */
.collab-profile-section {
    text-align: left;
    margin: 0 0 1rem;
}
.collab-profile-section-label {
    display: block;
    font-size: .65rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text-subtle);
    margin: 0 0 .4rem;
}
/* Sections inside the card body keep their inner blocks margin-free
   so the wrapper's bottom margin governs spacing between sections. */
.collab-profile-section .collab-profile-bio,
.collab-profile-section .collab-profile-joke {
    margin-bottom: 0;
}
/* Appreciation section is centered (count chip + button). */
.collab-profile-section-cookies { text-align: center; }
.collab-profile-section-cookies .collab-profile-section-label { text-align: center; }
.collab-profile-section-cookies .profile-cookie-actions {
    margin: 0;
    padding-top: 0;
}
.collab-profile-section-cookies .profile-cookie-actions::before { display: none; }

/* ── Footer anchor ("Member since X · N workspaces shared") ─────── */
.collab-profile-footer {
    display: inline-flex; align-items: center; gap: .35rem;
    flex-wrap: wrap; justify-content: center;
    margin: .5rem 0 0;
    font-size: .72rem;
    color: var(--text-subtle);
}
.collab-profile-footer-sep { opacity: .7; }
/* Bio rendered as a soft quote-card. The left-edge accent line
   reads as a quotation without needing literal " " glyphs. */
.collab-profile-bio {
    text-align: left;
    background: var(--surface-2);
    border-left: 3px solid color-mix(in srgb, var(--primary) 55%, var(--border));
    border-radius: 8px;
    padding: .65rem .85rem;
    margin: 0 0 1.1rem;
    font-size: .82rem; line-height: 1.45;
    color: var(--text);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
/* Favorite joke card - distinct from bio. Warmer accent (amber/yellow)
   so the eye reads it as "personality" rather than "professional bio".
   The little tag at the top earns its emoji by being one of the only
   visual cues on the card. */
.collab-profile-joke {
    text-align: left;
    background: linear-gradient(135deg,
        color-mix(in srgb, #f59e0b 8%, var(--surface-2)),
        var(--surface-2));
    border-left: 3px solid color-mix(in srgb, #f59e0b 70%, var(--border));
    border-radius: 8px;
    padding: .65rem .85rem;
    margin: 0 0 1.1rem;
    font-size: .82rem; line-height: 1.45;
    color: var(--text);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    display: flex; flex-direction: column; gap: .25rem;
}
.collab-profile-joke-tag {
    font-size: .68rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase;
    color: color-mix(in srgb, #b45309 80%, var(--text));
}
.collab-profile-joke-text { font-style: italic; color: var(--text); }
/* Link chips: LinkedIn / Website. Styled like rounded outline
   buttons so they read as actionable, not just static labels.
   The LinkedIn variant carries brand colors (#0A66C2 fill) so the
   user reads it at a glance; the Website variant stays neutral so
   it doesn't compete. */
.collab-profile-links {
    display: flex; gap: .55rem; flex-wrap: wrap; justify-content: center;
    margin: 0 0 1.1rem;
}
.collab-profile-link {
    display: inline-flex; align-items: center; gap: .45rem;
    padding: .4rem .85rem;
    border-radius: 999px;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text);
    font-size: .82rem; font-weight: 600;
    text-decoration: none;
    transition: border-color .12s ease, background .12s ease, color .12s ease, transform .08s ease, box-shadow .12s ease;
    max-width: 100%;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.collab-profile-link:hover {
    transform: translateY(-1px);
    box-shadow: 0 4px 10px -3px rgba(15, 23, 42, .12);
}
.collab-profile-link-icon { flex: 0 0 auto; }

/* LinkedIn variant: branded blue glyph at rest; on hover the chip
   fills with the brand color so the icon flips white for contrast. */
.collab-profile-link.is-linkedin .collab-profile-link-icon { color: #0A66C2; }
.collab-profile-link.is-linkedin:hover {
    background: #0A66C2;
    border-color: #0A66C2;
    color: #fff;
}
.collab-profile-link.is-linkedin:hover .collab-profile-link-icon { color: #fff; }
html[data-theme="dark"] .collab-profile-link.is-linkedin .collab-profile-link-icon { color: #4096e6; }
html[data-theme="dark"] .collab-profile-link.is-linkedin:hover {
    background: #0A66C2;
    border-color: #0A66C2;
}

/* Website variant: neutral globe, lifts to the app primary on hover
   to differentiate it from the LinkedIn-branded chip. */
.collab-profile-link.is-website .collab-profile-link-icon { color: var(--text-muted); }
.collab-profile-link.is-website:hover {
    background: var(--primary-bg);
    border-color: color-mix(in srgb, var(--primary) 40%, var(--border));
    color: var(--primary);
}
.collab-profile-link.is-website:hover .collab-profile-link-icon { color: var(--primary); }
.collab-profile-meta {
    display: grid; grid-template-columns: 1fr 1fr; gap: .55rem;
    text-align: left;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: .85rem 1rem;
}
.collab-profile-stat {
    display: flex; flex-direction: column; gap: 2px;
    min-width: 0;
}
.collab-profile-stat-label {
    font-size: .65rem; font-weight: 700; letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--text-subtle);
}
.collab-profile-stat-value {
    font-size: .92rem; color: var(--text); font-weight: 600;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.collab-profile-stat-value.is-owner { color: var(--primary); }

/* Custom-emoji row: the input + "Use this" submit sit on the same line
   so a paste-then-go flow is one tap. */
.icon-custom-row { display: flex; gap: .5rem; align-items: flex-end; }
.icon-custom-row > div { flex: 1 1 auto; }
.icon-custom-row .btn { flex: 0 0 auto; }

/* Upload-your-own row inside the IconPicker. Now lives inside the
   extras row (right column) with no separator of its own - the
   extras row provides the border-top above. The crop canvas + zoom
   slider only render when the user has actually picked a file; in
   the idle state we just show the "Choose image" button + hint. */
.icon-upload-row {
    display: flex; flex-direction: column; gap: .5rem;
    margin: 0;
}
.icon-upload-pick { display: flex; align-items: center; gap: .65rem; flex-wrap: wrap; }
.icon-upload-pick-btn { cursor: pointer; }
.icon-upload-crop {
    position: relative;
    width: 256px; height: 256px;
    margin: .25rem auto;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 12px;
    overflow: hidden;
    cursor: grab;
    touch-action: none;
}
.icon-upload-crop:active { cursor: grabbing; }
.icon-upload-crop canvas { display: block; width: 100%; height: 100%; user-select: none; }
.icon-upload-mask {
    position: absolute; inset: 0;
    pointer-events: none;
    box-shadow: 0 0 0 9999px rgba(15, 23, 42, .25) inset;
    border-radius: 50%;
    border: 2px dashed rgba(255,255,255,.85);
}
.icon-upload-zoom {
    display: flex; align-items: center; gap: .55rem;
    max-width: 320px; margin: 0 auto;
    width: 100%;
}
.icon-upload-zoom-label { font-size: .78rem; color: var(--text-muted); flex: 0 0 auto; }
.icon-upload-zoom input[type="range"] { flex: 1 1 auto; }

/* ================== Teams page ==================
   Premium card-list layout. Each team is a self-contained card with
   a tinted header (icon + name + count + actions), an inline list of
   members (avatar + name/email + role + remove on hover), and a
   styled "add a teammate" input at the bottom that uses the shared
   chip-suggest popover visuals.
   ================================================ */
/* Top padding matches .app-page-inner's default 2rem so the page
   title isn't glued to the nav bar. The previous override set
   padding-top to 0 which made the heading collide with the topbar
   on every entry to /Teams. */
.teams-page { max-width: 880px; margin: 0 auto; padding: 2rem 1.5rem 4rem; }

/* Page header (used on Teams; reusable for any "list page" that wants
   a stronger affordance than the bare h1 + subtitle pair). The icon
   tile gives the title a visual anchor; the count pill earns its
   place by communicating density at a glance ("how many teams?"). */
.page-head {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 1rem;
    padding: .25rem 0 1.25rem;
    margin-bottom: 1.25rem;
    border-bottom: 1px solid var(--border);
}
.page-head-icon {
    flex: 0 0 auto;
    width: 48px; height: 48px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 12px;
    background: linear-gradient(135deg,
        color-mix(in srgb, var(--primary) 18%, var(--surface)),
        color-mix(in srgb, var(--primary) 6%, var(--surface-2)));
    border: 1px solid color-mix(in srgb, var(--primary) 22%, var(--border));
    color: var(--primary);
    box-shadow: 0 6px 16px color-mix(in srgb, var(--primary) 15%, transparent);
}
.page-head-text { min-width: 0; }
.page-head-title {
    margin: 0 0 .15rem;
    font-size: 1.85rem; font-weight: 700;
    color: var(--text);
    letter-spacing: -.025em;
    line-height: 1.15;
    display: inline-flex; align-items: center; gap: .55rem;
}
.page-head-count {
    font-size: .78rem; font-weight: 700;
    color: var(--primary);
    background: var(--primary-bg);
    padding: 2px 10px;
    border-radius: 999px;
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
    font-variant-numeric: tabular-nums;
    letter-spacing: 0;
    line-height: 1.4;
    vertical-align: middle;
}
.page-head-sub {
    margin: 0;
    font-size: .9rem;
    color: var(--text-muted);
    line-height: 1.5;
    max-width: 60ch;
}
.page-head-actions { display: flex; gap: .5rem; align-items: center; }
@media (max-width: 640px) {
    .page-head { grid-template-columns: auto 1fr; }
    .page-head-actions { grid-column: 1 / -1; justify-content: flex-end; }
}
.teams-list { display: flex; flex-direction: column; gap: 1.25rem; }

.team-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: var(--shadow-sm);
    /* `overflow: visible` so the per-team chip-invite-picker's suggest
       popup can extend past the card's bottom edge without being
       clipped. The previous `overflow: hidden` (used to crop the
       header gradient to the rounded corners) cut off the dropdown
       just below the input - the user's "visibility problem in
       Teams". The card's children already use plain surface
       backgrounds, so nothing visually relies on the overflow clip. */
    overflow: visible;
    transition: border-color .18s ease, box-shadow .18s ease, transform .18s ease;
}
.team-card:hover {
    border-color: color-mix(in srgb, var(--primary) 25%, var(--border));
    box-shadow: 0 12px 28px rgba(15, 23, 42, .07);
    transform: translateY(-1px);
}
/* While the per-card chip-suggest is open the card lifts above its
   siblings so the dropdown isn't covered by the next team-card below.
   focus-within fires while any input/menu inside the card has focus. */
.team-card { position: relative; z-index: 0; }
.team-card:focus-within { z-index: 10; }

/* Team card header reuses the workspace-card pattern: same gradient
   stripe, same icon affordance, same identity-text typography. The
   .team-card-head wrapper just specializes padding/background; every
   inner class (.account-icon-btn, .ws-glyph, .account-identity,
   .account-email, .account-sub) is the SAME rule that powers the
   workspace card. One source of truth for "entity card title bar". */
/* Team card header matches the Workspaces card pattern: flat surface
   background, same padding rhythm, border-bottom-only (border lives
   on the outer .team-card). One source of truth across the two
   surfaces - any visual change to the header lands consistently on
   both. */
.team-card-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem;
    padding: 1rem 1.35rem .85rem;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}
/* Team name styling identical to workspace card title (.account-email
   is shared - on workspaces it holds the address, on teams it holds
   the team name). Both render as the strong heading. */
.team-card-head .account-email {
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: -.01em;
    color: var(--text);
    /* Plain text in a div defaults to the I-beam (text-edit) cursor.
       The team name isn't directly editable here - renaming goes
       through the dedicated Rename button - so the I-beam is
       misleading. Use the arrow cursor so the row reads as a static
       label, not "click to type". */
    cursor: default;
}
/* When the team head's identity-text wraps two lines (name + sub), the
   icon visually dominates because flex-center stacks both lines around
   one icon. Pull the line-height tighter so the text "block" matches
   the icon height more closely - reads as balanced. */
.team-card-head .account-identity-text { line-height: 1.25; gap: 1px; }
/* Member-count pill sitting next to the team name. Uses a real
   `border` (not box-shadow) for its outline because the parent
   .account-email has overflow:hidden for title ellipsis - a
   box-shadow outline gets clipped at the title boundary, leaving
   only half the pill rendered (the user reported this). The parent
   is inline-flex with its own gap so we don't add a margin-left
   here. flex:0 0 auto keeps the pill at its natural width when the
   team name is long. */
.team-card-count {
    font-size: .72rem; font-weight: 700; color: var(--primary);
    background: var(--surface);
    padding: 1px 7px; border-radius: 999px;
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    line-height: 1.4;
}
/* Sub-line under the team name. Same .account-sub baseline so the
   workspace's "Up to date . 2m ago" and the team's "2 members" share
   typography. */
.team-card-sub {
    text-transform: none !important;
    letter-spacing: 0 !important;
    font-weight: 500 !important;
    font-size: .78rem !important;
    color: var(--text-muted) !important;
}
.team-card-actions { display: inline-flex; gap: .25rem; flex: 0 0 auto; }
.team-card-action {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .3rem .55rem; border-radius: 6px;
    background: transparent; border: 1px solid transparent;
    color: var(--text-muted); font-size: .78rem; font-weight: 500;
    cursor: pointer;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.team-card-action:hover {
    background: var(--surface);
    border-color: var(--border);
    color: var(--text);
}
.team-card-action.is-danger:hover { color: var(--danger); border-color: var(--danger); }

/* Body padding mirrors the Workspaces card body. A single full-width
   rule separates the header from the member list; rows themselves
   are dividerless (Notion-style) so the card reads as one block of
   content rather than a striped table. */
.team-card-body {
    padding: .85rem 1.35rem 1.1rem;
    border-top: 1px solid var(--border);
}
.team-members {
    list-style: none; padding: 0; margin: 0;
    display: flex; flex-direction: column;
    gap: 2px;
}
/* Member row: dividerless. The 4px row gap gives enough breathing
   room without needing borders. Padding-right leaves room for the
   role badge / remove-X. Hover background turns the row into a
   subtle highlight rather than ringing it with lines. */
.team-member {
    display: flex; align-items: center; gap: .85rem;
    padding: .45rem .5rem;
    margin: 0 -.5rem;
    border-radius: 8px;
    transition: background .12s ease;
    min-height: 44px;
}
.team-member:not(.is-owner):hover { background: var(--surface-2); cursor: default; }
.team-member.is-owner { cursor: default; }

/* Team member avatar shares its rule with the workspace collaborator
   pip - same gradient, same shape, same shadow ring. Two surfaces, one
   visual identity for "this is a teammate's avatar". The workspace
   .ws-collab-pip-img is 34px (overlapping pile); the team
   .team-member-avatar is 36px (per-row, no overlap), but everything
   else is identical via the shared rule below. */
.team-member-avatar,
.ws-collab-pip-img {
    border-radius: 50%;
    background: var(--avatar-bg);
    color: var(--avatar-color);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 600;
    flex: 0 0 auto;
    box-shadow: 0 0 0 2px var(--surface);
    overflow: hidden;
}
.team-member-avatar { width: 36px; height: 36px; font-size: .85rem; }
.ws-collab-pip-img { width: 34px; height: 34px; font-size: .82rem; }
img.team-member-avatar,
img.ws-collab-pip-img { object-fit: cover; }

.team-member-text {
    display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto;
    line-height: 1.3;
}
.team-member-name {
    font-size: .92rem; color: var(--text); font-weight: 600;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.team-member-self-tag {
    margin-left: .35rem;
    font-size: .62rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--primary); background: var(--primary-bg);
    padding: 1px 6px; border-radius: 999px;
    vertical-align: middle;
}
.team-member-sub {
    font-size: .76rem; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.team-member-role {
    flex: 0 0 auto;
    font-size: .65rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-subtle);
    padding: 2px 8px; border-radius: 999px;
    background: var(--surface-2);
    border: 1px solid var(--border);
}
.team-member.is-owner .team-member-role {
    color: var(--primary);
    background: var(--primary-bg);
    border-color: color-mix(in srgb, var(--primary) 25%, var(--border));
}
/* Remove-action design: the role badge ("MEMBER" / "OWNER") and the
   remove-X share the same right-edge slot, the X crossfading in on
   hover. They're rendered as inline siblings (not absolutely positioned
   stacked) - that earlier approach left a hairline gap when the badge
   faded but the X hadn't sized to its anchor yet. Now they swap in
   place with a fixed minimum slot width so neither pops the layout. */
.team-member-actions {
    flex: 0 0 auto;
    position: relative;
    min-width: 84px;
    display: flex; align-items: center; justify-content: flex-end;
}
.team-member-role {
    transition: opacity .12s ease;
}
.team-member:not(.is-owner):hover .team-member-role,
.team-member:not(.is-owner):focus-within .team-member-role {
    opacity: 0;
}
.team-member-remove-form {
    margin: 0;
    position: absolute; top: 50%; right: 0;
    transform: translateY(-50%);
    opacity: 0; pointer-events: none;
    transition: opacity .12s ease;
}
.team-member:hover .team-member-remove-form,
.team-member:focus-within .team-member-remove-form {
    opacity: 1; pointer-events: auto;
}
/* Pill-shaped remove button matching the role badge's rhythm. Reads as
   "Remove" rather than a bare X - the icon + label tell the user what
   the button DOES without needing a tooltip. Danger styling on idle is
   subtle (muted text on neutral bg) and saturates on hover. */
.team-member-remove {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: 3px 10px 3px 7px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text-muted);
    font-size: .68rem; font-weight: 600; letter-spacing: .04em;
    text-transform: uppercase;
    cursor: pointer;
    transition: color .12s ease, background .12s ease, border-color .12s ease, transform .12s ease;
}
.team-member-remove svg { width: 12px; height: 12px; }
.team-member-remove::after { content: "Remove"; }
.team-member-remove:hover {
    color: var(--danger);
    background: var(--danger-bg);
    border-color: color-mix(in srgb, var(--danger) 35%, var(--border));
    transform: translateX(-1px);
}

/* Add-member input - styled to LOOK like an inviting action even before
   the user clicks. The plain-form-control look read as static text; this
   version has a + icon inside a dashed circle, a subtle dashed wrapper,
   and a saturated focus state so the picker feels like a real call-to-
   action. The popover above is the existing .chip-suggest. */
.team-add-form { margin: 0; }
.team-add-input.chip-input {
    display: flex; align-items: center; gap: .55rem;
    padding: .45rem .7rem;
    background: var(--surface-2);
    border: 1px dashed var(--border-strong);
    border-radius: 8px;
    transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
    min-height: 48px;
    position: relative;
}
.team-add-input.chip-input:focus-within,
.team-add-input.chip-input.is-focused {
    background: var(--surface);
    border-style: solid;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-bg);
}
.team-add-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; border-radius: 50%;
    background: var(--surface);
    border: 1px dashed var(--border-strong);
    color: var(--text-subtle);
    flex: 0 0 auto;
    transition: background .12s ease, color .12s ease, border-color .12s ease, border-style .12s ease;
}
.team-add-input.chip-input:focus-within .team-add-icon,
.team-add-input.chip-input.is-focused .team-add-icon {
    background: var(--primary); color: #fff;
    border-color: var(--primary); border-style: solid;
}
.team-add-needle.chip-input-entry {
    flex: 1 1 auto;
    background: transparent; border: 0; outline: 0; padding: 0;
    font-size: .92rem; color: var(--text);
}
.team-add-needle.chip-input-entry::placeholder { color: var(--text-subtle); }
.team-add-suggest {
    position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 30;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 10px; box-shadow: 0 12px 32px rgba(15,23,42,.16);
    max-height: 280px; overflow-y: auto;
    display: flex; flex-direction: column; padding: 4px;
}
/* Portaled variant: appended to <body> so .team-card's overflow:hidden
   stops clipping the popup. Coordinates set from JS at open time. */
.team-add-suggest.is-portal {
    position: absolute;
    top: 0; left: 0; right: auto;
    z-index: 1080;
    width: auto;
}
/* Empty-state row inside the picker - distinct from "popup not open"
   and from a populated list. Reads as helpful copy, not a row. */
.chip-suggest-empty {
    padding: .9rem 1rem;
    font-size: .82rem;
    color: var(--text-muted);
    text-align: center;
    line-height: 1.4;
}

/* ================== Collaborator strip on workspace card ==================
   Single-line pile + names + Manage. The previous fat chip grid took
   ~120px of vertical space for what is essentially a "who's in here"
   readout - replaced with an overlapping avatar pile (Notion-style) +
   inline names + per-avatar role tooltips. Stays one row even on a
   workspace with eight collaborators (sixth+ collapse into "+N"). */
.ws-collab-strip {
    margin-top: .75rem;
    padding: .7rem .9rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 10px;
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem;
    min-height: 56px;
}
.ws-collab-left {
    display: flex; align-items: center; gap: 1rem;
    min-width: 0; flex: 1 1 auto;
}
.ws-collab-pile { display: inline-flex; flex: 0 0 auto; }

.ws-collab-pip {
    /* Pip is a <button> so users can click to open the profile modal -
       reset the user-agent button chrome so it looks like a circle, not
       a button. Padding/border/font-family unset; only the visual we
       define below is kept. */
    width: 34px; height: 34px; border-radius: 50%;
    padding: 0; border: 0; font-family: inherit;
    display: inline-flex; align-items: center; justify-content: center;
    box-shadow: 0 0 0 2px var(--surface-2);
    background: var(--avatar-bg);
    color: var(--avatar-color);
    font-size: .82rem; font-weight: 600;
    overflow: hidden;
    cursor: pointer;
    transition: transform .12s ease, z-index 0s .12s;
    position: relative;
}
.ws-collab-pip:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--surface-2), 0 0 0 4px var(--primary);
}
.ws-collab-pip + .ws-collab-pip { margin-left: -10px; }
.ws-collab-pip:hover { transform: translateY(-2px) scale(1.06); z-index: 2; transition: transform .12s ease, z-index 0s; }
.ws-collab-pip.is-owner { box-shadow: 0 0 0 2px var(--primary); }
/* Admin gets a softer ring than Owner so the role hierarchy reads visually
   left-to-right: solid primary (Owner) > muted indigo (Admin) > unringed
   (Viewer). Both viewers and owners need to be able to identify each
   role at a glance, regardless of which one they hold themselves. */
.ws-collab-pip.is-admin { box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 55%, var(--surface-2)); }
/* If a user is somehow tagged both (shouldn't happen at the data layer
   but defensively): Owner wins, since the Owner ring is more prominent. */
.ws-collab-pip.is-owner.is-admin { box-shadow: 0 0 0 2px var(--primary); }
/* .ws-collab-pip-img is defined in the team-member-avatar shared block
   higher up so the two surfaces never drift in styling. Don't redefine
   the visuals here. */
.ws-collab-pip-more {
    background: var(--surface);
    color: var(--text-subtle);
    border: 1px dashed var(--border-strong);
    font-size: .72rem;
    box-shadow: 0 0 0 2px var(--surface-2);
}

/* ----- Workspace card "members" panel -----
   Three role rows stacked vertically: OWNER (bigger avatar, named)
   then ADMINS (named, comma-separated) then USERS (avatar pile +
   count, the full named list lives on /Collaborators). Manage
   button anchors top-right via position:absolute so it never
   shoves the OWNER label off-axis. */
.ws-members {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: .75rem .85rem;
    /* Two-column grid: fixed-width LABEL column + flexible CONTENT
       column. Every row inherits this grid so avatars across all
       rows land at the EXACT same X position (vertically aligned),
       regardless of which group they belong to. */
    display: grid;
    grid-template-columns: 80px 1fr;
    row-gap: .55rem;
    column-gap: .65rem;
    align-items: center;
}
.ws-members-line {
    /* Each line is a sub-grid contributing to the parent's two
       columns. Using display:contents passes the line's children
       directly through to the parent grid, which is what
       vertically aligns avatars across heterogeneous rows. */
    display: contents;
}
.ws-members-rolelabel {
    /* Lives in column 1 of the parent grid - fixed width, every
       row's label sits in the same vertical strip. */
    grid-column: 1;
    align-self: center;
    font-weight: 700;
    font-size: .7rem;
    letter-spacing: .06em;
    color: var(--text-muted);
}
.ws-members-rolelabel.is-owner { color: var(--primary); }
.ws-members-rolelabel.is-admin { color: color-mix(in srgb, var(--primary) 70%, var(--text-muted)); }
.ws-members-people {
    /* Lives in column 2. Avatars start at this column's edge so
       OWNER, ADMIN, and USER rows all share the same X start
       position - the panel reads as a clean two-column grid. */
    grid-column: 2;
    display: inline-flex; align-items: center; flex-wrap: wrap;
    gap: .25rem;
    /* Reserve right padding so the absolute-positioned Manage
       button doesn't overlap the trailing content. */
    padding-right: 90px;
}
.ws-members-pile {
    display: inline-flex; align-items: center;
}
.ws-members-pip {
    /* Pure circle button. No background pill, no name label - the
       previous design dragged a sideways grey rectangle on hover
       which read as a stretched rendering glitch. Identity comes
       from the avatar itself, name from the tooltip on hover, role
       from the column-1 label. */
    background: transparent;
    border: 0;
    padding: 0;
    border-radius: 50%;
    cursor: pointer;
    transition: transform .12s ease, z-index 0s, box-shadow .12s ease;
    display: inline-flex; align-items: center; justify-content: center;
}
.ws-members-pip + .ws-members-pip { margin-left: -10px; }
.ws-members-pip:hover {
    transform: translateY(-1px) scale(1.08);
    z-index: 2;
    box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--primary);
    border-radius: 50%;
}
.ws-members-avatar {
    /* Single uniform size across every role row so the panel
       reads as a clean grid. Visual hierarchy comes from the
       role-label color, not from differently-sized avatars.
       Uses the shared --avatar-bg / --avatar-color tokens so the
       letter pile in the workspace card matches every other
       letter-bubble in the app (share modal, profile card,
       chip pickers, …). Earlier this rule used surface-2 + muted
       text which made the bubbles read as disabled / placeholder. */
    width: 32px; height: 32px;
    border-radius: 50%;
    object-fit: cover;
    flex: 0 0 auto;
    background: var(--avatar-bg);
    color: var(--avatar-color);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .85rem; font-weight: 600;
}
.ws-members-pip-more {
    /* Matches .ws-members-avatar size so the pile reads as a
       single grid of identically-sized circles, the +N bubble
       sitting flush with the others. */
    width: 32px; height: 32px;
    border-radius: 50%;
    background: var(--surface);
    color: var(--text-subtle);
    border: 1px dashed var(--border-strong);
    font-size: .72rem; font-weight: 600;
    box-shadow: 0 0 0 2px var(--surface-2);
    margin-left: -10px;
    display: inline-flex; align-items: center; justify-content: center;
}
.ws-members-count {
    font-size: .8rem;
    color: var(--text-muted);
    margin-left: .25rem;
}
.ws-members-manage {
    position: absolute;
    top: .55rem; right: .65rem;
}
.ws-collab-meta {
    display: flex; flex-direction: column; min-width: 0;
    line-height: 1.35;
    gap: 1px;
}
/* Count is the LEAD now (avatars convey identity). Bigger / bolder so
   the user reads "3 collaborators" first; role breakdown sits below as
   supporting metadata. Listing names was redundant with the pile and
   noisy past 2-3 people. */
.ws-collab-count {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    letter-spacing: -.005em;
}
.ws-collab-summary {
    font-size: .78rem;
    color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    display: inline-flex; align-items: center; gap: .35rem; flex-wrap: wrap;
}
.ws-collab-summary > text { /* @text Razor wrapper is a fragment - inline content */ }
.ws-collab-hidden { color: var(--warning); font-weight: 600; }
.ws-collab-manage {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; gap: .3rem;
    color: var(--primary); font-weight: 600;
    padding: .35rem .65rem;
}
.ws-collab-manage:hover { background: var(--primary-bg); }

/* Legacy fat-card classes (kept temporarily so older renderings don't
   break before the page is reloaded). They render off-screen size 0
   if any stray markup is still around - effectively dead. */
.account-collab-section {
    margin-top: .75rem;
    padding: .85rem 1rem .9rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
}
.account-collab-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: .75rem;
    margin-bottom: .65rem;
}
.account-collab-title {
    display: flex; align-items: baseline; gap: .55rem;
    flex: 1 1 auto; min-width: 0;
}
.account-collab-icon { color: var(--text-muted); flex: 0 0 auto; transform: translateY(2px); }
.account-collab-heading {
    font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    color: var(--text-subtle);
    flex: 0 0 auto;
}
.account-collab-summary {
    font-size: .82rem; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    min-width: 0;
}
.account-collab-summary-sub { color: var(--text-muted); font-size: .78rem; margin-left: .15rem; }
.account-collab-manage {
    display: inline-flex; align-items: center; gap: .25rem;
    flex: 0 0 auto;
    color: var(--primary); font-weight: 600;
}
.account-collab-manage:hover { color: var(--primary-hover); background: var(--primary-bg); }

/* Grid of people chips. Auto-fills so a 1-person workspace renders
   compact and a 6-person workspace fills the row evenly. min(220px)
   keeps the chip wide enough for "Loïc Carrère · Owner" without the
   role tag wrapping awkwardly. */
.account-collab-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: .4rem;
}
.collab-chip {
    display: flex; align-items: center; gap: .6rem;
    padding: .45rem .55rem;
    background: var(--surface-2);
    border: 1px solid transparent;
    border-radius: 8px;
    transition: border-color .12s ease, background .12s ease, transform .08s ease;
    min-width: 0;
}
.collab-chip:hover { border-color: var(--border-strong); background: var(--surface); transform: translateY(-1px); }
.collab-chip.is-owner {
    background: linear-gradient(135deg, var(--primary-bg), color-mix(in srgb, var(--primary-bg) 60%, var(--surface)));
    border-color: color-mix(in srgb, var(--primary) 25%, var(--border));
}
.collab-chip-avatar {
    width: 32px; height: 32px; border-radius: 50%;
    background: var(--avatar-bg);
    color: var(--avatar-color);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 600; font-size: .78rem;
    flex: 0 0 auto;
    box-shadow: 0 0 0 2px var(--surface);
}
.collab-chip.is-owner .collab-chip-avatar { box-shadow: 0 0 0 2px var(--primary); }
img.collab-chip-avatar { object-fit: cover; }
.collab-chip-meta {
    display: flex; flex-direction: column; min-width: 0;
    line-height: 1.2;
}
.collab-chip-name {
    font-size: .85rem; font-weight: 600; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.collab-chip-role {
    font-size: .65rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-subtle);
}
.collab-chip.is-owner .collab-chip-role { color: var(--primary); }

/* Overflow chip: visually consistent with the others but neutral.
   The "+N" sits in the avatar slot; the meta column reads "N more
   collaborators" so a glance picks up the count and the meaning. */
.collab-chip.is-overflow {
    background: var(--surface);
    border: 1px dashed var(--border-strong);
}
.collab-chip-avatar-more {
    background: var(--surface); color: var(--text-subtle);
    border: 1px dashed var(--border-strong);
    box-shadow: none;
    font-variant-numeric: tabular-nums;
}

/* Hidden-from-viewer footer. Subtle, italic-feeling row that explains
   why the count above doesn't match the workspace's actual member
   count. Uses an eye-with-strike icon so the meaning is unambiguous. */
.account-collab-foot {
    margin-top: .6rem;
    padding-top: .55rem;
    border-top: 1px dashed var(--border);
    display: flex; align-items: center; gap: .4rem;
    font-size: .72rem; color: var(--text-subtle);
}
.account-collab-foot svg { flex: 0 0 auto; }
.quick-open-avatar-ghost {
    background: transparent; color: var(--text-subtle);
    border: 1px dashed var(--border-strong); font-weight: 400;
}
.quick-open-add:hover .quick-open-avatar-ghost { border-color: var(--primary); color: var(--primary); }
.quick-open-body { flex: 1 1 auto; min-width: 0; }
.quick-open-email { font-weight: 500; color: var(--text); font-size: .9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.quick-open-meta { font-size: .75rem; color: var(--text-subtle); margin-top: .1rem; display: flex; align-items: center; gap: .35rem; flex-wrap: wrap; }
.quick-open-meta .qo-dot { color: var(--border-strong); }
.quick-open-meta .qo-viewer-tag {
    color: var(--primary);
    background: var(--primary-bg);
    border-radius: 4px;
    padding: 0 .35rem;
    font-weight: 600;
    font-size: .68rem;
    letter-spacing: .02em;
    text-transform: uppercase;
}
.quick-open-arrow { color: var(--text-subtle); flex: 0 0 auto; transition: transform .12s ease, color .12s ease; }

/* Two-column dashboard layout: workspaces on the left, activity on the right.
   Collapses to one column under 880px so the cards keep their breathing room. */
.home-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.25rem;
    align-items: start;
    margin-top: .25rem;
}
@media (max-width: 880px) {
    .home-grid { grid-template-columns: 1fr; }
}
.home-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 1rem 1.1rem 1.1rem;
}
.home-card-head {
    display: flex; align-items: baseline; justify-content: space-between;
    margin: 0 0 .8rem;
}
.home-card-head h2 {
    font-size: .9rem; font-weight: 600; color: var(--text);
    margin: 0; letter-spacing: -.01em;
}
.home-card-all {
    font-size: .78rem; color: var(--text-subtle); text-decoration: none;
    transition: color .12s ease;
}
.home-card-all:hover { color: var(--primary); }
.home-card-workspaces .quick-open-card { padding: .6rem .7rem; }

/* Activity feed inside the home dashboard. Reuses the timestamp / avatar
   conventions from the dedicated /Activity page but rendered tighter. */
.home-activity { list-style: none; padding: 0; margin: 0; }
.home-activity-item + .home-activity-item { border-top: 1px solid var(--border); }
.home-activity-row {
    display: flex; gap: .7rem; padding: .65rem .55rem;
    margin: 0 -.55rem;
    text-decoration: none; color: inherit;
    border-radius: 8px;
    transition: background-color .12s ease;
}
.home-activity-row:hover { background: var(--surface-2); }
.home-activity-row:hover .home-activity-line strong { color: var(--primary); }
.home-activity-avatar {
    width: 28px; height: 28px; border-radius: 50%;
    background: var(--primary-bg); color: var(--primary);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 600; font-size: .75rem; flex: 0 0 auto;
}
img.home-activity-avatar { object-fit: cover; }
.home-activity-body { flex: 1 1 auto; min-width: 0; }
.home-activity-line {
    font-size: .85rem; color: var(--text); line-height: 1.35;
    overflow: hidden; text-overflow: ellipsis;
}
.home-activity-line strong { font-weight: 600; transition: color .12s ease; }
/* Resting-state contrast: keep the verb at full --text so the row reads
   as a live link, not as faded metadata. The "where" stays one notch
   muted to mark it as secondary context (the workspace label), but we
   use --text-muted (not --text-subtle) so it doesn't look disabled. */
.home-activity-verb { color: var(--text); margin: 0 .25rem; }
.home-activity-where { color: var(--text-muted); }
.home-activity-quote {
    font-size: .82rem; color: var(--text);
    background: var(--surface-2);
    border-left: 2px solid var(--border-strong);
    padding: .35rem .6rem;
    border-radius: 4px;
    margin: .35rem 0 .25rem;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    /* Required for line-clamp to actually trigger - without an explicit
       max-height and word-break, a long single token would still overflow
       horizontally and the clamp wouldn't engage. */
    word-break: break-word;
    max-height: 2.6em;
    line-height: 1.3;
}
.home-activity-time { font-size: .7rem; color: var(--text-muted); }
.home-activity-empty { padding: 1rem .25rem; }

/* ── Home dashboard "For you" notification card ─────────────────────
   Surfaces the unread set the bell badge counts (assignments to me,
   cookies received) so the user always knows what triggered the dot
   AND can act on each item without hunting. "Mark all as read" clears
   the bell counter; clicking an assignment row deep-links to the
   thread which auto-clears it via SeenByAssigneeUtc on the message
   page. */
.home-notif-card {
    margin: 0 0 1.25rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
    transition: opacity .2s ease, transform .2s ease;
}
.home-notif-card.is-clearing { opacity: 0; transform: translateY(-4px); }
.home-notif-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: .65rem .9rem;
    background: linear-gradient(135deg, color-mix(in srgb, var(--primary) 8%, var(--surface-2)), var(--surface-2));
    border-bottom: 1px solid var(--border);
}
.home-notif-title {
    display: inline-flex; align-items: center; gap: .45rem;
    font-size: .82rem;
    font-weight: 700;
    color: var(--text);
    letter-spacing: -.005em;
}
.home-notif-title svg { color: var(--primary); }
.home-notif-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 20px; height: 20px;
    padding: 0 .45rem;
    background: var(--primary);
    color: #fff;
    border-radius: 999px;
    font-size: .68rem;
    font-weight: 700;
    margin-left: .15rem;
}
.home-notif-clear {
    background: transparent;
    border: 0;
    color: var(--text-muted);
    font-size: .76rem;
    font-weight: 600;
    cursor: pointer;
    padding: .25rem .55rem;
    border-radius: 6px;
    transition: background .12s ease, color .12s ease;
}
.home-notif-clear:hover { background: var(--surface); color: var(--primary); }
.home-notif-clear:disabled { opacity: .55; cursor: default; }
.home-notif-body {
    display: flex;
    flex-direction: column;
}
.home-notif-item {
    display: flex; align-items: center; gap: .65rem;
    padding: .65rem .9rem;
    border-top: 1px solid var(--surface-2);
    text-decoration: none;
    color: inherit;
    transition: background .1s ease;
}
.home-notif-item:first-child { border-top: 0; }
.home-notif-item:hover { background: var(--surface-2); }
.home-notif-icon {
    width: 28px; height: 28px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 8px;
    flex: 0 0 auto;
    font-size: 1rem; line-height: 1;
}
.home-notif-icon-assign {
    background: color-mix(in srgb, var(--primary) 12%, var(--surface-2));
    color: var(--primary);
}
.home-notif-icon-cookie {
    background: linear-gradient(135deg, color-mix(in srgb, #f59e0b 18%, var(--surface)), color-mix(in srgb, #ec4899 14%, var(--surface)));
}
.home-notif-avatar {
    width: 26px; height: 26px;
    border-radius: 50%;
    flex: 0 0 auto;
    background: linear-gradient(135deg, var(--primary), #8b5cf6);
    color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .7rem; font-weight: 700;
}
.home-notif-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.home-notif-line {
    font-size: .87rem;
    color: var(--text);
    line-height: 1.35;
    overflow: hidden; text-overflow: ellipsis;
}
.home-notif-line strong { font-weight: 700; }
.home-notif-line em { font-style: normal; font-weight: 600; color: var(--primary); }
.home-notif-sub {
    font-size: .72rem;
    color: var(--text-muted);
}

/* Unread-mention call-out banner. Slim row with icon, copy, stacked
   avatars, and an arrow. Sits between hero and dashboard - the
   strongest visual signal on the page when someone has pinged you. */
.home-mentions-banner {
    display: flex; align-items: center; gap: .9rem;
    padding: .8rem 1rem;
    margin: 0 0 1.25rem;
    background: linear-gradient(135deg, var(--primary-bg), color-mix(in srgb, var(--primary-bg) 70%, var(--surface)));
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
    border-radius: 12px;
    text-decoration: none; color: inherit;
    transition: transform .12s ease, border-color .12s ease, box-shadow .12s ease, opacity .3s ease;
}
.home-mentions-banner.is-leaving { opacity: 0; pointer-events: none; }
.home-mentions-banner.is-hidden { display: none !important; }
.home-mentions-banner:hover {
    transform: translateY(-1px);
    border-color: var(--primary);
    box-shadow: 0 6px 18px rgba(79, 70, 229, .15);
}
.home-mentions-icon {
    flex: 0 0 auto;
    width: 38px; height: 38px; border-radius: 10px;
    background: var(--primary); color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
}
.home-mentions-body { flex: 1 1 auto; min-width: 0; }
.home-mentions-headline { font-weight: 600; color: var(--text); font-size: .95rem; line-height: 1.25; }
.home-mentions-sub { font-size: .8rem; color: var(--text-muted); margin-top: 2px; }
.home-mentions-avatars {
    display: inline-flex; flex: 0 0 auto;
    /* Right-edge stacking with negative margins for the classic "avatar
       pile" look. The first avatar sits at full opacity; the rest layer
       behind. Cap is 3 avatars (matched in Razor's Take(3)). */
}
.home-mentions-avatar {
    width: 28px; height: 28px; border-radius: 50%;
    background: var(--primary-bg); color: var(--primary);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .72rem; font-weight: 600;
    box-shadow: 0 0 0 2px var(--surface);
}
.home-mentions-avatar + .home-mentions-avatar { margin-left: -10px; }
img.home-mentions-avatar { object-fit: cover; }
.home-mentions-arrow { color: var(--primary); flex: 0 0 auto; transition: transform .12s ease; }
.home-mentions-banner:hover .home-mentions-arrow { transform: translateX(2px); }

/* Footer hint */
.home-footer-hint {
    text-align: center; margin-top: 2.5rem;
    color: var(--text-subtle); font-size: .78rem;
}
.home-footer-hint a { color: var(--text-muted); }
.home-footer-hint a:hover { color: var(--primary); }
.home-footer-hint kbd {
    display: inline-block; padding: 1px 5px; margin: 0 1px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 4px; font-size: .7rem; color: var(--text-muted);
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
}

.section-title { font-size: 1rem; font-weight: 700; color: var(--text); margin: 2rem 0 .85rem; }

/* Activity feed */
.activity-page { max-width: 760px; margin: 0 auto; padding: 1.5rem; }
.page-title { font-size: 1.6rem; font-weight: 700; margin: 0; color: var(--text); letter-spacing: -.02em; }
.page-subtitle { font-size: .85rem; color: var(--text-muted); margin-top: .2rem; }
/* Activity filter chips + pagination. Chip row sits directly under the
   page title and wraps on narrow viewports. "is-active" inverts to the
   primary color so the active filter is visually obvious even in
   muted light themes. */
.activity-filters {
    display: flex; flex-wrap: wrap; gap: .4rem;
    margin-bottom: 1.1rem;
    padding: .25rem 0;
}
.activity-filter-chip {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .35rem .75rem;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text-muted);
    font-size: .82rem; font-weight: 500;
    text-decoration: none;
    transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
.activity-filter-chip:hover {
    border-color: var(--primary);
    color: var(--primary);
}
.activity-filter-chip.is-active {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}

.activity-pagination {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem;
    margin-top: 1.25rem;
    padding: .5rem 0 .25rem;
    border-top: 1px solid var(--border);
    flex-wrap: wrap;
}
.activity-pagination-summary { font-size: .82rem; color: var(--text-muted); }
.activity-pagination-controls { display: flex; gap: .4rem; }
.activity-pagination-controls .is-disabled {
    pointer-events: none;
    opacity: .4;
}

.activity-feed { display: flex; flex-direction: column; gap: .4rem; }
.activity-entry {
    display: flex; gap: .75rem; align-items: flex-start;
    padding: .75rem .85rem;
    background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
    text-decoration: none; color: inherit;
    transition: border-color .12s ease, background .12s ease;
}
.activity-entry:hover { border-color: var(--border-strong); background: var(--surface-2); }

/* Orphaned entries - the linked account was removed, so the entry can't
   navigate anywhere and shouldn't invite a click. Muted, no hover lift. */
.activity-entry.is-orphaned {
    cursor: not-allowed;
    opacity: .72;
    border-style: dashed;
    background: var(--surface);
}
.activity-entry.is-orphaned:hover {
    border-color: var(--border);
    background: var(--surface);
}
.activity-entry.is-orphaned .activity-avatar {
    background: var(--border-strong);
    color: var(--text-muted);
}
.activity-orphan-tag {
    display: inline-block;
    padding: .05rem .45rem;
    font-size: .72rem;
    font-weight: 500;
    color: var(--text-muted);
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    margin-left: .15rem;
    vertical-align: baseline;
}
html[data-theme="dark"] .activity-entry.is-orphaned .activity-avatar {
    background: #334155;
    color: #94a3b8;
}
.activity-avatar {
    width: 30px; height: 30px; border-radius: 50%;
    background: linear-gradient(135deg, var(--primary), #8b5cf6); color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: .85rem; flex: 0 0 auto;
}
.activity-body { flex: 1 1 auto; min-width: 0; }
.activity-line { font-size: .88rem; color: var(--text-muted); }
.activity-line strong { color: var(--text); font-weight: 600; }
.activity-line em { color: var(--text); font-style: normal; font-weight: 500; }
.activity-reaction-chip {
    display: inline-flex; align-items: center;
    margin-left: .35rem;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    font-size: 1rem; line-height: 1.1;
}
/* Mail-sent rows render the subject + "to" recipient on a
   single line in lieu of a body quote. Slightly tighter than
   the regular quote so the row reads like a tiny mail receipt. */
.activity-quote.activity-mailsent {
    display: flex; flex-wrap: wrap; align-items: baseline;
    gap: .5rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: .35rem .65rem;
    font-style: normal;
    color: var(--text);
    quotes: none;
}
.activity-quote.activity-mailsent::before,
.activity-quote.activity-mailsent::after { content: ""; }
.activity-quote.activity-mailsent strong {
    color: var(--text);
    font-weight: 600;
    font-size: .9rem;
}
.activity-mailsent-to {
    color: var(--text-muted);
    font-size: .82rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    min-width: 0;
}
.activity-mailsent-to::before {
    content: "to ";
    color: var(--text-subtle);
    margin-right: .15rem;
}

/* Reply / Reply-all / Forward compose panel. Sits below the
   message body, above the thread-next list, and slides in when
   one of the topbar triggers is clicked. To / Cc / Subject
   stack as labelled rows; body is a freely-resizing textarea. */
.msg-compose {
    margin-top: 1.5rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, .06);
    overflow: hidden;
}
.msg-compose-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: .75rem 1.1rem;
    border-bottom: 1px solid var(--border);
    background: color-mix(in srgb, var(--primary) 6%, var(--surface));
}
.msg-compose-title {
    display: flex; align-items: baseline; gap: .65rem;
    min-width: 0;
}
.msg-compose-mode {
    font-weight: 700;
    color: var(--text);
    font-size: .92rem;
}
.msg-compose-from {
    font-size: .8rem;
    color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.msg-compose-from strong {
    color: var(--text);
    font-weight: 600;
}
.msg-compose-close {
    background: transparent;
    border: 0;
    border-radius: 8px;
    width: 28px; height: 28px;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--text-muted);
    cursor: pointer;
}
.msg-compose-close:hover { background: var(--surface-2); color: var(--text); }
.msg-compose form { padding: .85rem 1.1rem 1rem; }
.msg-compose-row {
    display: grid;
    grid-template-columns: 70px 1fr;
    align-items: center;
    gap: .65rem;
    padding: .35rem 0;
    border-bottom: 1px solid var(--border-soft, var(--border));
}
.msg-compose-row label {
    color: var(--text-muted);
    font-size: .82rem;
    font-weight: 500;
    margin: 0;
}
.msg-compose-row input {
    width: 100%;
    border: 0;
    background: transparent;
    padding: .35rem 0;
    font-size: .92rem;
    color: var(--text);
    outline: none;
}
.msg-compose textarea {
    width: 100%;
    min-height: 180px;
    margin-top: .85rem;
    padding: .85rem;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    font-size: .92rem;
    line-height: 1.55;
    color: var(--text);
    font-family: inherit;
    resize: vertical;
    outline: none;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.msg-compose textarea:focus {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-bg);
}

/* ── Rich-text compose editor (toolbar + contenteditable canvas) ─── */
.compose-editor {
    margin-top: .85rem;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    overflow: hidden;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.compose-editor:focus-within {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-bg);
}
.compose-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 2px;
    padding: .35rem .45rem;
    background: var(--surface-2);
    border-bottom: 1px solid var(--surface-2);
}
.compose-tool {
    width: 28px; height: 28px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent;
    border: 0;
    border-radius: 6px;
    color: var(--text-muted);
    cursor: pointer;
    transition: background .1s ease, color .1s ease;
}
.compose-tool:hover { background: var(--surface); color: var(--text); }
.compose-tool.is-active { background: var(--primary-bg); color: var(--primary); }
.compose-tool:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 1px;
}
.compose-tool-sep {
    width: 1px;
    height: 18px;
    margin: 0 .25rem;
    background: var(--border);
    flex: 0 0 auto;
}
.compose-tool-with-caret {
    width: auto;
    padding: 0 .35rem;
    gap: 2px;
}
.compose-tool-with-caret .compose-tool-caret { opacity: .6; margin-left: -1px; }
.compose-tool-with-caret[aria-expanded="true"] {
    background: var(--primary-bg);
    color: var(--primary);
}

/* Generic compose-toolbar dropdown - host for the font-size menu (and
   any future picker the toolbar grows). The button stays in the
   toolbar's flex flow; the menu pops below via absolute positioning
   and is gated by the [hidden] attribute so JS just toggles a single
   property. */
.compose-tool-dropdown { position: relative; display: inline-flex; }
.compose-tool-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    z-index: 30;
    min-width: 150px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: .25rem;
    box-shadow: 0 12px 28px -8px rgba(15,23,42,.18), 0 2px 6px -1px rgba(15,23,42,.06);
    animation: bulk-fade-in .1s ease;
}
.compose-tool-menu[hidden] { display: none; }
.compose-tool-menu-item {
    display: flex;
    align-items: center;
    gap: .65rem;
    width: 100%;
    padding: .35rem .55rem;
    border: 0;
    background: transparent;
    color: var(--text);
    border-radius: 5px;
    cursor: pointer;
    text-align: left;
    transition: background .08s ease;
    font: inherit;
}
.compose-tool-menu-item:hover { background: var(--primary-bg); }
.compose-tool-menu-item.is-active { background: var(--primary-bg); color: var(--primary); }
.compose-tool-menu-item > span:first-child {
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; flex: 0 0 auto;
    font-family: inherit;
    color: var(--text);
}
.compose-tool-menu-label {
    flex: 1 1 auto;
    font-size: .85rem;
    color: var(--text);
}
.compose-link-panel {
    display: flex;
    align-items: center;
    gap: .35rem;
    padding: .45rem .55rem;
    background: color-mix(in srgb, var(--primary) 6%, var(--surface));
    border-bottom: 1px solid var(--surface-2);
    animation: bulk-fade-in .12s ease;
}
.compose-link-panel[hidden] { display: none; }
.compose-link-panel input[type="url"] {
    flex: 1 1 auto;
    min-width: 0;
    padding: .32rem .55rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text);
    font-size: .85rem;
    outline: 0;
}
.compose-link-panel input[type="url"]:focus {
    border-color: var(--primary);
    box-shadow: 0 0 0 2px var(--primary-bg);
}
.compose-body {
    min-height: 180px;
    max-height: 480px;
    overflow-y: auto;
    padding: .85rem 1rem;
    font-size: .92rem;
    line-height: 1.55;
    color: var(--text);
    outline: 0;
    /* Allow vertical resize via the wrapper's resize hook below; the
       contenteditable itself doesn't take a native resize handle. */
}
.compose-body:empty::before {
    content: attr(data-placeholder);
    color: var(--text-subtle);
    pointer-events: none;
}
.compose-body p:first-child { margin-top: 0; }
.compose-body p:last-child { margin-bottom: 0; }
.compose-body ul, .compose-body ol { padding-left: 1.4rem; margin: .4rem 0; }
.compose-body blockquote {
    margin: .4rem 0;
    padding: .25rem 0 .25rem .85rem;
    border-left: 3px solid var(--border-strong);
    color: var(--text-muted);
}
.compose-body a { color: var(--primary); }
.compose-body a:hover { text-decoration: underline; }

.msg-compose-foot {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem;
    margin-top: .85rem;
}
.msg-compose-hint { flex: 1 1 auto; min-width: 0; }
.msg-compose-foot div { flex: 0 0 auto; display: inline-flex; gap: .35rem; }

/* Recipient field invalid state. JS toggles `.is-invalid` when a
   comma-separated entry doesn't match a basic email shape. */
.msg-compose-row input.is-invalid {
    box-shadow: inset 0 -1px 0 0 #DC2626;
}
.msg-compose-validation {
    margin-top: .5rem;
    padding: .45rem .65rem;
    border-radius: 8px;
    background: rgba(220, 38, 38, .1);
    color: #991B1B;
    font-size: .82rem;
}
html[data-theme="dark"] .msg-compose-validation {
    background: rgba(248, 113, 113, .15);
    color: #FCA5A5;
}

/* Attachments: a file-pick button + a hint line + a chip list of
   the picked files. The native input is hidden; the styled label
   triggers it. */
.msg-compose-attachments {
    margin-top: .65rem;
    display: flex; flex-wrap: wrap;
    align-items: center; gap: .45rem .85rem;
}
.msg-compose-attach-pick {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .3rem .65rem;
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--text-muted);
    font-size: .82rem; font-weight: 500;
    cursor: pointer;
    border: 1px solid var(--border);
    transition: background .12s ease, color .12s ease;
    margin: 0;
}
.msg-compose-attach-pick:hover { background: var(--surface); color: var(--text); }
.msg-compose-attach-pick input[type="file"] { display: none; }
.msg-compose-attach-hint {
    font-size: .78rem;
    color: var(--text-subtle);
}
.msg-compose-attach-list {
    flex-basis: 100%;
    display: flex; flex-wrap: wrap;
    gap: .35rem;
}
.msg-compose-attach-chip {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .25rem .65rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: .78rem;
    max-width: 16rem;
}
.msg-compose-attach-name {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    color: var(--text);
}
.msg-compose-attach-size {
    color: var(--text-subtle);
    font-variant-numeric: tabular-nums;
    flex: 0 0 auto;
}
.msg-compose-attach-chip { padding-right: .35rem; }
.msg-compose-attach-remove {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px;
    padding: 0;
    margin-left: .15rem;
    background: transparent;
    border: 0;
    border-radius: 50%;
    color: var(--text-subtle);
    cursor: pointer !important;
    transition: background .12s ease, color .12s ease;
}
.msg-compose-attach-remove:hover {
    background: rgba(220, 38, 38, .12);
    color: #B91C1C;
}
.msg-compose-attach-remove svg { display: block; }

/* Drop-zone visual when a file is being dragged over the compose
   panel. Pseudo-element overlay rather than a background swap so
   the original form chrome stays visible underneath - the user
   sees "I'm about to drop on THIS panel" without losing track of
   their work-in-progress draft. */
.msg-compose { position: relative; }
.msg-compose::after {
    content: "Drop files to attach";
    position: absolute; inset: 6px;
    display: flex; align-items: center; justify-content: center;
    border: 2px dashed var(--primary);
    border-radius: 12px;
    background: color-mix(in srgb, var(--primary) 8%, var(--surface));
    color: var(--primary);
    font-weight: 700;
    font-size: 1rem;
    pointer-events: none;
    opacity: 0;
    transition: opacity .15s ease;
    z-index: 5;
}
.msg-compose.is-drag-over::after { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
    .msg-compose::after { transition: none; }
}
.activity-quote {
    font-size: .82rem; color: var(--text); margin-top: .3rem;
    border-left: 2px solid var(--primary); padding-left: .65rem;
    overflow: hidden; text-overflow: ellipsis; display: -webkit-box;
    -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.activity-time { font-size: .72rem; color: var(--text-subtle); margin-top: .25rem; }

/* Workspaces page: consistent single-column layout. Header (title + Add
   button), pending-invitations block, and the workspace cards ALL align
   to the same narrower column so nothing feels stranded on a wide screen.
   Previously the page-inner was 1200px but the cards were capped at
   880px, producing a lopsided "title floats far left, button floats far
   right" empty canvas. */
.workspaces-page { max-width: 880px; }

/* Account row - rendered as distinct "cards" with clear gaps between imports
   so each running sync process reads as its own unit, not a row in a table. */
#accountsCard {
    background: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
    display: flex;
    flex-direction: column;
    gap: .75rem;
    width: 100%;
}
/* Account card - stacked layout: header row (identity + actions always on
   the same baseline) and body row (status + progress + metrics). Premium
   finish: subtle divider between header and body, tighter rhythm, brighter
   accents, crisper typography. */
.account-row {
    display: flex;
    flex-direction: column;
    gap: 0;
    padding: 0;
    overflow: hidden;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: var(--shadow-sm);
}
/* No hover effect: the card itself isn't clickable - the actions in the
   header (Open / Pause / Remove) are what the user interacts with. A hover
   tint made the whole card look actionable, which was misleading. */
.account-row.flash {
    /* Long, double-pulse animation so the user can't miss
       which workspace just landed on the page after Connect-
       mailbox. The previous 1.8s single-pulse was easy to
       skip past if the page was scrolled or the user blinked
       at the wrong moment. */
    animation: row-flash 3s ease-out 1;
    /* Outline ring stays for the full duration as a
       belt-and-braces signal the row is the focused one,
       even after the inner pulse fades. */
    outline: 2px solid var(--primary);
    outline-offset: 2px;
    border-radius: 12px;
    transition: outline-color .3s ease 2.6s, outline-offset .3s ease 2.6s;
}
.account-row.flash.flash-fade-out {
    outline-color: transparent;
    outline-offset: 0;
}
@keyframes row-flash {
    0%   { background: color-mix(in srgb, var(--primary) 22%, transparent); box-shadow: inset 4px 0 0 var(--primary); }
    25%  { background: color-mix(in srgb, var(--primary) 14%, transparent); box-shadow: inset 4px 0 0 var(--primary); }
    50%  { background: color-mix(in srgb, var(--primary) 22%, transparent); box-shadow: inset 4px 0 0 var(--primary); }
    75%  { background: color-mix(in srgb, var(--primary) 12%, transparent); box-shadow: inset 4px 0 0 var(--primary); }
    100% { background: transparent; box-shadow: inset 4px 0 0 transparent; }
}
/* Legacy account-email/sub/status/metrics rules removed - superseded by the
   premium layout further down (.account-row-header / .account-row-body
   typography and the stat-card metrics grid). */

/* Whole row is display-only UI - no I-beam cursor, no accidental text selection. */
.account-row,
.account-row * {
    cursor: default;
    user-select: none;
}
/* Keep interactive controls pointer-y. */
/* Interactive elements AND all their descendants show the hand cursor.
   Without the `... *` half of each pair, hovering the SVG icon or the label
   span inside a button fell through to the `.account-row *` default-cursor
   rule and produced a flicker between pointer and arrow as the mouse moved
   across the button. */
.account-row a,        .account-row a *,
.account-row button,   .account-row button *,
.account-row [role="button"], .account-row [role="button"] *,
.account-row .btn,     .account-row .btn * {
    cursor: pointer !important;
}
/* Even while an action button is mid-submit (we disable it briefly to show
   the spinner) the hand cursor should stay. Bootstrap's default `disabled`
   style flips to `not-allowed` which flickers on every live-progress tick
   that re-renders the button. */
.account-row .btn:disabled,     .account-row .btn:disabled *,
.account-row button:disabled,   .account-row button:disabled * {
    cursor: pointer !important;
    pointer-events: auto;
}
.account-row input,
.account-row textarea {
    cursor: text;
    user-select: text;
}

/* Gap / failure warning block on the account row */
.account-gap {
    margin-top: .55rem;
    padding: .55rem .7rem;
    background: var(--warning-bg);
    border: 1px solid rgba(245, 158, 11, .25);
    border-radius: 8px;
    font-size: .8rem;
}
.account-gap-head { display: flex; align-items: center; gap: .35rem; margin-bottom: .15rem; }
.account-gap-icon { color: #f59e0b; font-weight: 700; }
.account-gap-head strong { color: var(--text); font-weight: 600; }
.account-gap-body { color: var(--text-muted); font-size: .72rem; line-height: 1.4; }
.account-gap-actions { display: flex; gap: .35rem; margin-top: .5rem; flex-wrap: wrap; }
.account-gap-actions .btn { font-size: .75rem; padding: .25rem .6rem; }
html[data-theme="dark"] .account-gap { background: rgba(251, 191, 36, .08); border-color: rgba(251, 191, 36, .35); }
/* Header - identity on the left, actions on the right. Flat background
   (no gradient) - the gradient was distracting and didn't add any info. */
.account-row-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1rem 1.35rem .85rem;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
}
.account-identity {
    min-width: 0;
    flex: 1 1 auto;
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: .8rem;
}
/* Inner column wraps the title + email + state stack so the icon stays
   inline-left and the text reads naturally beside it. */
.account-identity-text {
    min-width: 0;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: .15rem;
}
.account-actions {
    display: flex;
    gap: .35rem;
    align-items: center;
    flex: 0 0 auto;
}

/* Body - premium dashboard layout: progress-row (status+bar+%) then a
   stat-card grid of metrics, then optional live-subject and gap warning. */
.account-row-body {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: .85rem;
    padding: 1rem 1.35rem 1.1rem;
}

/* Progress row - bar spans the full body width, percentage to the right.
   Status message now lives on its OWN line above (see .account-status),
   so the bar doesn't get squished into a narrow 140-260px slot any more. */
.account-progress-row {
    display: flex;
    align-items: center;
    gap: .75rem;
}
.account-progress-row .progress.account-progress {
    flex: 1 1 auto;
    height: 8px;
    background: var(--surface-2);
    border-radius: 999px;
    overflow: hidden;
}
.account-progress-row .account-progress .progress-bar {
    height: 100%;
    background: var(--primary);
    border-radius: 999px;
    transition: width .35s ease;
}
.account-pct {
    font-size: .78rem;
    font-weight: 700;
    color: var(--text-muted);
    letter-spacing: -.01em;
    flex: 0 0 auto;
    min-width: 2.4rem;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Metrics grid - each metric is a stat card with label above value. */
.account-metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
    gap: .3rem .8rem;
    margin: 0;
    padding: .55rem .75rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 10px;
}
.account-metric {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    min-width: 0;
    overflow: hidden;
}
.account-metric .metric-label {
    font-size: .62rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--text-muted);
    white-space: nowrap;
}
.account-metric .metric-value {
    font-size: .88rem;
    color: var(--text);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.account-metric .metric-value strong {
    color: var(--text);
    font-weight: 700;
}
.account-metric .metric-sep {
    color: var(--text-subtle);
    margin: 0 .1rem;
}
.account-metric .metric-unit {
    color: var(--text-muted);
    font-weight: 500;
    margin-left: .2rem;
    font-size: .75rem;
}

/* Category breakdown - horizontal row of compact pills, one per non-empty
   MessageCategory. Built to read at a glance: icon + name + count. */
.account-categories {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
}
.cat-pill {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .2rem .6rem .2rem .45rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: .76rem;
    color: var(--text);
    line-height: 1.2;
    white-space: nowrap;
}
.cat-pill .cat-icon { font-size: .85rem; line-height: 1; }
.cat-pill .cat-name { font-weight: 500; color: var(--text-muted); }
.cat-pill .cat-count {
    font-weight: 700;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    letter-spacing: -.01em;
}

/* Mobile: tighter metric grid. */
@media (max-width: 720px) {
    .account-metrics {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* Refined typography in the identity block - bigger email, subtle state. */
.account-email {
    font-weight: 600;
    color: var(--text);
    font-size: 1rem;
    letter-spacing: -.005em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
}
.account-email-sub {
    font-size: .78rem;
    font-weight: 400;
    color: var(--text-muted);
    text-transform: none;
    letter-spacing: 0;
    margin-top: .1rem;
    /* Don't shout email addresses at the user. The legacy all-caps
       treatment came from .account-sub before this override - left
       in place to belt-and-suspender the cascade. */
    font-variant-caps: normal;
}
/* State sub-line: instead of an all-caps yelled label like "IDLE",
   render as a subtle pill. Reads as data, not as a heading. */
.account-state-sub {
    text-transform: none !important;
    letter-spacing: 0 !important;
    font-weight: 500 !important;
    font-size: .7rem !important;
    color: var(--text-subtle) !important;
    display: inline-flex; align-items: center; gap: .35rem;
    margin-top: .15rem;
}
.account-state-sub::before {
    content: "";
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--text-subtle);
    flex: 0 0 auto;
}
.account-rename-btn {
    background: transparent;
    border: none;
    padding: 2px 5px;
    border-radius: 5px;
    color: var(--text-subtle);
    cursor: pointer;
    opacity: 0;
    transition: opacity 120ms ease, background-color 120ms ease, color 120ms ease;
    display: inline-flex;
    align-items: center;
}
.account-row:hover .account-rename-btn,
.account-rename-btn:focus-visible { opacity: 1; }
.account-rename-btn:hover { background: var(--primary-bg); color: var(--primary); }
.account-sub {
    font-size: .72rem;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: .05em;
}

/* Status line - icon + message, middle-aligned. Used inside the
   account-progress-row to the left of the progress bar. */
.account-status {
    font-size: .925rem;
    color: var(--text);
    display: flex;
    align-items: center;
    gap: .55rem;
    margin: 0;
    font-weight: 500;
    min-width: 0;
}
.account-status .account-msg {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

/* Live-subject - quote-style block below the metrics grid. */
.account-live-subject {
    font-size: .8rem !important;
    color: var(--text-muted) !important;
    margin: 0 !important;
    padding: .35rem .65rem !important;
    background: var(--surface-2);
    border-left: 2px solid var(--primary);
    border-radius: 4px;
}
.account-actions .btn { font-size: .8rem; padding: .35rem .7rem; }
.btn.with-icon { display: inline-flex; align-items: center; gap: .35rem; line-height: 1; }
.btn.with-icon svg { width: 14px; height: 14px; flex: 0 0 auto; stroke-width: 2; }
.btn.icon-only { width: 32px; height: 32px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; }
.btn.icon-only svg { width: 15px; height: 15px; stroke-width: 1.8; }
.btn-ghost-danger { color: var(--text-subtle); background: transparent; border: 1px solid transparent; transition: all .12s ease; }
.btn-ghost-danger:hover { color: var(--danger); background: var(--danger-bg); border-color: #fecaca; }
.btn-ghost { color: var(--text-muted); background: transparent; border: 1px solid var(--border); transition: all .12s ease; }
.btn-ghost:hover { color: var(--text); background: var(--surface-2); border-color: var(--border-strong); }
.btn-ghost.is-busy { opacity: .6; pointer-events: none; }
.btn-ghost.is-busy svg { animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Spinner */
.spinner-border-sm { width: 1rem; height: 1rem; border-width: 2px; }

/* Archive app */
.archive-layout { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }

/* ============================================================
   Archive header - compact left-clustered toolbar.
   Identity sits tight on the left; search pill and chips follow
   immediately with a small gap. No stretch, no wasted distance.
   ============================================================ */
.archive-header {
    flex: 0 0 auto;
    padding: .55rem 1rem;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    gap: .85rem;
    min-height: 52px;
}
.archive-identity {
    display: flex; align-items: baseline; gap: .55rem;
    flex: 0 1 auto;
    min-width: 0;
    padding-right: .85rem;
    border-right: 1px solid var(--border);
}
.archive-identity h2 {
    font-size: .95rem; font-weight: 600; color: var(--text); margin: 0;
    letter-spacing: -.01em;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    max-width: 260px;
    line-height: 1.2;
}
.archive-meta {
    font-size: .75rem; color: var(--text-muted);
    display: inline-flex; align-items: center; gap: .4rem;
    white-space: nowrap;
    line-height: 1.2;
}
/* Current-filter scope chip - makes the active label filter unmissable
   and gives a one-click escape hatch (×) to see all mail. */
.archive-scope {
    display: inline-flex; align-items: center; gap: .25rem;
    padding: 2px 4px 2px 9px;
    background: var(--primary-bg);
    border-radius: 999px;
    font-size: .72rem;
    color: var(--primary);
}
.archive-scope strong { font-weight: 700; letter-spacing: -.005em; }
.archive-scope-clear {
    display: inline-flex; align-items: center; justify-content: center;
    width: 16px; height: 16px;
    border-radius: 50%;
    color: var(--primary); text-decoration: none;
    font-size: .9rem; font-weight: 600; line-height: 1;
    transition: background .1s ease;
}
.archive-scope-clear:hover { background: rgba(79, 70, 229, .15); color: var(--primary); text-decoration: none; }
.archive-meta-sep { color: var(--text-subtle, var(--text-muted)); opacity: .5; }
.archive-mode-dot {
    display: inline-block; width: 7px; height: 7px; border-radius: 50%;
    background: var(--primary); flex: 0 0 auto;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 18%, transparent);
}
.archive-mode-text {
    font-size: .68rem; text-transform: uppercase; letter-spacing: .04em;
    color: var(--primary); font-weight: 700;
    /* Fixed-width slot so "GROUPED" (7ch) and "FLAT" (4ch) render in the
       same footprint. Without this the badge resized between clicks and
       the next click landed on whatever moved into the cursor's spot. */
    min-width: 3.6rem; text-align: center;
}
.archive-count {
    /* Tabular numerals so 3,631 / 9,262 / 12,858 all align column-wise -
       the count sits to the right of the toggle and we don't want digits
       jittering between clicks. */
    font-variant-numeric: tabular-nums;
}
/* Clickable grouped/flat toggle. Reads as an INTERACTIVE control, not
   a disabled badge: solid border, primary-tinted backdrop, primary
   text + dot. Two visual states distinguish modes:
     .grouped -> green dot ("threading is on")
     .flat    -> primary dot ("flat-message stream")
   Hover lifts the saturation so the click affordance is unmistakable. */
.archive-mode-toggle {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .2rem .55rem; height: 22px;
    background: var(--primary-bg);
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
    border-radius: 999px;
    text-decoration: none;
    transition: background .12s ease, border-color .12s ease, transform .08s ease;
    cursor: pointer;
}
.archive-mode-toggle:hover {
    background: color-mix(in srgb, var(--primary-bg) 60%, var(--surface));
    border-color: var(--primary);
    transform: translateY(-1px);
}
.archive-mode-toggle:active { transform: translateY(0); }
.archive-mode-toggle.grouped .archive-mode-dot {
    background: var(--success);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--success) 22%, transparent);
}
.archive-mode-toggle.flat .archive-mode-dot {
    background: var(--primary);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 22%, transparent);
}
.archive-mode-toggle.grouped .archive-mode-text { color: var(--success); }
.archive-mode-toggle.flat    .archive-mode-text { color: var(--primary); }

/* Sync health indicator - small dot + popover */
.sync-indicator { position: relative; margin-left: .35rem; }
.sync-dot-btn {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .15rem .45rem; height: 22px;
    background: var(--surface-2); border: 1px solid transparent;
    border-radius: 999px; cursor: pointer; color: var(--text-muted);
    font-size: .7rem; font-weight: 500;
    transition: background .1s ease, border-color .1s ease;
}
.sync-dot-btn:hover { background: var(--surface); border-color: var(--border); color: var(--text); }
.sync-dot {
    display: inline-block; width: 7px; height: 7px; border-radius: 50%;
    background: var(--text-subtle);
    box-shadow: 0 0 0 2px rgba(148,163,184,.15);
}
.sync-indicator[data-state="Idle"] .sync-dot { background: #10b981; box-shadow: 0 0 0 2px rgba(16,185,129,.2); }
.sync-indicator[data-state="Initial"] .sync-dot,
.sync-indicator[data-state="Incremental"] .sync-dot {
    background: #f59e0b; box-shadow: 0 0 0 2px rgba(245,158,11,.25);
    animation: sync-pulse 1.2s ease-in-out infinite;
}
.sync-indicator[data-state="Error"] .sync-dot { background: #ef4444; box-shadow: 0 0 0 2px rgba(239,68,68,.25); }
.sync-indicator[data-state="Paused"] .sync-dot { background: #94a3b8; }
@keyframes sync-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

.sync-popover {
    position: absolute; top: calc(100% + 8px); left: 0; z-index: 50;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 12px; box-shadow: 0 20px 48px rgba(15,23,42,.18);
    width: 360px; padding: .9rem 1rem;
    animation: slide-down-sm .12s ease;
    font-size: .82rem;
}
.sync-popover[hidden] { display: none; }
.sync-pop-head { display: flex; justify-content: space-between; align-items: baseline; gap: .5rem; margin-bottom: .5rem; }
.sync-pop-head strong { font-size: .95rem; color: var(--text); font-weight: 600; }
.sync-pop-time { font-size: .72rem; color: var(--text-subtle); }
.sync-pop-body { color: var(--text-muted); line-height: 1.5; }
.sync-pop-line { margin-bottom: .35rem; }
.sync-pop-line:empty { display: none; }
.sync-pop-error { color: #ef4444; font-weight: 500; white-space: pre-wrap; word-break: break-word; }

/* Progress block: labeled bar with right-aligned numeric readout */
.sync-pop-progress { margin-top: .55rem; }
.sync-pop-progress[hidden] { display: none; }
.sync-pop-progress-label {
    display: flex; justify-content: space-between; align-items: baseline;
    font-size: .7rem; color: var(--text-subtle); text-transform: uppercase;
    letter-spacing: .04em; margin-bottom: .25rem;
}
.sync-pop-progress-label .sync-pop-progress-text {
    color: var(--text); text-transform: none; letter-spacing: 0;
    font-size: .75rem; font-weight: 600;
}
.sync-pop-progress-bar { width: 100%; height: 6px; background: var(--surface-2); border-radius: 999px; overflow: hidden; }
.sync-pop-progress-fill { height: 100%; background: var(--primary); border-radius: 999px; transition: width .25s ease; }

/* Stats row: ETA / speed / downloaded */
.sync-pop-stats {
    display: grid; grid-template-columns: repeat(3, 1fr);
    gap: .5rem; margin-top: .65rem;
    padding: .5rem .6rem; background: var(--surface-2); border-radius: 8px;
}
.sync-pop-stats[hidden] { display: none; }
.sync-stat { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.sync-stat-label { font-size: .65rem; color: var(--text-subtle); text-transform: uppercase; letter-spacing: .05em; }
.sync-stat strong { font-size: .82rem; color: var(--text); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Currently downloading block */
.sync-pop-current {
    margin-top: .65rem;
    padding: .55rem .65rem;
    background: var(--primary-bg); border-radius: 8px;
    border-left: 3px solid var(--primary);
}
.sync-pop-current[hidden] { display: none; }
.sync-pop-current-label { font-size: .65rem; color: var(--primary); text-transform: uppercase; letter-spacing: .05em; font-weight: 600; margin-bottom: .15rem; }
.sync-pop-current-subject { font-size: .8rem; color: var(--text); font-weight: 500; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.sync-pop-current-from { font-size: .7rem; color: var(--text-muted); margin-top: .15rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.sync-pop-actions { display: flex; justify-content: flex-end; gap: .4rem; margin-top: .75rem; padding-top: .75rem; border-top: 1px solid var(--border); }

/* Gap / failure audit panel inside the sync popover */
.sync-pop-gap {
    margin-top: .7rem;
    padding: .55rem .65rem;
    background: var(--danger-bg);
    border: 1px solid rgba(248, 113, 113, .25);
    border-radius: 8px;
}
.sync-pop-gap[hidden] { display: none; }
.sync-pop-gap-head { display: flex; align-items: center; gap: .35rem; margin-bottom: .2rem; }
.sync-pop-gap-icon { color: var(--warning); font-size: .85rem; }
.sync-pop-gap-head strong { color: var(--text); font-size: .82rem; font-weight: 600; }
.sync-pop-gap-body { color: var(--text-muted); font-size: .75rem; line-height: 1.4; }
.sync-pop-gap-toggle {
    background: transparent; border: 0; padding: 0;
    color: var(--primary); font-size: .75rem; font-weight: 500; cursor: pointer;
}
.sync-pop-gap-toggle:hover { text-decoration: underline; }
.sync-pop-gap-quickactions {
    display: flex; align-items: center; gap: .75rem;
    margin-top: .55rem;
    justify-content: space-between;
    flex-wrap: wrap;
}
.sync-pop-gap-quickactions .btn-sm { font-size: .75rem; padding: .3rem .7rem; display: inline-flex; align-items: center; }
.sync-pop-gap-details { margin-top: .5rem; padding-top: .5rem; border-top: 1px dashed var(--border); }
.sync-pop-gap-details[hidden] { display: none; }
.sync-pop-gap-list {
    max-height: 220px; overflow-y: auto;
    font-size: .75rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
}
.sync-pop-gap-empty { color: var(--text-subtle); padding: .5rem .6rem; display: block; font-size: .72rem; }
.gap-failure { padding: .4rem .55rem; border-bottom: 1px solid var(--border); }
.gap-failure:last-child { border-bottom: 0; }
.gap-failure-head { display: flex; justify-content: space-between; gap: .5rem; }
.gap-failure-uid { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-weight: 600; color: var(--text); }
.gap-failure-meta { color: var(--text-subtle); font-size: .7rem; }
.gap-failure-error { color: var(--danger); font-size: .7rem; margin-top: .15rem; word-break: break-word; }
.sync-pop-gap-actions { display: flex; gap: .35rem; margin-top: .5rem; }

/* Commandbar is a flex row - search + filter + sort, packed left. */
.archive-commandbar {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    gap: .4rem;
    min-width: 0;
}

/* Search: pill input - same visual weight as Filter/Sort chips (outlined, surface bg) */
.archive-search {
    flex: 0 1 380px;
    min-width: 220px;
    position: relative;
    height: 34px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    transition: border-color .12s ease, background .12s ease, box-shadow .12s ease;
    margin: 0;
}
.archive-search:hover { border-color: var(--border-strong); }
.archive-search:focus-within {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px rgba(79,70,229,.12);
}
.archive-search .cmd-icon {
    position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
    width: 14px; height: 14px; color: var(--text-subtle); pointer-events: none;
}
.archive-search input {
    display: block;
    width: 100%; height: 100%;
    padding: 0 54px 0 32px; /* right padding reserves room for ? + ✕ */
    border: 0; outline: 0; background: transparent;
    font-size: .85rem; color: var(--text);
    border-radius: 8px;
    line-height: 1;
}
.archive-search input::placeholder { color: var(--text-subtle); }
.cmd-clear {
    position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
    width: 20px; height: 20px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--text-subtle); text-decoration: none; font-size: 11px; line-height: 1;
    opacity: 0; pointer-events: none; transition: opacity .12s ease, background .12s ease;
}
.cmd-clear.visible { opacity: 1; pointer-events: auto; }
.cmd-clear:hover { background: var(--surface-2); color: var(--text); }

/* Action chips: Filter + Sort - sit immediately next to search, no gap */
.cmd-btn {
    display: inline-flex; align-items: center; gap: .4rem;
    padding: 0 .7rem; height: 34px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    font-size: .82rem; font-weight: 500;
    color: var(--text);
    cursor: pointer;
    flex: 0 0 auto;
    transition: background .1s ease, border-color .1s ease, color .1s ease;
}
.cmd-btn:hover { background: var(--surface-2); border-color: var(--border-strong); }
.cmd-btn.active { background: var(--primary-bg); border-color: var(--primary); color: var(--primary); }
.cmd-btn svg { flex: 0 0 auto; color: var(--text-muted); }
.cmd-btn.active svg { color: var(--primary); }
.cmd-btn .cmd-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 18px; height: 18px; padding: 0 5px;
    background: var(--primary); color: #fff;
    border-radius: 9px; font-size: .68rem; font-weight: 700;
    margin-left: .1rem;
}

.sort-menu { position: relative; display: flex; }

/* Mobile-only sidebar toggle - hidden on desktop where sidebar is always visible */
.sidebar-toggle-btn { display: none; }

/* Search help (?) button inside the pill + popover */
.search-help-btn {
    position: absolute; right: 30px; top: 50%; transform: translateY(-50%);
    width: 18px; height: 18px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: 0; color: var(--text-subtle);
    font-size: 11px; font-weight: 700; cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.search-help-btn:hover { background: var(--surface-2); color: var(--primary); }
.search-help-pop {
    position: absolute; top: calc(100% + 8px); left: 0; z-index: 60;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 12px; box-shadow: 0 20px 48px rgba(15,23,42,.18);
    width: 380px; padding: .9rem 1rem; font-size: .82rem;
    animation: slide-down-sm .12s ease;
}
.search-help-pop[hidden] { display: none; }
.search-help-pop .shp-title { font-size: .78rem; font-weight: 700; color: var(--text); text-transform: uppercase; letter-spacing: .05em; margin-bottom: .6rem; }
.search-help-pop .shp-list { display: grid; grid-template-columns: auto 1fr; gap: .35rem .9rem; margin: 0; }
.search-help-pop dt { color: var(--text); }
.search-help-pop dt code { background: var(--surface-2); padding: 1px 6px; border-radius: 4px; font-size: .78rem; color: var(--primary); font-weight: 500; }
.search-help-pop dd { color: var(--text-muted); margin: 0; font-size: .78rem; align-self: center; }
.search-help-pop .shp-footer { margin-top: .75rem; padding-top: .75rem; border-top: 1px solid var(--border); font-size: .72rem; color: var(--text-subtle); }

/* Quick filter chips */
.quick-filters {
    display: flex; gap: .4rem; align-items: center;
    padding: .45rem 1rem; background: var(--surface);
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}
.qf-chip {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .25rem .7rem; height: 26px;
    background: transparent; border: 1px solid var(--border);
    border-radius: 999px; font-size: .78rem; font-weight: 500;
    color: var(--text-muted); text-decoration: none;
    cursor: pointer; transition: all .1s ease;
}
.qf-chip:hover { background: var(--surface-2); color: var(--text); }
.qf-chip.active { background: var(--primary-bg); border-color: var(--primary); color: var(--primary); }
/* Toast that confirms a view action (save / delete / share / unshare).
   Floats in the top-right over the page - overlay only, does NOT take layout
   space, so appearing/dismissing it never shifts the content underneath. */
.view-flash {
    position: fixed;
    top: 1rem;
    right: 1rem;
    z-index: 10000;
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: .6rem 1rem;
    max-width: min(420px, calc(100vw - 2rem));
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text);
    border-radius: 12px;
    font-size: .875rem;
    font-weight: 500;
    box-shadow: 0 8px 24px -8px rgba(15, 23, 42, .28),
                0 2px 6px rgba(15, 23, 42, .12);
    animation: view-flash-in .2s ease-out;
    pointer-events: auto;
}
.view-flash svg { flex: 0 0 auto; color: var(--primary); }
.view-flash > span { flex: 1 1 auto; color: var(--text); min-width: 0; }
.view-flash-close {
    flex: 0 0 auto;
    background: transparent;
    border: 0;
    font-size: 1.15rem;
    line-height: 1;
    color: var(--text-muted);
    cursor: pointer;
    padding: 0 .3rem;
    border-radius: 4px;
}
.view-flash-close:hover { color: var(--text); background: var(--surface-2); }
.view-flash.is-leaving { animation: view-flash-out .2s ease-in forwards; }
@keyframes view-flash-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
@keyframes view-flash-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(-8px); } }
html[data-theme="dark"] .view-flash {
    background: var(--surface);
    border-color: var(--border-strong);
    box-shadow: 0 8px 24px -8px rgba(0, 0, 0, .5),
                0 2px 6px rgba(0, 0, 0, .4);
}

.qf-chip svg { color: currentColor; }
.qf-chip .qf-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; display: inline-block; }

/* Undo toast */
.undo-toast {
    position: fixed; bottom: 24px; left: 50%; transform: translate(-50%, 20px);
    background: #1e293b; color: #fff;
    padding: .65rem .75rem .65rem 1rem;
    border-radius: 10px;
    box-shadow: 0 20px 48px rgba(15,23,42,.45);
    display: flex; align-items: center; gap: .6rem;
    min-width: 280px; max-width: 90vw;
    z-index: 4000;
    opacity: 0; transition: opacity .18s ease, transform .18s ease;
    overflow: hidden;
}
.undo-toast.visible { opacity: 1; transform: translate(-50%, 0); }
.undo-toast .undo-msg { font-size: .85rem; color: #e2e8f0; flex: 1 1 auto; }
.undo-toast .undo-btn {
    padding: .25rem .7rem; border-radius: 6px;
    background: transparent; border: 1px solid rgba(255,255,255,.25); color: #fff;
    font-size: .8rem; font-weight: 600; cursor: pointer;
    transition: background .1s ease;
}
.undo-toast .undo-btn:hover { background: rgba(255,255,255,.12); }
.undo-toast.is-undoing .undo-btn { opacity: .5; cursor: default; }
.undo-toast .undo-close {
    width: 24px; height: 24px; border-radius: 50%;
    background: transparent; border: 0; color: #94a3b8;
    font-size: 16px; cursor: pointer; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
}
.undo-toast .undo-close:hover { background: rgba(255,255,255,.1); color: #fff; }
.undo-toast .undo-bar {
    position: absolute; left: 0; bottom: 0; height: 2px;
    background: var(--primary); width: 100%;
}
@keyframes undo-countdown { from { width: 100%; } to { width: 0%; } }

/* Icon-only commandbar chip (e.g. Refresh) */
.cmd-btn.cmd-icon-btn { padding: 0; width: 34px; justify-content: center; }
.cmd-btn.cmd-icon-btn svg { color: var(--text-muted); }
.cmd-btn.cmd-icon-btn:hover svg { color: var(--primary); }
/* Trailing chip: pushed to the far right of the commandbar */
.cmd-btn.cmd-trailing { margin-left: auto; }
.cmd-btn.is-spinning svg { animation: cmd-refresh-spin .9s linear infinite; }
@keyframes cmd-refresh-spin { to { transform: rotate(-360deg); } }

@media (max-width: 900px) {
    .archive-header { flex-wrap: wrap; gap: .55rem; }
    .archive-identity { padding-right: 0; border-right: 0; flex: 1 1 100%; }
    .archive-commandbar { flex: 1 1 100%; }
    .archive-search { flex: 1 1 auto; max-width: none; }
}
@media (max-width: 560px) {
    .cmd-btn span.cmd-label { display: none; }
    .cmd-btn { padding: 0 .55rem; }
}

/* Advanced filter form - floating panel anchored under Filters button via JS */
.adv-panel-form { position: static; z-index: 40; }
.adv-panel-form .adv-panel {
    position: fixed;
    /* top + left set by JS to sit under the Filters button */
    width: min(680px, calc(100vw - 2rem));
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 1.1rem 1.25rem 1rem;
    box-shadow: 0 20px 50px rgba(15, 23, 42, .18), 0 2px 6px rgba(15, 23, 42, .06);
    margin: 0;
    z-index: 40;
    animation: slide-down-sm .15s ease;
}
.adv-panel-form .adv-panel .adv-row { gap: .85rem; margin-bottom: .65rem; }
.adv-panel-form .adv-panel label span { font-size: .74rem; }
.adv-panel-form .adv-panel input[type="text"],
.adv-panel-form .adv-panel input[type="date"],
.adv-panel-form .adv-panel label input:not([type="checkbox"]) { padding: .4rem .6rem; font-size: .85rem; }
.adv-panel-form .adv-panel .adv-checks { padding-top: .6rem; margin-top: .5rem; gap: .5rem; }
.adv-panel-form .adv-panel .adv-actions { margin-top: .75rem; padding-top: .75rem; border-top: 1px solid var(--border); }

/* Label-filter row. Visually matches the other input rows in the
   panel: a small-caps caption followed by a full-width control. The
   "control" here is a stack - mode <select> on top, chip cloud +
   hint underneath when mode = "specific". The mode select's full
   width is intentional; pairing it with anything to the right made
   the row feel half-empty when chips were hidden. */
.adv-panel-form .adv-panel .adv-row-1 {
    grid-template-columns: 1fr;
}
.adv-panel-form .adv-panel .adv-row-label-filter {
    margin-top: .25rem;
    padding-top: .65rem;
    border-top: 1px solid var(--border);
}
.adv-panel-form .adv-panel .adv-label-control {
    display: flex;
    flex-direction: column;
    gap: .5rem;
}
.adv-panel-form .adv-panel .adv-label-mode {
    /* Compact select sized to its content. Width: max-content lets
       the browser size to the longest option text + native chevron;
       no full-width stretch, no oversized minimum. The widest option
       ("Has specific label(s)") sits around ~170px including the
       arrow, which is right-sized for a small dropdown. */
    width: max-content;
    max-width: 100%;
    padding: .35rem .55rem;
    font-size: .85rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text);
    appearance: auto;
    cursor: pointer;
    align-self: flex-start;
}
.adv-panel-form .adv-panel .adv-label-mode:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 1px;
}

/* Compact label list: hidden until mode == specific. One line per
   label (checkbox + name), scrollable when the list grows past
   ~6 entries. Sits flush below the select with a soft outline so
   it reads as a "list of options to tick" without competing with
   the input rows above. */
.adv-panel-form .adv-panel .adv-label-list {
    display: none;
    flex-direction: column;
    gap: 0;
    max-height: 200px;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
}
.adv-panel-form .adv-panel .adv-label-control[data-adv-label-active="true"] .adv-label-list {
    display: flex;
}
.adv-panel-form .adv-panel .adv-label-row {
    /* One row = one line. Force horizontal flow and LEFT alignment
       (justify-content: flex-start) so the checkbox sits next to its
       label like a regular OS-style multi-pick list. The select-
       picker style on .chk has previously bled in via specificity
       conflicts; the !important here is defensive against any
       future ancestor change that injects align-items: center on
       a stacked column. */
    display: flex !important;
    flex-direction: row !important;
    align-items: center !important;
    justify-content: flex-start !important;
    gap: .5rem;
    padding: .35rem .6rem;
    margin: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
    font-size: .85rem;
    color: var(--text);
    text-transform: none;
    letter-spacing: 0;
    text-align: left;
    transition: background .08s ease;
}
.adv-panel-form .adv-panel .adv-label-row + .adv-label-row {
    border-top: 1px solid var(--surface-2);
}
.adv-panel-form .adv-panel .adv-label-row:hover {
    background: var(--surface-2);
}
.adv-panel-form .adv-panel .adv-label-row input[type="checkbox"] {
    flex: 0 0 auto;
    width: 14px;
    height: 14px;
    margin: 0;
    accent-color: var(--primary);
    cursor: pointer;
}
.adv-panel-form .adv-panel .adv-label-row-name {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 500;
    /* Override the panel's small-caps eyebrow style on bare <span>. */
    text-transform: none;
    letter-spacing: 0;
    font-size: .85rem;
    color: var(--text);
}
.adv-panel-form .adv-panel .adv-label-row:has(input:checked) {
    background: var(--primary-bg);
}
.adv-panel-form .adv-panel .adv-label-row:has(input:checked) .adv-label-row-name {
    color: var(--primary);
    font-weight: 600;
}
.adv-panel-form .adv-panel .adv-label-hint {
    display: none;
    font-size: .72rem;
    color: var(--text-muted);
    line-height: 1.4;
    padding: 0 .15rem;
}
.adv-panel-form .adv-panel .adv-label-control[data-adv-label-active="true"] .adv-label-hint {
    display: block;
}
/* Backdrop click-out support (hidden by default; shown when panel is open via JS) */
.adv-panel-backdrop { position: fixed; inset: 0; z-index: 35; background: transparent; }
.adv-panel-backdrop[hidden] { display: none; }

/* Confirm modal - premium replacement for browser confirm() */
.confirm-backdrop { position: fixed; inset: 0; z-index: 9998; background: rgba(15,23,42,.55); backdrop-filter: blur(3px); display: flex; align-items: center; justify-content: center; padding: 1rem; animation: fade-in .12s ease; }
.confirm-backdrop[hidden] { display: none; }
.confirm-panel { width: min(440px, 92vw); background: var(--surface); border: 1px solid var(--border); border-radius: 16px; box-shadow: 0 20px 60px rgba(0,0,0,.3); padding: 1.75rem 1.75rem 1.25rem; text-align: center; animation: slide-down-sm .15s ease; }
.confirm-icon { width: 56px; height: 56px; margin: 0 auto 1rem; border-radius: 14px; background: var(--primary-bg); color: var(--primary); display: inline-flex; align-items: center; justify-content: center; }
.confirm-panel h3 { font-size: 1.15rem; font-weight: 700; margin: 0 0 .6rem; letter-spacing: -.01em; color: var(--text); }
.confirm-panel p { font-size: .9rem; color: var(--text-muted); line-height: 1.55; margin: 0 0 1.5rem; }
.confirm-panel p strong { color: var(--text); }
.confirm-actions { display: flex; gap: .5rem; justify-content: center; }
.confirm-actions .btn { min-width: 120px; }
/* Spinner shown inside the OK button while an async-confirm callback
   is running. Same look as the bulk-toolbar spinner so the visual
   language is consistent across the app. */
.confirm-actions .btn .confirm-spin {
    display: inline-block;
    width: 14px; height: 14px;
    margin-right: .45rem;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    vertical-align: -2px;
    animation: confirm-spin .65s linear infinite;
}
@keyframes confirm-spin { to { transform: rotate(360deg); } }
.confirm-actions .btn:disabled, .confirm-actions .btn[disabled] { opacity: .7; cursor: progress; }
/* Inline-error state for the message paragraph - used when an async
   onConfirm callback throws so the user can read what went wrong
   without leaving the modal. */
.confirm-panel p.confirm-error {
    color: #b91c1c;
    background: color-mix(in srgb, #b91c1c 6%, transparent);
    border: 1px solid color-mix(in srgb, #b91c1c 22%, transparent);
    border-radius: 8px;
    padding: .65rem .85rem;
    margin: 0 0 1.25rem;
    text-align: left;
}
.archive-toolbar-left { flex: 0 0 auto; min-width: 0; }
.archive-toolbar-left h2 { font-size: 1.05rem; font-weight: 600; margin: 0; color: var(--text); letter-spacing: -.01em; }
.archive-toolbar-left .sub { font-size: .75rem; color: var(--text-muted); margin-top: .1rem; }

/* (legacy .archive-search block removed - see unified rules around line 736) */
.archive-body { flex: 1 1 auto; min-height: 0; display: flex; overflow: hidden; }
/* Sidebar drawer backdrop is mobile-only - hide on desktop so it can never
   accidentally cover the page and block clicks on the navbar. */
.archive-sidebar-backdrop { display: none; }
.archive-sidebar { flex: 0 0 260px; border-right: 1px solid var(--border); background: var(--surface); overflow-y: auto; padding: 1rem .75rem; }
.archive-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; overflow: hidden; background: var(--surface); }
.archive-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.archive-list { flex: 1 1 auto; min-width: 0; overflow-y: auto; background: var(--surface); padding: 0; }

.mail-sidebar .list-group-item { border: 0; padding: .5rem .8rem; background: transparent; color: var(--text); font-size: .875rem; border-radius: 8px; text-decoration: none; display: flex; align-items: center; gap: .7rem; min-height: 34px; }
.mail-sidebar .list-group-item:hover { background: var(--surface-2); }

/* Sidebar rows (system folders + saved views) are navigational, not text.
   user-select: none prevents the text caret that otherwise appears when
   clicking the label - same issue the .account-row rule solved on the
   Accounts page. cursor: pointer is forced onto descendants (icons, count
   chips, name spans) so no child falls through to a text cursor. */
.mail-sidebar .list-group-item,
.mail-sidebar .list-group-item *,
.mail-sidebar .view-link,
.mail-sidebar .view-link * {
    -webkit-user-select: none;
    user-select: none;
    cursor: pointer !important;
}
.mail-sidebar .list-group-item.active { background: var(--primary-bg); color: var(--primary); font-weight: 600; }
.mail-sidebar h6 { font-size: .7rem; letter-spacing: .08em; color: var(--text-subtle); text-transform: uppercase; margin: 1rem .8rem .4rem; font-weight: 600; }
.mail-sidebar .list-group { display: flex; flex-direction: column; gap: 2px; }

/* Sidebar user-label row: link + hover-revealed delete trigger. The link
   keeps the same visual as before (hover, active, etc. - inherited from
   .list-group-item rules elsewhere). The delete button overlays the
   right side of the row, hidden until hover/focus so the sidebar stays
   calm at rest. */
.user-label-row {
    position: relative;
    display: flex; align-items: stretch;
}
.user-label-row .user-label-link { flex: 1 1 auto; min-width: 0; }
/* Active-state: when the sidebar's current Label matches this user
   label, paint the link with the same primary-bg + primary-color
   treatment system folders use (see .mail-sidebar .list-group-item
   .active). The active class lives on the OUTER .user-label-row so
   the row's per-label dot + delete affordance can react to it; the
   visible chrome lives on the inner .user-label-link to match the
   surrounding folder rows pixel-for-pixel. */
.user-label-row.active .user-label-link {
    background: var(--primary-bg);
    color: var(--primary);
    font-weight: 600;
}
.user-label-row.active .user-label-link .sb-name { color: var(--primary); }
.user-label-row.active .user-label-link .sb-count { color: var(--primary); }
.user-label-delete {
    position: absolute;
    top: 50%; right: 6px;
    transform: translateY(-50%);
    width: 22px; height: 22px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 0;
    background: transparent;
    color: var(--text-muted);
    border-radius: 5px;
    cursor: pointer;
    opacity: 0;
    transition: opacity .12s ease, background .12s ease, color .12s ease;
}
.user-label-row:hover .user-label-delete,
.user-label-delete:focus-visible { opacity: 1; }
.user-label-delete:hover {
    background: color-mix(in srgb, #b91c1c 12%, transparent);
    color: #b91c1c;
}
.user-label-delete:focus-visible {
    outline: 2px solid #b91c1c;
    outline-offset: 1px;
}
/* Hide the count badge while hovering so it doesn't sit under the
   trash icon. Reappears on mouse-out. */
.user-label-row:hover .user-label-link .sb-count { opacity: 0; }
/* Pending state while the server processes the delete. */
.user-label-row.is-deleting { opacity: .5; pointer-events: none; }
.mail-sidebar .label-dot { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; margin: 0; }
.mail-sidebar .label-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto; min-width: 0; }
.mail-sidebar .list-group-item > span:first-child { flex: 0 0 auto; }
.mail-sidebar .list-group-item .sb-icon {
    flex: 0 0 auto;
    width: 15px; height: 15px;
    color: var(--text-muted);
}
.mail-sidebar .list-group-item:hover .sb-icon,
.mail-sidebar .list-group-item.has-unread .sb-icon {
    color: var(--text);
}
.mail-sidebar .list-group-item.active .sb-icon { color: var(--primary); }

/* Sidebar label + count layout
   Name takes the middle, count hugs the right edge.
   The count is a subtle tabular-number pill - low visual weight when
   the item is inactive, crisp primary-tinted when the item is active,
   and primary-filled on hover so it feels like part of the same chip. */
.mail-sidebar .sb-name {
    flex: 1 1 auto; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-sidebar .sb-count {
    flex: 0 0 auto;
    font-size: .7rem; font-weight: 600;
    color: var(--text-muted);
    /* Subtle pill background so the count always reads as a chip,
       never as faded "disabled" text. The has-unread variant below
       takes over with a primary tint when there's something new to
       see; the default state stays a neutral but legible chip. */
    background: var(--surface-2);
    padding: 1px 7px;
    border-radius: 999px;
    font-variant-numeric: tabular-nums;
    letter-spacing: .01em;
    transition: background .12s ease, color .12s ease;
    margin-left: auto;
}
.mail-sidebar .list-group-item:hover .sb-count {
    background: var(--surface);
    color: var(--text);
}
.mail-sidebar .list-group-item.active .sb-count {
    background: var(--primary);
    color: #fff;
}
/* Dark-mode: tweak the neutral chip background so it has the same
   "always-a-chip" presence without competing with the row hover. */
html[data-theme="dark"] .mail-sidebar .sb-count {
    background: rgba(148,163,184,.12);
}
html[data-theme="dark"] .mail-sidebar .list-group-item:hover .sb-count {
    background: rgba(148,163,184,.20);
}

/* Unread accent. A folder with unread messages gets the count pill in
   the primary color so it stands out without an extra badge. The dot in
   front of the name is intentionally small - the goal is to read at a
   glance, not to scream. */
.mail-sidebar .list-group-item.has-unread .sb-name { font-weight: 600; color: var(--text); }
.mail-sidebar .list-group-item.has-unread .sb-count {
    background: var(--primary-bg);
    color: var(--primary);
}
.mail-sidebar .list-group-item.has-unread.active .sb-count { background: var(--primary); color: #fff; }

/* Hover popover: pure CSS, anchored to the sidebar row. Reads the
   data-sb-tip attribute via attr() in content. Sits to the RIGHT of
   the row (the natural reading direction inside the sidebar) so it
   doesn't cover the row label itself. */
.mail-sidebar .list-group-item[data-sb-tip] { position: relative; }
.mail-sidebar .list-group-item[data-sb-tip]:hover::after {
    content: attr(data-sb-tip);
    position: absolute;
    top: 50%; left: calc(100% + 8px); transform: translateY(-50%);
    background: var(--text); color: var(--surface);
    font-size: .72rem; font-weight: 500; line-height: 1.3;
    padding: 5px 9px; border-radius: 6px;
    white-space: nowrap;
    pointer-events: none;
    z-index: 50;
    box-shadow: 0 4px 14px rgba(15, 23, 42, .25);
    opacity: 0; animation: sb-tip-in .12s ease forwards;
}
@keyframes sb-tip-in { to { opacity: 1; } }
/* Preference: hide sidebar counts. Set via body.hide-label-counts
   from Burofolk.prefs.apply(). No layout shift - the row just loses
   its right-hand chip. */
body.hide-label-counts .mail-sidebar .sb-count { display: none; }
/* Sidebar count badges are now always rendered (even at 0) so live updates
   from star/read toggles can mutate the same node without inserting/removing
   DOM. Hide the empty case so a 0-count folder reads identically to the
   old conditional render. */
.mail-sidebar .sb-count:empty { display: none; }

.sidebar-nav .nav-item {
    display: flex; align-items: center; gap: .6rem;
    padding: .45rem .75rem;
    font-size: .875rem;
    color: var(--text);
    border-radius: 6px;
    text-decoration: none;
    cursor: pointer;
}
.sidebar-nav .nav-item:hover { background: var(--surface-2); }
.sidebar-nav .nav-item.active { background: var(--primary-bg); color: var(--primary); font-weight: 600; }
.sidebar-heading { font-size: .7rem; letter-spacing: .08em; color: var(--text-subtle); text-transform: uppercase; margin: 1rem .75rem .4rem; font-weight: 600; }
.label-dot { display: inline-block; width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }

/* Mail row */
.mail-row {
    display: grid;
    /* Left cluster: checkbox | read-stripe | star - zero gap so each control
       owns its pixels. Content takes everything else as a two-line block:
       Line 1: sender ................................... date
       Line 2: subject · muted snippet preview ........................
       This is the Superhuman / Apple Mail pattern - scannable because each
       row has predictable visual anchors instead of one long text river.
       Left padding gives the checkbox breathing room from the row edge; the
       checkbox column itself widens a little so the label can fully fill it
       and own its pixels for clicks. */
    grid-template-columns: 36px 22px 32px minmax(0, 1fr);
    column-gap: 0;
    align-items: center;
    padding: .7rem 1.25rem .7rem .9rem;
    border-bottom: 1px solid var(--surface-2);
    text-decoration: none;
    color: inherit;
    cursor: default;
    transition: background .08s ease;
}
.mail-row > .mail-content {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
    /* Bigger left gap so the text doesn't crowd the star. The text area is
       also the only "open the message" hit zone on the row - cursor flips
       to pointer here, and stays default everywhere else (checkbox, read
       stripe, star) so users can target each control without surprises. */
    padding-left: .85rem;
    padding-right: .25rem;
    cursor: pointer;
}
.mail-line-1 {
    display: flex;
    align-items: baseline;
    gap: .75rem;
    min-width: 0;
}
.mail-line-1 > .mail-from {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: baseline;
    gap: .4rem;
    overflow: hidden;
}
.mail-line-1 > .mail-date {
    flex: 0 0 auto;
}
/* "Open in new tab" icon on each row. Invisible by default so the list
   stays clean; fades in on row hover so it's discoverable exactly when
   the user is considering that row. Clicks on it don't bubble up to the
   row's own click handler - so you don't also get a same-tab navigation
   happening under the new-tab action. */
.mail-line-1 > .mail-open-tab {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px;
    border-radius: 6px;
    color: var(--text-muted);
    text-decoration: none;
    opacity: 0;
    transition: opacity .12s ease, background .12s ease, color .12s ease;
}
.mail-row:hover .mail-line-1 > .mail-open-tab { opacity: .7; }
.mail-line-1 > .mail-open-tab:hover {
    opacity: 1;
    background: var(--primary-bg);
    color: var(--primary);
}
.mail-line-1 > .mail-open-tab:focus-visible {
    opacity: 1;
    outline: 2px solid var(--primary);
    outline-offset: 1px;
}
.mail-line-1 > .mail-open-tab svg { display: block; }
.mail-row.selected {
    background: var(--primary-bg) !important;
    box-shadow: inset 3px 0 0 var(--primary);
}

/* Custom checkbox in the row's first column. The label fills the entire
   grid cell (full width, full row height) so any click that lands in the
   checkbox column toggles selection - no narrow hit-target, no accidental
   row-open from clicking just beside the visual square. The 17px box still
   reads as a small precise checkbox; the surrounding label is invisible
   but absorbs the clicks. */
.mail-select {
    display: flex; align-items: center; justify-content: center;
    cursor: pointer; margin: 0;
    width: 100%;
    min-height: 36px;
    align-self: stretch;
    opacity: .4;
    transition: opacity .15s ease;
    -webkit-user-select: none;
    user-select: none;
}
.mail-row:hover .mail-select,
.mail-row.selected .mail-select,
body.has-selection .mail-select { opacity: 1; }
.mail-checkbox { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.mail-check-box {
    display: inline-block; width: 17px; height: 17px;
    border: 1.5px solid var(--border-strong); border-radius: 5px;
    background: var(--surface);
    position: relative;
    transition: background .12s ease, border-color .12s ease, box-shadow .12s ease, transform .1s ease;
}
.mail-select:hover .mail-check-box {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-bg);
}
.mail-checkbox:focus-visible + .mail-check-box {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px rgba(79, 70, 229, .25);
}
.mail-checkbox:checked + .mail-check-box {
    background: var(--primary);
    border-color: var(--primary);
    box-shadow: 0 2px 6px -1px rgba(79, 70, 229, .45);
}
.mail-select:active .mail-check-box { transform: scale(.92); }
.mail-checkbox:checked + .mail-check-box::after {
    content: ""; position: absolute;
    left: 4.5px; top: 1px;
    width: 5px; height: 10px;
    border-right: 2.2px solid #fff;
    border-bottom: 2.2px solid #fff;
    transform: rotate(42deg);
    border-radius: 1px;
}

/* Thread expand button - a clickable version of the count pill. Same
   visual weight as the static pill so its presence doesn't shout; the
   hover state signals interactivity. */
.thread-expand {
    display: inline-flex; align-items: center; gap: 3px;
    flex: 0 0 auto;
    background: var(--surface-2); border: 0; cursor: pointer;
    padding: 1px 6px 1px 7px; height: 18px;
    border-radius: 999px;
    color: var(--text-muted);
    font: inherit; font-size: .68rem; font-weight: 700; line-height: 1;
    transition: background .12s ease, color .12s ease;
}
.thread-expand:hover { background: var(--primary-bg); color: var(--primary); }
.thread-expand .thread-count {
    /* Inside the button - neutralize the standalone pill styling so the
       whole button reads as a single chip. */
    background: transparent; color: inherit;
    font-size: inherit; font-weight: inherit;
    padding: 0; margin: 0; line-height: 1;
}
.thread-expand:hover .thread-count { background: transparent; color: inherit; }
.thread-expand .thread-chev { transition: transform .15s ease; opacity: .8; }
.mail-row.expanded .thread-expand { background: var(--primary-bg); color: var(--primary); }
.mail-row.expanded .thread-expand .thread-chev { transform: rotate(180deg); opacity: 1; }
.mail-row.unread .thread-expand { background: var(--primary-bg); color: var(--primary); opacity: .85; }

/* Thread children - subordinate single-line sub-rows shown when a thread
   is expanded. They're indented far enough to clearly read as "inside"
   the parent, with a visible vertical connector on the left so the
   hierarchy is unambiguous.

   Connector uses direct border-left on the element (not a pseudo-
   element) so it's always visible regardless of zebra stripes or
   background layers. */
.thread-children {
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
    padding: .2rem 0;
}
html[data-theme="dark"] .thread-children { background: rgba(79, 70, 229, .05); }
.thread-child {
    display: grid;
    grid-template-columns: 20px minmax(140px, 200px) minmax(0, 1fr) 70px;
    gap: .6rem;
    align-items: center;
    padding: .4rem 1.25rem .4rem .75rem;
    color: var(--text);
    text-decoration: none;
    margin-left: 4.5rem;
    border-left: 2px solid var(--border);
    transition: background .08s ease, border-color .08s ease;
}
.thread-child:hover {
    background: var(--surface);
    color: var(--text);
    border-left-color: var(--primary);
}
/* Unread dot indicator - first grid column. */
.thread-child .tc-dot {
    justify-self: center;
    width: 7px; height: 7px;
    border-radius: 50%;
    background: transparent;
}
.thread-child.unread .tc-dot { background: var(--primary); }
.thread-child .tc-from {
    color: var(--text);
    font-weight: 600;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: .82rem;
    min-width: 0;
}
.thread-child .tc-body { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; font-size: .82rem; }
.thread-child .tc-subject { color: var(--text); font-weight: 500; }
.thread-child .tc-snippet { color: var(--text-subtle, var(--text-muted)); margin-left: .5rem; font-weight: 400; font-size: .78rem; opacity: .9; }
.thread-child .tc-snippet::before {
    content: '·';
    display: inline-block;
    margin-right: .45rem;
    color: var(--border-strong, #cbd5e1);
    opacity: .85;
    transform: translateY(-1px);
}
.thread-child .tc-date { text-align: right; color: var(--text-muted); font-size: .72rem; white-space: nowrap; font-variant-numeric: tabular-nums; }
.thread-child .tc-attach { color: var(--text-muted); margin-left: .25rem; display: inline-flex; align-items: center; vertical-align: middle; }
.thread-child .tc-attach svg { display: block; }
.thread-child.starred .tc-subject::before { content: "★ "; color: #eab308; }
/* Zebra-stripe + fixed-height hover (no CLS) */
.mail-row:nth-child(even) { background: #f3f5fb; }
.mail-row:nth-child(odd) { background: var(--surface); }
/* Dark-mode list - standard mail-client pattern: uniform row background,
   thin separators, unread signaled by the left stripe + bold text that
   already live on .read-btn / .mail-row.unread. No zebra, no bg tint. */
html[data-theme="dark"] .mail-row,
html[data-theme="dark"] .mail-row:nth-child(odd),
html[data-theme="dark"] .mail-row:nth-child(even) {
    background: var(--surface);
}
html[data-theme="dark"] .mail-row { border-bottom: 1px solid rgba(148, 163, 184, .08); }
html[data-theme="dark"] .mail-row:hover {
    background: #182133 !important;
}
.mail-row:hover {
    background: var(--primary-bg) !important;
}
.mail-row:hover .mail-snippet { color: var(--text); }
.mail-row:hover .mail-from { color: var(--primary); }
.mail-row:hover .mail-date { color: var(--text); font-weight: 500; }

/* Cursor-follow hover preview - interactive so users can act on the message
   (open / toggle star / toggle read) without first clicking into it.
   CRITICAL: while hidden (no .visible class) it must NOT capture pointer
   events or it will sit invisibly at top:0/left:0 and block clicks on the
   nav and other top-of-viewport UI. */
.mail-preview {
    position: fixed; z-index: 2000;
    background: var(--surface); color: var(--text);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 20px 48px rgba(15,23,42,.22);
    padding: .9rem 1.05rem 3.1rem;
    width: 480px; max-width: calc(100vw - 32px);
    /* Predictable visual size regardless of whether we have a body yet
       (fetch pending), a short snippet fallback, or a long body. The card
       should feel like "the email card", not "a variable-height tooltip". */
    min-height: 300px;
    pointer-events: none;
    opacity: 0; transform: translateY(4px);
    transition: opacity .12s ease, transform .12s ease;
    font-size: .85rem;
    line-height: 1.5;
}
.mail-preview.visible { pointer-events: auto; }
/* Action footer inside the preview */
.mail-preview .mp-actions {
    position: absolute; left: .6rem; right: .6rem; bottom: .6rem;
    display: flex; gap: .3rem; align-items: center;
    padding-top: .5rem; border-top: 1px dashed var(--border);
    margin-top: .5rem;
}
.mail-preview .mp-act {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .3rem .55rem; height: 28px;
    background: var(--surface-2); border: 1px solid transparent;
    border-radius: 6px; cursor: pointer;
    font-size: .75rem; font-weight: 500; color: var(--text-muted);
    transition: all .1s ease;
}
.mail-preview .mp-act:hover { background: var(--primary-bg); color: var(--primary); border-color: var(--primary); }
.mail-preview .mp-act.is-primary { margin-left: auto; background: var(--primary); color: #fff; }
.mail-preview .mp-act.is-primary:hover { background: var(--primary-hover); color: #fff; }
.mail-preview .mp-act svg { flex: 0 0 auto; }
.mail-preview.visible { opacity: 1; transform: translateY(0); }

/* Header: subject + meta badges + date in one tidy row */
.mail-preview .mp-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: .75rem;
    /* Tight spacing below the subject line - the .mp-headers block
       directly underneath has its own padding-bottom + border-bottom
       and used to look detached from the subject because of an
       extra .5rem margin here. Halving it brings From flush against
       the subject the way the user expects. */
    margin-bottom: .25rem;
}
.mail-preview .mp-subject {
    flex: 1 1 auto; min-width: 0;
    font-weight: 600; font-size: .95rem; color: var(--text);
    line-height: 1.35;
    overflow: hidden; text-overflow: ellipsis;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.mail-preview .mp-meta {
    flex: 0 0 auto;
    display: flex; flex-direction: column; align-items: flex-end; gap: .2rem;
}
.mail-preview .mp-date { font-size: .7rem; color: var(--text-subtle); white-space: nowrap; }
.mail-preview .mp-badge {
    display: inline-flex; align-items: center;
    padding: 1px 7px; border-radius: 999px;
    font-size: .65rem; font-weight: 600; letter-spacing: .02em;
    white-space: nowrap;
}
.mail-preview .mp-badge-unread { background: var(--primary-bg); color: var(--primary); }
.mail-preview .mp-badge-attach { background: var(--surface-2); color: var(--text-muted); }

.mail-preview .mp-from {
    font-size: .75rem; color: var(--text-subtle);
    margin-bottom: .65rem; padding-bottom: .5rem;
    border-bottom: 1px solid var(--border);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-preview .mp-from strong { color: var(--text); font-weight: 600; }

/* Header rows (From / To / Reply-To). Each address is rendered as
   a hoverable .mp-addr chip that pops up the contact card. */
.mail-preview .mp-headers {
    display: flex; flex-direction: column;
    /* Zero gap so consecutive lines stack tight; the .mp-line's own
       compact line-height controls vertical rhythm. The previous
       .2rem flex gap visually doubled with the line-height padding
       and made the section look airy. */
    gap: 0;
    margin-bottom: .55rem; padding-bottom: .45rem;
    border-bottom: 1px solid var(--border);
    font-size: .76rem;
}
.mail-preview .mp-headers:empty { display: none; }
.mail-preview .mp-line {
    display: flex; gap: .55rem; align-items: baseline;
    line-height: 1.5;
}
.mail-preview .mp-line-label {
    flex: 0 0 auto; min-width: 56px;
    color: var(--text-subtle);
    text-transform: uppercase; letter-spacing: .03em;
    font-size: .68rem; font-weight: 600;
}
.mail-preview .mp-line-value {
    flex: 1 1 auto; min-width: 0;
    color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mp-addr {
    display: inline; cursor: default;
    color: var(--text);
    border-bottom: 1px dotted transparent;
    transition: border-color .12s ease, color .12s ease;
}
.mp-addr:hover, .mp-addr:focus-visible {
    color: var(--primary);
    border-bottom-color: var(--primary);
    outline: none;
}

/* Gmail-style contact card that pops up when hovering an .mp-addr.
   Floating element appended to <body> so it can escape the parent
   preview's overflow + z-index context. Hidden by default (opacity:
   0 + pointer-events: none) so it doesn't intercept clicks until
   it animates in. */
.mp-addr-card {
    position: fixed;
    z-index: 1080;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 12px 28px rgba(15, 23, 42, .14), 0 2px 6px rgba(15, 23, 42, .08);
    padding: .55rem .65rem;
    min-width: 220px; max-width: 320px;
    opacity: 0; transform: translateY(-2px); pointer-events: none;
    transition: opacity .12s ease, transform .12s ease;
}
.mp-addr-card.visible {
    opacity: 1; transform: translateY(0); pointer-events: auto;
}
.mp-addr-card { min-width: 280px; max-width: 360px; padding: .75rem .8rem; }
.mp-addr-card-row {
    display: flex; align-items: center; gap: .65rem;
    padding-bottom: .55rem;
    border-bottom: 1px solid var(--border);
    margin-bottom: .55rem;
}
.mp-addr-card-avatar {
    flex: 0 0 auto;
    width: 36px; height: 36px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%;
    background: color-mix(in srgb, var(--primary) 18%, var(--surface-2));
    color: var(--primary);
    font-weight: 700; font-size: 1rem;
    text-transform: uppercase;
}
.mp-addr-card-text { flex: 1 1 auto; min-width: 0; }
.mp-addr-card-name {
    font-size: .9rem; font-weight: 600; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mp-addr-card-email {
    font-size: .78rem; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    user-select: text;
}
.mp-addr-card-actions {
    display: flex; gap: .35rem; align-items: center;
    flex-wrap: wrap;
}
.mp-addr-card-action {
    flex: 1 1 auto;
    display: inline-flex; align-items: center; justify-content: center;
    gap: .35rem;
    padding: .4rem .55rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 7px;
    color: var(--text);
    font-size: .78rem; font-weight: 500;
    cursor: pointer;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.mp-addr-card-action:hover {
    background: var(--primary-bg);
    border-color: var(--primary);
    color: var(--primary);
}
.mp-addr-card-action svg { flex: 0 0 auto; }
.mp-addr-card-action.mp-addr-card-copy-icon {
    flex: 0 0 auto;
    width: 32px; padding: .4rem 0;
}
.mp-addr-card-action.mp-addr-card-copy-icon span { display: none; }
.mp-addr-card-action.mp-addr-card-send {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}
.mp-addr-card-action.mp-addr-card-send:hover {
    background: var(--primary-hover);
    border-color: var(--primary-hover);
    color: #fff;
}
.mp-addr-card-action.is-copied {
    background: color-mix(in srgb, #10b981 16%, var(--surface));
    border-color: #10b981;
    color: #047857;
}
/* Sender name in the mail list now triggers the contact card.
   Subtle hover affordance so the user knows it's interactive. */
.mail-from-name.mp-addr {
    cursor: pointer;
    border-bottom: 1px dotted transparent;
    transition: border-color .12s ease;
}
.mail-from-name.mp-addr:hover {
    border-bottom-color: var(--primary);
}

.mail-preview .mp-body {
    color: var(--text); font-size: .82rem; line-height: 1.55;
    /* Reserves consistent vertical real estate: short bodies/snippets fill
       the top and leave whitespace below; long bodies get clamped. Either
       way, the card visually commits to the same footprint every time. */
    min-height: 160px; max-height: 240px;
    overflow: hidden;
    display: -webkit-box; -webkit-line-clamp: 10; -webkit-box-orient: vertical;
    white-space: pre-wrap; word-break: break-word;
}

/* Action footer: two compact icon-buttons + a primary Open button */
.mail-preview .mp-actions {
    position: absolute; left: .75rem; right: .75rem; bottom: .6rem;
    display: flex; gap: .3rem; align-items: center;
    padding-top: .55rem; border-top: 1px solid var(--border);
}
.mail-preview .mp-act {
    display: inline-flex; align-items: center; justify-content: center; gap: .3rem;
    padding: 0 .5rem; height: 28px;
    background: transparent; border: 1px solid var(--border); color: var(--text-muted);
    border-radius: 6px; cursor: pointer;
    font-size: .75rem; font-weight: 500;
    transition: all .1s ease;
}
.mail-preview .mp-act:hover { background: var(--primary-bg); color: var(--primary); border-color: var(--primary); }
.mail-preview .mp-act:not(.is-primary):not(.is-on) { width: 30px; padding: 0; }
.mail-preview .mp-act.is-on { color: #eab308; border-color: #eab308; }
.mail-preview .mp-act.is-primary {
    margin-left: auto; padding: 0 .75rem;
    background: var(--primary); color: #fff; border-color: var(--primary);
    text-decoration: none;
}
.mail-preview .mp-act.is-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); color: #fff; }
.mail-preview .mp-act svg { flex: 0 0 auto; }
@media (max-width: 820px) { .mail-preview { display: none !important; } }
.mail-flag { text-align: center; font-size: 1rem; line-height: 1; }
.mail-flag .star { color: #eab308; }
.important-marker {
    display: inline-block;
    color: #eab308;
    font-weight: 700;
    font-size: .75rem;
    margin-right: .3rem;
    transform: translateY(-1px);
    opacity: .9;
}
/* ── Two-line row typography ─────────────────────────────────────────
   Line 1 (sender + date): sender leads the eye down the list; date
     anchors the right edge so you can scan chronology at a glance.
   Line 2 (subject + snippet): subject is primary content, snippet is
     a recessed preview in muted color and slightly smaller.
   Unread state bumps weight + saturation on line 1's sender and line
   2's subject so unread rows feel heavier without changing layout. */
.mail-from {
    font-weight: 600;
    color: var(--text);
    font-size: .9rem;
    letter-spacing: -.005em;
    min-width: 0;
}
.mail-from-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    /* flex: 0 1 auto - name takes its natural width and shrinks under
       pressure (ellipsis). Crucially, it does NOT grow, so the thread-
       expand pill stays pinned right next to the name instead of being
       flung to the far right of the line. */
    flex: 0 1 auto;
}
.mail-from .thread-expand,
.mail-from .thread-count,
.mail-from .important-marker { flex: 0 0 auto; }
.mail-from-link { color: inherit; text-decoration: none; }
.mail-from-link:hover { color: var(--primary); text-decoration: underline; }

.mail-body {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: .84rem;
    min-width: 0;
    line-height: 1.4;
}
.mail-subject {
    color: var(--text);
    font-weight: 500;
}
.mail-snippet {
    color: var(--text-muted);
    margin-left: .5rem;
    font-weight: 400;
    opacity: .78;
}
/* Subtle dot separator between subject and snippet - lighter than an
   em-dash, just enough punctuation to say "this is where the preview
   starts". */
.mail-snippet::before {
    content: '·';
    display: inline-block;
    margin-right: .45rem;
    color: var(--border-strong, #cbd5e1);
    opacity: .7;
    transform: translateY(-1px);
}

.mail-date {
    text-align: right;
    font-size: .72rem;
    color: var(--text-muted);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    letter-spacing: .01em;
}
.mail-attach {
    color: var(--text-muted);
    margin-left: .35rem;
    display: inline-flex;
    align-items: center;
    vertical-align: middle;
}
.mail-attach svg { display: block; }
.mail-pin {
    display: inline-flex; align-items: center; vertical-align: middle;
    margin-right: .35rem;
    color: #eab308; /* amber to match starred palette */
}
.mail-pin svg { display: block; }
/* Team-notes indicator on a row */
.mail-note {
    display: inline-flex; align-items: center; gap: 2px;
    margin-left: .35rem;
    padding: 0 5px; height: 16px;
    background: var(--primary-bg); color: var(--primary);
    border-radius: 999px;
    vertical-align: middle;
    border: 1px solid transparent;
}
.mail-note svg { color: currentColor; }
.mail-note-count { font-size: .66rem; font-weight: 700; line-height: 1; }
html[data-theme="dark"] .mail-note { background: rgba(129,140,248,.15); border-color: rgba(129,140,248,.35); }

/* Unread emphasis */
.mail-row.unread .mail-from,
.mail-row.unread .mail-subject { font-weight: 700; color: var(--text); }
.mail-row.unread .mail-date { color: var(--text); font-weight: 600; }
.mail-row { position: relative; }

/* Clickable read/unread indicator - a slim vertical stripe. The button fills
   its whole grid cell with no gaps around it, so the full 22px column is
   clickable and there's no dead-zone whitespace that would fall through to
   the row navigation handler. */
.read-btn {
    width: 100%; height: 34px; padding: 0;
    border: 0; background: transparent; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
}
.read-btn::before {
    content: ""; display: block;
    width: 3px; height: 22px; border-radius: 2px;
    transition: background .12s ease, width .12s ease, height .12s ease;
}
/* Unread = solid primary stripe. Read = a ghosted, always-visible stripe so
   the control stays discoverable even when every row is read. */
.read-btn.is-unread::before { background: var(--primary); }
.read-btn.is-read::before { background: var(--border-strong); opacity: .35; }
.mail-row:hover .read-btn.is-read::before { opacity: .65; }
.read-btn:hover::before { background: var(--primary); opacity: 1; width: 4px; height: 26px; }
.read-btn:hover[title]::after {
    /* small inline tooltip so the affordance is obvious on hover */
    content: attr(title);
    position: absolute;
    left: 100%; top: 50%; transform: translateY(-50%);
    margin-left: 6px;
    padding: 2px 8px;
    background: var(--text); color: var(--surface);
    border-radius: 5px; font-size: .7rem; white-space: nowrap;
    pointer-events: none; z-index: 10;
    box-shadow: 0 2px 6px rgba(0,0,0,.15);
}
.read-btn { position: relative; }
.read-btn:disabled { opacity: .4; pointer-events: none; }

/* Flash bar shown after an account redirect (e.g. the inbox the user was
   viewing was removed on another tab). Dismissible, auto-fades after 6s. */
.account-flash {
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .55rem .9rem;
    margin-bottom: 1rem;
    background: var(--warning-bg, #fef3c7);
    border: 1px solid rgba(245, 158, 11, .35);
    color: var(--text);
    border-radius: 10px;
    font-size: .875rem;
    animation: account-flash-in .25s ease-out;
}
.account-flash svg { color: #d97706; flex: 0 0 auto; }
@keyframes account-flash-in {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}
html[data-theme="dark"] .account-flash {
    background: rgba(251, 191, 36, .12);
    border-color: rgba(251, 191, 36, .4);
}

.empty-state { padding: 4rem 1.5rem; text-align: center; color: var(--text-muted); }
.empty-state .emoji { font-size: 2.5rem; display: block; margin-bottom: .75rem; }
.empty-state .empty-badge {
    width: 64px; height: 64px;
    margin: 0 auto 1rem;
    border-radius: 18px;
    background: linear-gradient(135deg, var(--primary), #8b5cf6);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 1.75rem;
    box-shadow: 0 8px 24px -10px rgba(79, 70, 229, .5);
}
.empty-state h5 { color: var(--text); font-weight: 700; margin-bottom: .35rem; }
.empty-state p { color: var(--text-muted); margin-bottom: 1.25rem; }
html[data-theme="dark"] .empty-state .empty-badge {
    box-shadow: 0 8px 28px -8px rgba(129, 140, 248, .45);
}

/* Pagination */
.pagination { margin: 1rem 0; justify-content: center; }
.pagination .page-link { color: var(--text); border: 1px solid var(--border); border-radius: 6px; margin: 0 .15rem; font-size: .825rem; padding: .35rem .75rem; }
.pagination .page-item.active .page-link { background: var(--primary); color: #fff; border-color: var(--primary); }

/* Premium pagination bar - flex layout with 1 / auto / 1 sizing so the nav
   controls stay dead-center regardless of how wide the left/right sides grow. */
.pagination-bar {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: .65rem 1.25rem;
    background: var(--surface);
    border-top: 1px solid var(--border);
    box-shadow: 0 -4px 12px -10px rgba(15,23,42,.08);
}
.pagination-range {
    flex: 1 1 0;
    font-size: .82rem;
    color: var(--text-muted);
    display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
    min-width: 0;
}
.pagination-controls { flex: 0 0 auto; }
.pagination-jump-wrap { flex: 1 1 0; display: flex; justify-content: flex-end; min-width: 0; }
.pg-single-page { color: var(--text-subtle); font-size: .78rem; }
@media (max-width: 900px) {
    .pagination-bar { flex-wrap: wrap; gap: .5rem; padding: .5rem .75rem; }
    .pagination-range, .pagination-controls, .pagination-jump-wrap { flex: 1 1 100%; justify-content: center; }
    /* Drop first/last (««/»») and the jumper on narrow screens - prev/next + page numbers are enough. */
    .pagination-controls .pg-btn[title="First page"],
    .pagination-controls .pg-btn[title="Last page"] { display: none; }
    .pagination-jump-wrap { display: none; }
}
@media (max-width: 560px) {
    /* Tighter still - hide numeric buttons except current, keep prev/next only. */
    .pagination-controls .pg-btn:not(.active):not([title="Previous"]):not([title="Next"]) { display: none; }
    .pagination-controls .pg-ellipsis { display: none; }
}
/* Legacy compact class no-op - kept in case lingering refs point at it */
.pagination-bar-compact { }
.pagination-range strong { color: var(--text); font-weight: 600; }
.pg-action {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .25rem .6rem; height: 26px;
    font-size: .76rem; font-weight: 500; color: var(--text-muted);
    background: transparent; border: 1px solid var(--border); border-radius: 6px;
    cursor: pointer; transition: all .1s ease;
}
.pg-action:hover { color: var(--primary); border-color: var(--primary); background: var(--primary-bg); }
.pg-action svg { color: currentColor; }

/* Compact kebab dropdown for per-view actions (mark-all-read, etc.) */
.pg-more { position: relative; }
.pg-more-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; padding: 0;
    background: transparent; border: 1px solid var(--border);
    border-radius: 6px; cursor: pointer; color: var(--text-muted);
    transition: all .1s ease;
}
.pg-more-btn:hover, .pg-more-btn.is-open { color: var(--primary); border-color: var(--primary); background: var(--primary-bg); }
.pg-more-menu {
    position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 30;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 10px; box-shadow: 0 12px 32px rgba(15,23,42,.15);
    min-width: 200px; padding: .35rem;
}
.pg-more-menu[hidden] { display: none; }
.pg-more-item {
    display: flex; align-items: center; gap: .5rem; width: 100%;
    padding: .5rem .65rem; background: transparent; border: 0;
    border-radius: 6px; cursor: pointer; text-align: left;
    font-size: .82rem; color: var(--text);
}
.pg-more-item:hover { background: var(--surface-2); }
.pg-more-item svg { color: var(--text-muted); flex: 0 0 auto; }
.pg-more-item:hover svg { color: var(--primary); }
/* Compact bar (single page): no pagination controls, no jumper - the range row
   sits alone and should left-align, not sit in a 3-column grid. */
.pagination-bar-compact { grid-template-columns: 1fr; }
.pagination-bar-compact .pagination-range { justify-self: start; }

/* ==== Action toolbar above the message list ============================
   Always visible (fixed-height row) so the list never jumps when the
   selection state changes. Two zones:
     - .bulk-bar-left   : swaps between "empty" hint and bulk actions
     - .bulk-bar-right  : refresh control, present in both states
   The body class .has-selection drives the swap (set by updateBar()). */
.bulk-bar {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: .4rem;
    padding: .5rem 1rem;
    height: 52px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    /* Container query context. Using the bulk-bar's actual rendered
       width (not the viewport) means the icon-only collapse fires
       based on whether the toolbar has room for labels - robust to
       sidebar collapse, split-view, embedded layouts, anything that
       changes the toolbar's available space without changing the
       window size. Container queries are supported in every
       evergreen browser since 2023 (Chrome 105+, Safari 16+, FF 110+). */
    container-type: inline-size;
    container-name: bulkbar;
}
.bulk-bar-left {
    flex: 1 1 auto;
    min-width: 0;
    display: flex; align-items: center; gap: .4rem;
    /* No overflow:hidden - it clips the absolutely-positioned label
       popover and the chooser menu. Horizontal scroll, when needed, is
       enabled per breakpoint below; the y-axis must stay visible. */
}
.bulk-bar-right {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: .4rem;
    padding-left: .5rem;
    border-left: 1px solid var(--surface-2);
}
/* Selection chooser: master checkbox + caret menu (Gmail pattern).
   Always visible. The checkbox toggles select-all-on-page; the caret
   opens a menu of advanced rules (All / None / Read / Unread / Starred /
   Unstarred). Sits at the very left of the toolbar in both empty and
   selection states - the action buttons follow it when selection > 0. */
.bulk-chooser {
    position: relative;
    flex: 0 0 auto;
    display: inline-flex; align-items: center;
    gap: 0;
    padding: 1px 4px 1px 2px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    height: 32px;
}
.bulk-chooser-cb {
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 100%;
    cursor: pointer;
    margin: 0;
}
.bulk-chooser-cb input[type="checkbox"] {
    position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none;
}
.bulk-chooser-box {
    display: inline-block; width: 16px; height: 16px;
    border: 1.5px solid var(--border-strong);
    border-radius: 4px;
    background: var(--surface);
    position: relative;
    transition: background .12s ease, border-color .12s ease;
}
.bulk-chooser-cb:hover .bulk-chooser-box { border-color: var(--primary); }
.bulk-chooser-cb input:checked + .bulk-chooser-box {
    background: var(--primary); border-color: var(--primary);
}
.bulk-chooser-cb input:checked + .bulk-chooser-box::after {
    content: ""; position: absolute;
    left: 4px; top: 0px;
    width: 5px; height: 10px;
    border-right: 2px solid #fff;
    border-bottom: 2px solid #fff;
    transform: rotate(42deg);
}
.bulk-chooser-cb input:indeterminate + .bulk-chooser-box {
    background: var(--primary); border-color: var(--primary);
}
.bulk-chooser-cb input:indeterminate + .bulk-chooser-box::after {
    content: ""; position: absolute;
    left: 3px; top: 6px; width: 8px; height: 2px;
    background: #fff; border-radius: 1px;
}
.bulk-chooser-caret {
    width: 18px; height: 100%;
    border: 0;
    background: transparent;
    cursor: pointer;
    color: var(--text-muted);
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 4px;
    transition: background .1s ease, color .1s ease;
}
.bulk-chooser-caret:hover { background: var(--surface-2); color: var(--text); }
.bulk-chooser-caret[aria-expanded="true"] { background: var(--surface-2); color: var(--primary); }
.bulk-chooser-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    z-index: 60;
    min-width: 160px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: .25rem;
    box-shadow: 0 12px 28px -8px rgba(15,23,42,.18), 0 2px 6px -1px rgba(15,23,42,.06);
    animation: bulk-fade-in .1s ease;
}
.bulk-chooser-menu[hidden] { display: none; }
.bulk-chooser-item {
    display: block;
    width: 100%;
    text-align: left;
    padding: .35rem .55rem;
    border: 0;
    background: transparent;
    color: var(--text);
    font-size: .85rem;
    border-radius: 5px;
    cursor: pointer;
    transition: background .08s ease, color .08s ease;
}
.bulk-chooser-item:hover { background: var(--primary-bg); color: var(--primary); }
.bulk-chooser-sep {
    height: 1px;
    background: var(--surface-2);
    margin: .25rem .25rem;
}

.bulk-bar-actions {
    display: none;
    align-items: center; gap: .4rem;
    flex: 1 1 auto;
    min-width: 0;
    animation: bulk-fade-in .14s ease;
}
body.has-selection .bulk-bar-actions { display: flex; }
@keyframes bulk-fade-in { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: none; } }
.bulk-btn-icon {
    width: 32px; padding: 0 !important;
    justify-content: center;
}
.bulk-btn-icon svg { margin: 0; }
.bulk-btn-icon.is-spinning svg { animation: bulk-spin 1s linear infinite; transform-origin: 50% 50%; }
.bulk-cancel {
    width: 30px; height: 30px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: 1px solid var(--border);
    border-radius: 6px; cursor: pointer; color: var(--text-muted);
    transition: all .1s ease;
}
.bulk-cancel:hover { background: var(--surface-2); color: var(--text); border-color: var(--border-strong); }
.bulk-count {
    font-size: .9rem;
    color: var(--text);
    margin: 0 .5rem 0 .25rem;
    /* Keep "1 selected" / "12 selected" on a single line at every
       width. Without this, narrow viewports wrap the text into
       "1 / selected" which looks like a layout bug and also
       inflates the row height. */
    white-space: nowrap;
    flex: 0 0 auto;
}
.bulk-count strong { color: var(--primary); font-weight: 700; }
.bulk-btn {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: 0 .7rem; height: 30px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 6px; cursor: pointer;
    font-size: .82rem; font-weight: 500; color: var(--text);
    transition: all .1s ease;
    /* Keep button labels on a single line at every viewport width.
       Without this, narrow viewports wrap "Mark read" -> "Mark"
       above "read" inside the fixed-height button, which both
       breaks the 30px height invariant AND looks awful. The
       toolbar overflow is handled by the .bulk-bar-actions
       horizontal-scroll rule below - buttons never lose their
       single-line layout. */
    white-space: nowrap;
    flex: 0 0 auto;
}
.bulk-btn:hover { background: var(--primary-bg); color: var(--primary); border-color: var(--primary); }
.bulk-btn:disabled { opacity: .6; cursor: default; }
.bulk-btn svg { color: currentColor; flex: 0 0 auto; }
.bulk-spin {
    display: inline-block; width: 12px; height: 12px;
    border: 2px solid var(--border-strong);
    border-top-color: var(--primary);
    border-radius: 50%;
    animation: bulk-spin .8s linear infinite;
}
@keyframes bulk-spin { to { transform: rotate(360deg); } }
.bulk-divider { width: 1px; height: 20px; background: var(--border); margin: 0 .2rem; }
/* Trash button reads as destructive: red label / hover, soft red ring
   on focus. Restore (its sibling state) reads as a normal action - we
   just give it a slightly green tint on hover so the verb-flip is
   immediately legible. */
.bulk-btn-trash:hover {
    background: color-mix(in srgb, #b91c1c 8%, var(--surface));
    border-color: color-mix(in srgb, #b91c1c 28%, var(--border));
    color: #b91c1c;
}
.bulk-btn-restore:hover {
    background: color-mix(in srgb, #16a34a 8%, var(--surface));
    border-color: color-mix(in srgb, #16a34a 28%, var(--border));
    color: #15803d;
}
/* ── Toolbar too narrow for labels: collapse to icon-only ──
   Container query on the .bulk-bar's own width - fires whenever
   the TOOLBAR (not the viewport) is too narrow for full labels,
   regardless of sidebar state, embedded layout, etc. Threshold
   1100px is the empirical fit-point for chooser + count + 9
   action buttons + cancel + refresh with their full text labels.
   Below it, every button collapses to a 32×32 icon-only square.
   Label text in these buttons lives as bare text nodes directly
   inside the button (no <span> wrapper), so we can't selectively
   hide it via a child selector. Instead we set `font-size: 0`
   on the button: text nodes collapse to zero size while SVG
   icons stay rendered at their explicit width/height attributes
   (13×13). Trailing dropdown chevrons (the second SVG sibling)
   are hidden via :not(:first-child) on the SVGs. Screen readers
   still announce the button via title / aria-label. */
@container bulkbar (max-width: 1100px) {
    .bulk-btn {
        width: 32px;
        padding: 0 !important;
        justify-content: center;
        gap: 0;
        font-size: 0;
        overflow: hidden;
    }
    .bulk-btn > svg:not(:first-child),
    .bulk-btn > kbd,
    .bulk-btn > span:not(.bulk-spin) {
        display: none;
    }
    .bulk-bar-actions {
        flex-wrap: nowrap;
        overflow: visible;
        gap: .25rem;
    }
    .bulk-bar {
        gap: .35rem;
        padding: .45rem .65rem;
    }
}

/* ── Toolbar very narrow: drop the "X selected" count too ──
   Below ~520px even icon-only is tight; the count is already
   implied by the highlighted row checkboxes. */
@container bulkbar (max-width: 520px) {
    .bulk-count { display: none; }
    .bulk-bar { padding: .35rem .4rem; }
    .bulk-bar-actions { gap: .15rem; }
}

/* Fallback for browsers that don't support container queries
   (Safari < 16, Firefox < 110). Mirror the breakpoints against
   the viewport. Modern users hit the container-query rules
   above; legacy users get the viewport-based collapse. */
@supports not (container-type: inline-size) {
    @media (max-width: 1100px) {
        .bulk-btn {
            width: 32px;
            padding: 0 !important;
            justify-content: center;
            gap: 0;
            font-size: 0;
            overflow: hidden;
        }
        .bulk-btn > svg:not(:first-child),
        .bulk-btn > kbd,
        .bulk-btn > span:not(.bulk-spin) {
            display: none;
        }
        .bulk-bar-actions {
            flex-wrap: nowrap;
            overflow: visible;
            gap: .25rem;
        }
        .bulk-bar {
            gap: .35rem;
            padding: .45rem .65rem;
        }
    }
    @media (max-width: 520px) {
        .bulk-count { display: none; }
        .bulk-bar { padding: .35rem .4rem; }
        .bulk-bar-actions { gap: .15rem; }
    }
}
/* Ensure archive-main is positioned so the bulk bar anchors to it */
.archive-main { position: relative; }

/* ── Right-click context menu on mail rows ──────────────────────────
   Anchored to the cursor on `contextmenu`, populated dynamically based
   on which bulk-action buttons are currently rendered (so a viewer
   without CanEditLabels never sees "Label as"). position:fixed +
   z-index above the bulk-bar / sidebars so scroll containers don't
   misplace it. The wrapper is hidden by default; the JS toggles
   `hidden` and writes left/top inline. */
.mail-ctx-menu {
    position: fixed;
    z-index: 1100;
    min-width: 200px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: .25rem;
    box-shadow: 0 12px 28px -8px rgba(15, 23, 42, .25), 0 2px 6px -1px rgba(15, 23, 42, .08);
    user-select: none;
    animation: mail-ctx-fade-in .1s ease;
}
.mail-ctx-menu[hidden] { display: none; }
@keyframes mail-ctx-fade-in {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: none; }
}
.mail-ctx-item {
    display: flex; align-items: center; gap: .55rem;
    width: 100%;
    padding: .4rem .6rem;
    border: 0;
    background: transparent;
    color: var(--text);
    font: inherit;
    font-size: .85rem;
    text-align: left;
    border-radius: 5px;
    cursor: pointer;
    transition: background .08s ease, color .08s ease;
}
.mail-ctx-item:hover,
.mail-ctx-item:focus-visible {
    background: var(--primary-bg);
    color: var(--primary);
    outline: none;
}
.mail-ctx-item[hidden] { display: none; }
.mail-ctx-item.is-destructive { color: #b91c1c; }
.mail-ctx-item.is-destructive:hover,
.mail-ctx-item.is-destructive:focus-visible {
    background: color-mix(in srgb, #b91c1c 8%, var(--surface));
    color: #b91c1c;
}
.mail-ctx-item svg:first-child { flex: 0 0 auto; color: currentColor; }
.mail-ctx-item .mail-ctx-chev { margin-left: auto; opacity: .7; }
.mail-ctx-sep {
    height: 1px;
    background: var(--surface-2);
    margin: .25rem .15rem;
}
.mail-ctx-sep[hidden] { display: none; }

/* Flyout sub-menus (Label as / Assign to). Each parent menu item
   sits inside a .mail-ctx-flyout-host wrapper which becomes the
   positioning context for the flyout. Hidden by default; opened on
   hover/focus by the JS. The flyout slides out to the right of the
   parent (left-aligned with the parent's right edge) and is
   reflowed to the LEFT (.is-flipped) when there's no horizontal
   space. The 4px overlap (negative margin) lets the user's pointer
   travel from parent to child without crossing a gap that would
   close the flyout. */
.mail-ctx-flyout-host {
    position: relative;
}
.mail-ctx-flyout-host[hidden] { display: none; }
.mail-ctx-flyout-host.is-open > .mail-ctx-item {
    background: var(--primary-bg);
    color: var(--primary);
}
.mail-ctx-flyout {
    position: absolute;
    top: -.25rem;
    left: calc(100% - 4px);
    z-index: 10;
    min-width: 240px;
    max-width: 320px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: .25rem;
    box-shadow: 0 12px 28px -8px rgba(15, 23, 42, .25), 0 2px 6px -1px rgba(15, 23, 42, .08);
    display: flex; flex-direction: column;
    animation: mail-ctx-fade-in .1s ease;
}
.mail-ctx-flyout[hidden] { display: none; }
.mail-ctx-flyout.is-flipped {
    left: auto;
    right: calc(100% - 4px);
}

.mail-ctx-search {
    display: flex; align-items: center; gap: .4rem;
    margin: .15rem .25rem .25rem;
    padding: .35rem .55rem;
    background: var(--surface-2);
    border-radius: 6px;
    border: 1px solid transparent;
    transition: border-color .12s ease, background .12s ease;
}
.mail-ctx-search:focus-within {
    background: var(--surface);
    border-color: var(--primary);
}
.mail-ctx-search svg { color: var(--text-subtle); flex: 0 0 auto; }
.mail-ctx-search input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: transparent;
    color: var(--text);
    font: inherit;
    font-size: .85rem;
    outline: none;
}

.mail-ctx-list {
    max-height: 280px;
    overflow-y: auto;
    padding: .15rem 0;
}
.mail-ctx-empty {
    padding: .65rem .65rem;
    color: var(--text-muted);
    font-size: .8rem;
    font-style: italic;
    text-align: center;
}

.mail-ctx-create-btn {
    display: flex; align-items: center; gap: .4rem;
    width: calc(100% - .5rem);
    margin: .15rem .25rem .15rem;
    padding: .4rem .55rem;
    border: 1px dashed var(--border-strong);
    background: transparent;
    color: var(--primary);
    font: inherit;
    font-size: .8rem;
    font-weight: 600;
    text-align: left;
    border-radius: 6px;
    cursor: pointer;
}
.mail-ctx-create-btn:hover, .mail-ctx-create-btn:focus-visible {
    background: var(--primary-bg);
    border-color: var(--primary);
    outline: none;
}
.mail-ctx-create-btn[hidden] { display: none; }

/* Label rows inside the labels sub-pane. Custom checkbox pseudo-
   element so we can show "all" (filled), "some" (dash) and "none"
   (empty) states without an actual <input>. */
.mail-ctx-label-row {
    display: flex; align-items: center; gap: .55rem;
    width: 100%;
    padding: .35rem .55rem;
    border: 0;
    background: transparent;
    color: var(--text);
    font: inherit;
    font-size: .85rem;
    text-align: left;
    border-radius: 5px;
    cursor: pointer;
    transition: background .08s ease;
}
.mail-ctx-label-row:hover, .mail-ctx-label-row:focus-visible {
    background: var(--primary-bg);
    color: var(--primary);
    outline: none;
}
.mail-ctx-label-name-text {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.mail-ctx-cb {
    flex: 0 0 auto;
    width: 14px; height: 14px;
    border-radius: 3px;
    border: 1.5px solid var(--border-strong);
    background: var(--surface);
    position: relative;
    transition: background .1s ease, border-color .1s ease;
}
.mail-ctx-cb.is-checked {
    background: var(--primary);
    border-color: var(--primary);
}
.mail-ctx-cb.is-checked::after {
    content: "";
    position: absolute;
    left: 3px; top: 0px;
    width: 4px; height: 8px;
    border-right: 2px solid #fff;
    border-bottom: 2px solid #fff;
    transform: rotate(42deg);
}
.mail-ctx-label-row.is-some .mail-ctx-cb {
    background: var(--primary);
    border-color: var(--primary);
}
.mail-ctx-label-row.is-some .mail-ctx-cb::after {
    content: "";
    position: absolute;
    left: 2px; top: 5px;
    width: 8px; height: 2px;
    background: #fff;
    border-radius: 1px;
}

/* Assignee rows. Avatar + 2-line text. Uses the shared --avatar-bg
   token so the letter bubble matches every other surface. */
.mail-ctx-assignee-row {
    display: flex; align-items: center; gap: .55rem;
    width: 100%;
    padding: .4rem .55rem;
    border: 0;
    background: transparent;
    color: var(--text);
    font: inherit;
    font-size: .85rem;
    text-align: left;
    border-radius: 5px;
    cursor: pointer;
    transition: background .08s ease;
}
.mail-ctx-assignee-row:hover, .mail-ctx-assignee-row:focus-visible {
    background: var(--primary-bg);
    outline: none;
}
.mail-ctx-assignee-avatar {
    width: 24px; height: 24px;
    border-radius: 50%;
    background: var(--avatar-bg);
    color: var(--avatar-color);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .72rem; font-weight: 700;
    flex: 0 0 auto;
    overflow: hidden;
}
img.mail-ctx-assignee-avatar { object-fit: cover; }
.mail-ctx-assignee-text {
    display: flex; flex-direction: column;
    min-width: 0;
    line-height: 1.15;
}
.mail-ctx-assignee-name {
    font-size: .85rem; font-weight: 600;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-ctx-assignee-email {
    font-size: .72rem; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-ctx-assignee-self {
    font-size: .68rem; color: var(--text-muted); font-weight: 500;
}
.mail-ctx-assignee-clear {
    border-bottom: 1px solid var(--surface-2);
    margin-bottom: .25rem;
    padding-bottom: .5rem;
    color: var(--text-muted);
}
.mail-ctx-assignee-clear-icon {
    width: 24px; height: 24px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%;
    background: var(--surface-2);
    color: var(--text-muted);
    font-size: 1rem;
    flex: 0 0 auto;
}

/* Right-clicked row visual highlight (subtle band on the left) so
   the user knows which row the menu acts on without us touching the
   checkbox. Survives until the menu closes. */
.mail-row.is-ctx-target {
    background: color-mix(in srgb, var(--primary) 6%, var(--surface));
    box-shadow: inset 3px 0 0 0 var(--primary);
}

/* ── Assignee picker popover ─ shared between the message-detail
   pill and the Mail/Index bulk "Assign to" toolbar button. Originally
   page-scoped to Message.cshtml; promoted to site.css so the bulk
   popover (rendered inside .label-pop on the Mail/Index page) gets
   the same look. The defensive overflow + object-fit on the avatar
   guards against an img child blowing past the 28x28 frame when the
   rules from a parent class don't otherwise apply. */
.msg-assignee-pop-list { max-height: 320px; overflow-y: auto; padding: .25rem; }
.msg-assignee-pop-item {
    display: flex; align-items: center; gap: .55rem;
    width: 100%;
    padding: .4rem .5rem;
    border: 0;
    background: transparent;
    border-radius: 7px;
    cursor: pointer;
    text-align: left;
    font: inherit;
    color: var(--text);
    transition: background .08s ease;
}
.msg-assignee-pop-item:hover { background: var(--surface-2); }
.msg-assignee-pop-item.is-current { background: var(--primary-bg); }
.msg-assignee-pop-item.is-current:hover { background: color-mix(in srgb, var(--primary) 15%, var(--surface-2)); }
.msg-assignee-pop-avatar {
    width: 28px; height: 28px;
    min-width: 28px; max-width: 28px;
    min-height: 28px; max-height: 28px;
    border-radius: 50%;
    flex: 0 0 auto;
    overflow: hidden;
    background: linear-gradient(135deg, var(--primary), #8b5cf6);
    color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .72rem; font-weight: 700;
    object-fit: cover;
    object-position: center;
}
img.msg-assignee-pop-avatar { display: block; }
.msg-assignee-pop-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.msg-assignee-pop-name { font-size: .85rem; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-assignee-pop-email { font-size: .72rem; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-assignee-pop-check { color: var(--primary); flex: 0 0 auto; }
.msg-assignee-pop-empty { padding: 1.2rem .8rem; text-align: center; color: var(--text-muted); font-size: .82rem; }
/* Clear-assignment footer. Reads as a clearly destructive secondary
   action: muted-red label on a faint warning band, slim height, and
   it's the only thing on its row so users can't mistake it for one
   of the picker items above. The picker hides this entirely when
   there's nothing currently assigned (the JS sets [hidden]). */
.msg-assignee-pop-clear {
    width: 100%;
    display: flex; align-items: center; justify-content: center; gap: .4rem;
    padding: .55rem .75rem;
    background: color-mix(in srgb, #b91c1c 5%, var(--surface));
    border: 0;
    border-top: 1px solid color-mix(in srgb, #b91c1c 18%, var(--surface-2));
    color: #b91c1c;
    cursor: pointer;
    font-family: inherit;
    font-size: .8rem;
    font-weight: 600;
    line-height: 1;
    transition: background .1s ease, color .1s ease;
}
.msg-assignee-pop-clear:hover {
    background: color-mix(in srgb, #b91c1c 14%, var(--surface));
    color: #991b1b;
}
.msg-assignee-pop-clear[hidden] { display: none; }
.msg-assignee-pop-clear svg { opacity: .85; }

/* Bulk-assign subtitle. Sits between the search input and the
   teammate list; tells the user what's already on the selection so
   "Clear" / "Assign to X" is unambiguous. */
.bulk-assign-subtitle {
    margin-top: .35rem;
    padding: .25rem .55rem;
    background: var(--surface-2);
    border-radius: 6px;
    font-size: .72rem;
    font-weight: 600;
    color: var(--text-muted);
    line-height: 1.3;
}
.bulk-assign-subtitle[hidden] { display: none; }

/* Per-row assignee chip - tucked into the .mail-line-1 between the
   thread-count badge and the open-in-new-tab affordance. Small and
   muted so it never competes with the sender / date typography but
   visible enough to scan ownership at a glance. */
.mail-assignee {
    display: inline-flex; align-items: center;
    flex: 0 0 auto;
    margin-left: .35rem;
}
.mail-assignee-avatar {
    width: 18px; height: 18px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--primary), #8b5cf6);
    color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .58rem; font-weight: 700;
    text-transform: uppercase;
    border: 1.5px solid var(--surface);
    box-shadow: 0 0 0 1px var(--border);
}

/* ── Compose CTA in the mail sidebar + floating compose card ════════
   The button at the top of the sidebar opens a Gmail-style compose
   card pinned to the bottom-right of the viewport. The card hosts
   the same rich-text editor used in the reply flow, plus chip-style
   recipient inputs and a Cc/Bcc reveal toggle. */
/* Compose CTA - slim, premium, single-color. The earlier chunky
   gradient + tinted icon-box version felt loud against the calm
   sidebar; this version reads as a confident primary action without
   shouting. Solid primary background, refined shadow, no decorative
   icon wrapper. */
.mail-compose-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    width: calc(100% - 1.2rem);
    margin: .9rem .6rem .75rem;
    padding: .5rem .9rem;
    height: 36px;
    background: var(--primary);
    color: #fff;
    border: 0;
    border-radius: 9px;
    cursor: pointer;
    font-family: inherit;
    font-size: .87rem;
    font-weight: 600;
    letter-spacing: -.005em;
    line-height: 1;
    box-shadow:
        0 1px 1px rgba(15, 23, 42, .06),
        0 4px 10px -4px color-mix(in srgb, var(--primary) 45%, transparent);
    transition: background .12s ease, box-shadow .15s ease, transform .15s ease;
}
.mail-compose-btn:hover {
    background: color-mix(in srgb, var(--primary) 88%, #000);
    box-shadow:
        0 1px 2px rgba(15, 23, 42, .08),
        0 6px 14px -4px color-mix(in srgb, var(--primary) 55%, transparent);
    transform: translateY(-1px);
}
.mail-compose-btn:active {
    transform: translateY(0);
    box-shadow: 0 1px 2px rgba(15, 23, 42, .08);
}
.mail-compose-btn:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}
.mail-compose-btn-icon {
    flex: 0 0 auto;
    opacity: .95;
}
.mail-compose-btn-label { flex: 0 0 auto; }

/* Compose shell - the full-viewport overlay that hosts the floating card.
   Backdrop is faint so the user can still see the inbox they're
   composing from; clicking it does NOT close (avoids losing a draft to
   a stray click - close requires the explicit ✕ button). */
.mail-compose-shell {
    position: fixed;
    inset: 0;
    z-index: 1080;
    pointer-events: none;
}
.mail-compose-shell[hidden] { display: none; }
.mail-compose-shell-backdrop {
    position: absolute; inset: 0;
    background: rgba(15, 23, 42, .12);
    backdrop-filter: blur(1px);
    pointer-events: auto;
    animation: mail-compose-fade .18s ease;
}
@keyframes mail-compose-fade { from { opacity: 0; } to { opacity: 1; } }

.mail-compose-card {
    position: absolute;
    bottom: 16px; right: 16px;
    width: min(620px, calc(100vw - 32px));
    max-height: calc(100vh - 32px);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: 0 24px 60px -12px rgba(15, 23, 42, .35),
                0 6px 14px -4px rgba(15, 23, 42, .15);
    display: flex; flex-direction: column;
    overflow: hidden;
    pointer-events: auto;
    animation: mail-compose-pop .22s cubic-bezier(.2,.8,.3,1.2);
}
@keyframes mail-compose-pop {
    from { opacity: 0; transform: translateY(20px) scale(.96); }
    to   { opacity: 1; transform: none; }
}
.mail-compose-shell.is-minimized .mail-compose-card {
    width: 320px;
    max-height: 48px;
    border-radius: 12px 12px 0 0;
    bottom: 0;
}
.mail-compose-shell.is-minimized .mail-compose-card-form,
.mail-compose-shell.is-minimized .mail-compose-shell-backdrop { display: none; }
.mail-compose-shell.is-maximized .mail-compose-card {
    inset: 24px;
    width: auto;
    max-height: none;
}

.mail-compose-card-head {
    flex: 0 0 auto;
    display: flex; align-items: center; justify-content: space-between;
    padding: .55rem .65rem .55rem .85rem;
    background: linear-gradient(135deg, color-mix(in srgb, var(--primary) 92%, #8b5cf6), #8b5cf6);
    color: #fff;
    cursor: default;
    user-select: none;
}
.mail-compose-card-title {
    display: inline-flex; align-items: center; gap: .5rem;
    font-size: .85rem; font-weight: 700;
    letter-spacing: -.005em;
}
.mail-compose-card-icon { opacity: .9; }
.mail-compose-card-actions { display: inline-flex; gap: 2px; }
.mail-compose-card-btn {
    width: 28px; height: 28px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent;
    border: 0;
    border-radius: 6px;
    color: rgba(255, 255, 255, .85);
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.mail-compose-card-btn:hover { background: rgba(255, 255, 255, .18); color: #fff; }
.mail-compose-card-btn-danger:hover { background: rgba(239, 68, 68, .85); color: #fff; }

.mail-compose-card-form {
    flex: 1 1 auto;
    overflow-y: auto;
    display: flex; flex-direction: column;
    padding: .85rem 1rem 1rem;
    gap: .55rem;
}
.mail-compose-card-from {
    display: flex; align-items: center; gap: .5rem;
    padding: .25rem 0 .55rem;
    border-bottom: 1px solid var(--surface-2);
    font-size: .82rem;
}
.mail-compose-card-from-label { color: var(--text-subtle); width: 50px; flex: 0 0 auto; font-weight: 500; }
.mail-compose-card-from-value { color: var(--text); font-weight: 500; }
.mail-compose-card-from-email { color: var(--text-muted); font-weight: 400; }

.mail-compose-card-row {
    display: flex; align-items: center; gap: .5rem;
    padding: .15rem 0;
    border-bottom: 1px solid var(--surface-2);
}
.mail-compose-card-row label {
    width: 50px; flex: 0 0 auto;
    color: var(--text-subtle);
    font-size: .82rem;
    font-weight: 500;
    margin: 0;
}
.mail-compose-card-row input[type="text"] {
    flex: 1 1 auto;
    border: 0;
    background: transparent;
    padding: .35rem 0;
    font-size: .9rem;
    color: var(--text);
    outline: none;
    min-width: 0;
}
.mail-compose-card-row input[type="text"]:focus { background: transparent; }
.mail-compose-card-row input::placeholder { color: var(--text-subtle); }
.mail-compose-card-cc-toggle {
    flex: 0 0 auto;
    background: transparent;
    border: 0;
    color: var(--text-muted);
    font-size: .76rem;
    font-weight: 600;
    padding: .25rem .5rem;
    border-radius: 6px;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.mail-compose-card-cc-toggle:hover { background: var(--surface-2); color: var(--primary); }

.mail-compose-card-editor {
    margin-top: .35rem;
    flex: 1 1 auto;
    min-height: 200px;
}
.mail-compose-card-editor .compose-body {
    min-height: 200px;
    max-height: 320px;
}

.mail-compose-card-attachments {
    display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
    margin-top: .25rem;
}
.mail-compose-card-attach-pick {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .35rem .65rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface);
    color: var(--text-muted);
    font-size: .78rem;
    font-weight: 500;
    cursor: pointer;
    transition: border-color .12s ease, color .12s ease, background .12s ease;
    margin: 0;
}
.mail-compose-card-attach-pick:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-bg); }
.mail-compose-card-attach-pick input[type="file"] { display: none; }
.mail-compose-card-attach-hint { color: var(--text-subtle); font-size: .76rem; }
.mail-compose-card-attach-list { display: flex; flex-wrap: wrap; gap: .35rem; flex: 1 1 100%; }
.mail-compose-card-attach-list:empty { display: none; }
.mail-compose-card-attach-chip {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .25rem .25rem .25rem .55rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: .76rem;
    color: var(--text);
}
.mail-compose-card-attach-chip-name { max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-compose-card-attach-chip-size { color: var(--text-muted); font-size: .7rem; }
.mail-compose-card-attach-chip-remove {
    width: 18px; height: 18px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 0;
    background: transparent;
    color: var(--text-muted);
    border-radius: 50%;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.mail-compose-card-attach-chip-remove:hover { background: rgba(239, 68, 68, .15); color: #b91c1c; }

.mail-compose-card-validation {
    color: #b91c1c;
    background: color-mix(in srgb, #b91c1c 8%, var(--surface));
    border: 1px solid color-mix(in srgb, #b91c1c 28%, var(--border));
    border-radius: 8px;
    padding: .5rem .65rem;
    font-size: .8rem;
}

.mail-compose-card-foot {
    display: flex; align-items: center; justify-content: space-between;
    gap: .85rem;
    padding-top: .5rem;
    border-top: 1px solid var(--surface-2);
    margin-top: .25rem;
}
.mail-compose-card-hint { flex: 1 1 auto; min-width: 0; color: var(--text-muted); }
.mail-compose-card-foot-actions { flex: 0 0 auto; display: inline-flex; gap: .35rem; }
.mail-compose-card-foot-actions .btn-primary {
    display: inline-flex; align-items: center;
}
.mail-compose-card-foot-actions .btn.is-sending {
    /* In-flight send: the spinner inside replaces the icon, the label
       reads "Sending…", the button stays in a clearly-active visual
       (no greyed-out cancel-look) so the user reads it as "your send
       is in progress" not "something failed". */
    opacity: 1;
    cursor: progress;
    background: color-mix(in srgb, var(--primary) 88%, #000);
    pointer-events: none;
}
.mail-compose-card-foot-actions .btn.is-sending .bulk-spin {
    border-top-color: #fff;
    border-color: rgba(255, 255, 255, .35);
    border-top-color: #fff;
}

@media (max-width: 720px) {
    .mail-compose-card { right: 0; bottom: 0; width: 100%; border-radius: 14px 14px 0 0; max-height: 90vh; }
}
.mail-compose-card-row.is-invalid input { color: #b91c1c; }

/* ── "Select all matching the filter" banner ════════════════════════
   Sits between the bulk-bar and the message list. Shows up when every
   row on the page is ticked AND the matching set is bigger than the
   page (so there's actually more to select). Visual is borrowed from
   Gmail / Superhuman: a slim tinted strip with centered copy, bold
   primary link, no harsh borders. Has two states:
     - normal  : "All N on this page are selected. <Select all M …>"
     - extended: "All M in Folder are selected. <Clear selection>"
   The extended variant uses a green tint to flag that the action will
   target far more than the visible page.

   CLS guard: the slot keeps a FIXED height regardless of state. When
   the banner has nothing to say it stays in flow with visibility:hidden
   so the message list never shifts when content appears or changes
   between the two banner phrasings. */
.bulk-banner {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: nowrap;            /* don't grow vertically when text is long */
    gap: .55rem;
    padding: 0 1rem;
    height: 36px;                  /* fixed - same across all states */
    box-sizing: border-box;
    background: var(--primary-bg);
    border-bottom: 1px solid color-mix(in srgb, var(--primary) 22%, var(--surface-2));
    font-size: .85rem;
    line-height: 1.2;
    color: var(--text);
    text-align: center;
    overflow: hidden;
}
/* Override UA's [hidden] default of display:none - the slot must keep
   its 36px so toggling on/off doesn't push the message list. */
.bulk-banner[hidden] {
    display: flex;
    visibility: hidden;
    background: transparent;
    border-bottom-color: var(--surface-2);
}
.bulk-banner-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.bulk-banner-text {
    color: var(--text);
    font-weight: 500;
}
.bulk-banner-action {
    background: transparent;
    border: 0;
    padding: 0;
    margin: 0;
    color: var(--primary);
    font-weight: 700;
    font-size: inherit;
    cursor: pointer;
    text-decoration: none;
    transition: color .12s ease, text-decoration-color .12s ease;
    text-decoration: underline;
    text-decoration-color: transparent;
    text-underline-offset: 3px;
    text-decoration-thickness: 2px;
}
.bulk-banner-action:hover,
.bulk-banner-action:focus-visible {
    color: var(--primary);
    text-decoration-color: var(--primary);
    outline: 0;
}
/* Extended state - the user already opted into "all matching"; the tint
   shifts to a "this is a heavy operation" green so it doesn't look
   identical to the suggestion banner. */
.bulk-banner.is-extended {
    background: color-mix(in srgb, #15803d 12%, var(--surface));
    border-bottom-color: color-mix(in srgb, #15803d 28%, var(--surface-2));
}
.bulk-banner.is-extended .bulk-banner-text { color: #14532d; }
.bulk-banner.is-extended .bulk-banner-action {
    color: #15803d;
}
.bulk-banner.is-extended .bulk-banner-action:hover,
.bulk-banner.is-extended .bulk-banner-action:focus-visible {
    color: #166534;
    text-decoration-color: #166534;
}
@keyframes bulk-banner-in {
    from { opacity: 0; transform: translateY(-3px); }
    to   { opacity: 1; transform: none; }
}
html[data-theme="dark"] .bulk-banner {
    background: color-mix(in srgb, var(--primary) 14%, var(--surface));
}
html[data-theme="dark"] .bulk-banner.is-extended {
    background: color-mix(in srgb, #15803d 18%, var(--surface));
}
html[data-theme="dark"] .bulk-banner.is-extended .bulk-banner-text { color: #bbf7d0; }
html[data-theme="dark"] .bulk-banner.is-extended .bulk-banner-action { color: #4ade80; }
@media (max-width: 720px) {
    .bulk-banner { padding: .55rem .75rem; font-size: .8rem; }
}

/* ==== "Label as" popover anchored to the bulk-bar button ============== */
.bulk-label-wrap { position: relative; display: inline-flex; }
.label-pop {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 60;
    width: 280px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 12px 32px -8px rgba(15,23,42,.2), 0 4px 8px -2px rgba(15,23,42,.08);
    padding: 0;
    animation: label-pop-in .12s ease;
    display: flex; flex-direction: column;
    max-height: 380px;
    overflow: hidden;
}
@keyframes label-pop-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.label-pop[hidden] { display: none; }
.label-pop-head {
    flex: 0 0 auto;
    padding: .55rem .55rem .4rem;
    border-bottom: 1px solid var(--surface-2);
}
.label-pop-search {
    width: 100%;
    padding: .4rem .55rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: .85rem;
    color: var(--text);
    background: var(--surface);
    outline: 0;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.label-pop-search:focus { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-bg); }
.label-pop-list {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: .25rem .25rem;
    min-height: 60px;
}
.label-pop-item {
    display: flex; align-items: center; gap: .55rem;
    padding: .4rem .55rem;
    border-radius: 6px;
    cursor: pointer;
    font-size: .85rem;
    color: var(--text);
    user-select: none;
    transition: background .08s ease;
}
.label-pop-item:hover { background: var(--surface-2); }
.label-pop-item input[type="checkbox"] {
    width: 14px; height: 14px;
    accent-color: var(--primary);
    cursor: pointer;
    flex: 0 0 auto;
}
.label-pop-item .label-pop-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.label-pop-item .label-pop-state {
    font-size: .68rem; font-weight: 600;
    color: var(--text-muted);
    flex: 0 0 auto;
    text-transform: uppercase;
    letter-spacing: .03em;
}
.label-pop-item.is-some .label-pop-state { color: var(--primary); }
.label-pop-item.is-pending-add { background: var(--primary-bg); }
.label-pop-item.is-pending-add .label-pop-state { color: var(--primary); }
.label-pop-item.is-pending-add .label-pop-state::before { content: "+ "; }
.label-pop-item.is-pending-remove .label-pop-state { color: #b91c1c; }
.label-pop-item.is-pending-remove .label-pop-state::before { content: "− "; }
/* Brief highlight for a label the user just created via "+ Create label".
   Pulses the row's outline so the eye lands on its (alphabetical)
   resting place after the auto-scroll. */
.label-pop-item.is-just-created {
    animation: label-pop-flash 1.4s ease;
}
@keyframes label-pop-flash {
    0% { box-shadow: inset 0 0 0 2px var(--primary); background: color-mix(in srgb, var(--primary) 18%, transparent); }
    60% { box-shadow: inset 0 0 0 2px var(--primary); background: var(--primary-bg); }
    100% { box-shadow: inset 0 0 0 0 transparent; }
}
.label-pop-empty {
    padding: 1.2rem .8rem;
    text-align: center;
    color: var(--text-muted);
    font-size: .8rem;
    line-height: 1.4;
}
.label-pop-create {
    flex: 0 0 auto;
    border-top: 1px solid var(--surface-2);
    padding: .35rem;
}
.label-pop-create-btn {
    width: 100%;
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .45rem .55rem;
    border: 0;
    background: transparent;
    color: var(--primary);
    border-radius: 6px;
    cursor: pointer;
    font-size: .82rem; font-weight: 600;
    text-align: left;
    transition: background .08s ease;
}
.label-pop-create-btn:hover { background: var(--primary-bg); }
.label-pop-create-btn strong { font-weight: 700; }
.label-pop-foot {
    flex: 0 0 auto;
    padding: .5rem;
    border-top: 1px solid var(--surface-2);
    display: flex; gap: .4rem; justify-content: flex-end;
    background: linear-gradient(to top, var(--surface-2), var(--surface));
}
/* The action toolbar can scroll horizontally on narrow screens; the
   popover should stay anchored visually even then. */
@media (max-width: 820px) {
    .label-pop { width: 260px; }
}

.pagination-controls {
    display: flex;
    align-items: center;
    gap: 2px;
    flex: 1 1 auto;
    justify-content: center;
    flex-wrap: wrap;
}
.pagination-controls .pg-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 32px;
    height: 32px;
    padding: 0 .55rem;
    font-size: .82rem;
    font-weight: 500;
    color: var(--text);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 8px;
    text-decoration: none;
    transition: all .1s ease;
    cursor: pointer;
    user-select: none;
}
.pagination-controls .pg-btn:hover {
    background: var(--surface-2);
    border-color: var(--border);
    color: var(--text);
}
.pagination-controls .pg-btn.active {
    background: var(--primary);
    color: #fff;
    font-weight: 600;
    border-color: var(--primary);
    box-shadow: 0 1px 2px rgba(79,70,229,.25);
}
.pagination-controls .pg-btn.active:hover {
    background: var(--primary-hover);
    border-color: var(--primary-hover);
    color: #fff;
}
.pagination-controls .pg-btn.disabled,
.pagination-controls .pg-btn[aria-disabled="true"] {
    opacity: .35;
    pointer-events: none;
    cursor: default;
}
.pagination-controls .pg-ellipsis {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 24px;
    height: 32px;
    color: var(--text-subtle);
    font-size: .9rem;
    padding: 0 .15rem;
    user-select: none;
}
.pagination-controls .pg-btn[title="First page"],
.pagination-controls .pg-btn[title="Last page"],
.pagination-controls .pg-btn[title="Previous"],
.pagination-controls .pg-btn[title="Next"] {
    color: var(--text-muted);
    font-weight: 500;
    padding: 0 .7rem;
}
.pagination-controls .pg-btn[title="Previous"],
.pagination-controls .pg-btn[title="Next"] {
    padding: 0 .9rem;
}

.pagination-jump {
    display: flex;
    align-items: center;
    gap: .4rem;
    font-size: .82rem;
    color: var(--text-muted);
    flex: 0 0 auto;
}
.pagination-jump label { margin: 0; color: var(--text-muted); }
.pagination-jump input {
    width: 56px;
    height: 30px;
    padding: 0 .45rem;
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    font-size: .82rem;
    font-variant-numeric: tabular-nums;
    color: var(--text);
    background: var(--surface);
    text-align: center;
}
.pagination-jump input:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px rgba(79,70,229,.15);
}
.pagination-jump .pg-of { color: var(--text-muted); font-variant-numeric: tabular-nums; }

@media (max-width: 720px) {
    .pagination-bar { justify-content: center; }
    .pagination-range, .pagination-jump { flex: 1 1 100%; justify-content: center; text-align: center; }
}

/* Message view */
.msg-header { padding: 1.25rem 1.5rem; border-bottom: 1px solid var(--border); background: var(--surface); }
.msg-subject { font-size: 1.25rem; font-weight: 700; margin-bottom: .5rem; }
.msg-meta { font-size: .825rem; color: var(--text-muted); }
.msg-body { padding: 1.5rem; }
.msg-attachments { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: 1rem; }
.msg-attachment-chip {
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .5rem .85rem;
    background: var(--surface-2);
    border-radius: 8px;
    font-size: .825rem;
    color: var(--text);
    text-decoration: none;
    border: 1px solid var(--border);
}
.msg-attachment-chip:hover { background: var(--primary-bg); color: var(--primary); }

/* Modal */
.modal-content { border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow); }
.modal-header { border-bottom: 1px solid var(--border); }
.modal-footer { border-top: 1px solid var(--border); }

/* ======================================================================
   Collaborators page
   ====================================================================== */
.collab-page { max-width: 860px; margin: 0 auto; }

/* Header row ---------------------------------------------------------- */
.collab-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.25rem;
}
.collab-header-text { min-width: 0; }
.collab-header-sep { opacity: 0.4; margin: 0 0.2rem; }

/* Empty-state hero (user has no workspaces yet) ---------------------- */
.collab-empty-hero { text-align: center; margin-top: 2rem; }
.collab-empty-hero .card-body { padding: 3rem 2rem; }
/* Empty-state hero icon shares the .collab-invite-icon paint
   surface (so the per-accent tint kicks in via
   .collab-panel-accent--violet .collab-invite-icon) but is
   bigger because it's a hero. Width/height + svg size are the
   only deltas; colour comes from the accent class. */
.collab-hero-icon {
    width: 64px !important; height: 64px !important;
    border-radius: 16px !important;
    margin-bottom: 1rem;
}
.collab-hero-icon svg { width: 32px; height: 32px; }
.collab-empty-hero h2 { margin: 0.25rem 0 0.5rem; font-size: 1.4rem; font-weight: 600; }
.collab-empty-hero p { color: var(--muted); max-width: 420px; margin: 0 auto 1.25rem; }

/* Invite panel -------------------------------------------------------- */
.collab-invite-panel { margin-bottom: 1.5rem; }

/* "Add a teammate to your teams" panel - inherits the invite
   panel's typography + spacing so the two surfaces feel like a
   pair. The teammate <select> uses the standard form-select
   width-cap so a long display name doesn't blow out the column. */
.collab-team-panel { margin-bottom: 1.5rem; }
/* Match the compact-invite-panel padding so the two cards line
   up vertically. Previously the team panel used wider padding
   which made it look narrower than the invite panel above it. */
.collab-team-panel .card-body { padding: 1.25rem 1.5rem; }
.collab-team-panel .collab-invite-lede { margin-bottom: 1rem; gap: 0.75rem; }
.collab-team-panel .collab-invite-icon { width: 36px; height: 36px; border-radius: 10px; }
.collab-team-panel .collab-invite-icon svg { width: 18px; height: 18px; }
.collab-team-panel .collab-invite-text h2 { font-size: 1.05rem; }
.collab-team-panel .collab-invite-text p { font-size: 0.88rem; }
/* Same flex-column-gap rhythm as the invite form so the two
   surfaces have identical vertical pacing between fields and
   the submit row. The team form previously used per-field
   margin-tops, which produced a slightly different rhythm
   that read as "broken" against the invite panel. */
.collab-team-form { display: flex; flex-direction: column; gap: 1rem; }

/* Tab strip on the unified invite panel. Two segmented buttons
   (Share workspaces / Add to teams) swap which picker is visible
   while keeping both checklists inside the same form - a single
   submit can carry checked items from BOTH tabs. The active tab
   gets a brand-color underline + the count badge highlights when
   any items are picked, so the user sees the "0 picked / 2 picked"
   state without flipping tabs. */
.collab-tabs {
    display: inline-flex;
    align-items: stretch;
    gap: .25rem;
    padding: .25rem;
    background: var(--surface-2);
    border-radius: 999px;
    align-self: flex-start;
}
.collab-tab {
    display: inline-flex; align-items: center; gap: .45rem;
    padding: .4rem .85rem;
    border: 0;
    background: transparent;
    color: var(--text-muted);
    font-size: .85rem; font-weight: 500;
    border-radius: 999px;
    cursor: pointer;
    transition: background .15s ease, color .15s ease;
}
.collab-tab:hover { color: var(--text); }
.collab-tab.is-active {
    background: var(--surface);
    color: var(--text);
    box-shadow: 0 1px 3px rgba(15, 23, 42, .08);
}
.collab-tab svg { color: currentColor; flex: 0 0 auto; }
.collab-tab-count {
    display: none;
    align-items: center; justify-content: center;
    min-width: 18px; height: 18px;
    padding: 0 5px;
    background: var(--primary);
    color: #fff;
    font-size: .68rem; font-weight: 700;
    border-radius: 999px;
    border: 1px solid var(--primary);
    font-variant-numeric: tabular-nums;
}
/* Only render the badge when at least one item is picked. The
   "0" hollow circle on the inactive tab read as an empty bug
   indicator rather than a count. */
.collab-tab-count.is-on { display: inline-flex; }
.collab-tab.is-active .collab-tab-count.is-on {
    box-shadow: 0 0 0 2px var(--primary-bg);
}

/* Tab panels: only the active one is rendered. The hidden
   panel's children (the picker checkboxes) stay in the DOM so
   their state survives a tab flip + a single form submit
   carries everything - tabs are pure visual filtering. */
.collab-tab-panel { display: none; }
.collab-tab-panel.is-active { display: block; }

/* ============================================================
   Collaborators page polish (moves 2 + 3 + 5).
   2: activity pulse moved into the People-section header
   3: stat ribbon under the page title
   5: tighter Invite-card chrome
   ============================================================ */

/* Stat ribbon. Mirrors the home-hero asymmetric stats - Plus
   Jakarta Sans ExtraBold numbers above small-caps labels with
   a 2px brand-color underline per tile. Replaces the prose
   subtitle when there's something concrete to count. */
.collab-stats {
    display: inline-flex; flex-wrap: wrap;
    gap: 1.5rem;
    margin-top: .5rem;
}
.collab-stat {
    display: inline-flex; flex-direction: column;
    gap: .15rem;
    padding: .15rem 0 .45rem;
    border-bottom: 2px solid var(--border);
    min-width: 4rem;
}
.collab-stat-cyan   { border-bottom-color: #06B6D4; }
.collab-stat-violet { border-bottom-color: #8B5CF6; }
.collab-stat-pink   { border-bottom-color: #EC4899; }
.collab-stat-amber  { border-bottom-color: #F59E0B; }
.collab-stat-value {
    font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
    font-weight: 800;
    font-size: 1.75rem;
    letter-spacing: -.03em;
    line-height: 1;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}
.collab-stat-label {
    font-size: .72rem;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: .06em;
}
.collab-stat { border-bottom-width: 3px; }

/* Activity pulse inside the People-section header. Pure text
   lines with a small status indicator on the left - no pill,
   no tinted background, no Lucide icon. Pending gets a small
   pulsing dot in the brand violet (or red when critical);
   acceptance gets the actor's avatar bubble. Reads as a
   newsroom-style status line, not a generic SaaS badge. */
.coworker-pulse {
    display: inline-flex;
    flex-wrap: wrap;
    gap: .85rem;
    margin-left: auto;
    align-items: center;
    font-size: .82rem;
    color: var(--text-muted);
    line-height: 1.4;
}
/* Vertical separator between adjacent pulse lines so the two
   strips don't read as one run-on sentence. */
.coworker-pulse-line + .coworker-pulse-line {
    padding-left: .85rem;
    border-left: 1px solid var(--border);
}
.coworker-pulse-line {
    display: inline-flex; align-items: center;
    gap: .5rem;
    max-width: 30rem;
    overflow: hidden;
    background: transparent;
    border: 0;
    padding: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    text-align: left;
}
.coworker-pulse-line:hover .coworker-pulse-text strong { color: var(--primary); }
.coworker-pulse-line[disabled],
.coworker-pulse-line:disabled { cursor: default; }
.coworker-pulse-line[disabled]:hover .coworker-pulse-text strong,
.coworker-pulse-line:disabled:hover .coworker-pulse-text strong { color: var(--text); }
.coworker-pulse-text {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    min-width: 0;
}
.coworker-pulse-line strong {
    color: var(--text);
    font-weight: 600;
}
.coworker-pulse-sep {
    color: var(--text-subtle);
    flex: 0 0 auto;
}
.coworker-pulse-meta {
    color: var(--text-subtle);
    flex: 0 0 auto;
}

/* Pending: a small filled dot in the brand violet, with a
   subtle pulse animation so the eye catches it. The halo is
   inside the dot itself (a 14px circle with a 6px filled
   centre via a radial gradient) so it never clips against an
   `overflow: hidden` ancestor like the panel-accent rail. */
.coworker-pulse-line .coworker-pulse-dot {
    width: 14px; height: 14px;
    border-radius: 50%;
    background: radial-gradient(circle,
        var(--primary, #8B5CF6) 0,
        var(--primary, #8B5CF6) 3px,
        color-mix(in srgb, var(--primary, #8B5CF6) 22%, transparent) 4px,
        color-mix(in srgb, var(--primary, #8B5CF6) 0%, transparent) 7px);
    flex: 0 0 auto;
    animation: coworker-pulse 2.4s ease-in-out infinite;
}
.coworker-pulse-line.is-critical .coworker-pulse-dot {
    background: radial-gradient(circle,
        #DC2626 0, #DC2626 3px,
        rgba(220, 38, 38, .25) 4px,
        rgba(220, 38, 38, 0) 7px);
}
.coworker-pulse-line.is-critical strong { color: #B91C1C; }
@keyframes coworker-pulse {
    0%, 100% { transform: scale(1); opacity: 1; }
    50%      { transform: scale(.85); opacity: .7; }
}
@media (prefers-reduced-motion: reduce) {
    .coworker-pulse-line .coworker-pulse-dot { animation: none; }
}

/* Acceptance: avatar-led, no dot, no green tint. The actor's
   real avatar anchors the line - feels personal, not generic.
   The avatar component (Html.UserAvatar) renders an <img> when
   the user has uploaded one, falling back to a coloured initial
   bubble. Either way our class sets the size + ring. */
.coworker-pulse-line.is-good { gap: .5rem; }
.coworker-pulse-avatar {
    width: 22px !important;
    height: 22px !important;
    border-radius: 50% !important;
    flex: 0 0 auto;
    font-size: .68rem !important;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .12);
    object-fit: cover;
}
/* The surface name in the acceptance line is a real link -
   click jumps to /Teams#team-{id} for teams or /Mail/{id} for
   workspaces. Keep the underline subtle so the line still reads
   as one sentence, not a wall of links. */
.coworker-pulse-link {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: border-color .15s ease, color .15s ease;
}
.coworker-pulse-link:hover {
    color: var(--primary);
    border-bottom-color: currentColor;
}
.coworker-pulse-link strong { color: inherit; }

/* Tighter Invite card chrome. Replace the lede paragraph (~80px
   of vertical real estate for two sentences that just restated
   the field labels) with a compact head: H2 + a one-line muted
   sub on the same row when there's space, wrapping below on
   narrow viewports. Form gap also tightened. */
.collab-invite-head {
    display: flex; flex-wrap: wrap;
    align-items: baseline; gap: .65rem;
    margin-bottom: .85rem;
}
.collab-invite-head h2 {
    margin: 0;
    font-size: 1.05rem; font-weight: 700;
    color: var(--text);
    letter-spacing: -.01em;
}
.collab-invite-sub {
    flex: 1 1 16rem;
    font-size: .82rem;
    color: var(--text-muted);
    line-height: 1.5;
}
.collab-invite-form { gap: .85rem; }
.collab-team-form { gap: .85rem; }

/* People-list toolbar: search input + sort selector. Sits in
   the section header band so the filter feels attached to the
   list, not to the page. Hidden when there's only one
   coworker (Razor gates the markup at ≥2). */
.coworker-toolbar {
    display: flex; align-items: center; gap: .65rem;
    padding: .65rem 1rem;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}
.coworker-search {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
}
.coworker-search svg {
    position: absolute;
    left: .65rem; top: 50%;
    transform: translateY(-50%);
    color: var(--text-subtle);
    pointer-events: none;
}
.coworker-search-input {
    width: 100%;
    padding: .45rem .75rem .45rem 2rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    color: var(--text);
    font-size: .87rem;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.coworker-search-input:focus {
    outline: 0;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-bg);
}
.coworker-sort {
    display: inline-flex; align-items: center; gap: .4rem;
    flex: 0 0 auto;
    font-size: .8rem;
    color: var(--text-muted);
}
.coworker-sort-label { font-weight: 500; }
.coworker-sort .form-select {
    width: auto;
    padding: .35rem 1.85rem .35rem .65rem;
    font-size: .82rem;
    border-radius: 8px;
}
.coworker-empty-filter {
    padding: 1.25rem 1rem;
    text-align: center;
    font-size: .88rem;
    color: var(--text-muted);
}

/* Pending-invitations modal list. One row per outstanding
   invitation with a kind-glyph, the invitee email + target
   surface, an expiry note, and a cancel button. Same row
   pattern as the workspace and team pickers above so the
   page feels coherent. */
.pending-invite-list {
    list-style: none;
    margin: 0; padding: 0;
    display: flex; flex-direction: column;
    gap: .5rem;
}
.pending-invite-row {
    display: flex; align-items: center;
    gap: .85rem;
    padding: .65rem .8rem;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
}
.pending-invite-kind {
    width: 28px; height: 28px;
    border-radius: 8px;
    display: inline-flex; align-items: center; justify-content: center;
    flex: 0 0 auto;
}
.pending-invite-kind-workspace {
    background: rgba(6, 182, 212, .14);
    color: #0891B2;
}
.pending-invite-kind-team {
    background: rgba(236, 72, 153, .14);
    color: #DB2777;
}
html[data-theme="dark"] .pending-invite-kind-workspace { color: #22D3EE; }
html[data-theme="dark"] .pending-invite-kind-team      { color: #F472B6; }
.pending-invite-text {
    flex: 1 1 auto;
    min-width: 0;
    display: flex; flex-direction: column;
    gap: .2rem;
}
/* Headline row: a small kind-tag pill ("Workspace access" /
   "Team membership") + the target's display name. The pill is
   the unambiguous "what kind of invitation" signal so the user
   never has to guess from icons alone. */
.pending-invite-headline {
    display: flex; align-items: center; gap: .5rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pending-invite-kindlabel {
    display: inline-flex; align-items: center;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: .65rem; font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    flex: 0 0 auto;
}
.pending-invite-kindlabel-workspace {
    background: rgba(6, 182, 212, .14);
    color: #0E7490;
}
.pending-invite-kindlabel-team {
    background: rgba(236, 72, 153, .14);
    color: #BE185D;
}
html[data-theme="dark"] .pending-invite-kindlabel-workspace { color: #67E8F9; }
html[data-theme="dark"] .pending-invite-kindlabel-team      { color: #F9A8D4; }
.pending-invite-target {
    font-weight: 600;
    color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    min-width: 0;
}
.pending-invite-line {
    font-size: .87rem;
    color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pending-invite-line strong {
    font-weight: 600;
    color: var(--text);
}
.pending-invite-meta {
    font-size: .75rem;
    color: var(--text-subtle);
    display: inline-flex; align-items: center;
    gap: .35rem;
}
.pending-invite-sep { color: var(--text-subtle); opacity: .6; }
.pending-invite-meta .is-critical {
    color: #B91C1C;
    font-weight: 600;
}
html[data-theme="dark"] .pending-invite-meta .is-critical { color: #FCA5A5; }

/* Two-line helper under each chip input. Line 1 spells out the
   commit keys (Enter/Tab/,); line 2 covers the suggestion path.
   Splitting into two lines reads faster than the previous prose
   sentence and makes the multi-add flow obvious. */
.chip-help {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    line-height: 1.5;
}
.chip-help kbd {
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: .68rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 1px 5px;
    color: var(--text);
    box-shadow: 0 1px 0 var(--border);
    margin: 0 .1rem;
}
.collab-team-picker {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: .55rem;
    margin-top: .35rem;
}
/* Team-pick row: same visual contract as `.collab-ws-option`
   (the workspace picker above it) so the two grids feel like
   the same control with different targets. Inherit its layout
   (flex + gap + hidden native input + custom .collab-ws-check
   SVG) verbatim - the only delta is the `is-already-member`
   state, which dims a row whose checkbox the JS turned off
   because the picked teammate is already in that team. */
.collab-team-option {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.7rem 0.85rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    cursor: pointer;
    transition: background-color 120ms ease, border-color 120ms ease, opacity 120ms ease;
    user-select: none;
    position: relative;
}
.collab-team-option:hover {
    border-color: var(--primary);
    background: var(--primary-bg);
}
.collab-team-option input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.collab-team-option:has(input:checked) {
    border-color: var(--primary);
    background: var(--primary-bg);
}
.collab-team-option:has(input:checked) .collab-ws-check {
    background: var(--primary);
    border-color: var(--primary);
}
.collab-team-option:has(input:checked) .collab-ws-check svg { opacity: 1; }
.collab-team-option.is-already-member {
    opacity: .55;
    cursor: not-allowed;
    background: var(--surface-2);
    border-color: var(--border);
}
.collab-team-option.is-already-member:hover {
    border-color: var(--border);
    background: var(--surface-2);
}
.collab-team-option.is-already-member .collab-ws-hint {
    color: var(--text-subtle);
    font-style: italic;
}
.collab-invite-panel.is-hero .card-body { padding: 2rem 2.2rem; }
.collab-invite-panel.is-compact .card-body { padding: 1.25rem 1.5rem; }

.collab-invite-lede {
    display: flex; gap: 1rem; align-items: flex-start;
    margin-bottom: 1.5rem;
}
.collab-invite-panel.is-compact .collab-invite-lede { margin-bottom: 1rem; gap: 0.75rem; }

.collab-invite-icon {
    width: 44px; height: 44px;
    border-radius: 12px;
    background: var(--surface-2);
    color: var(--text);
    display: inline-flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    transition: transform .25s cubic-bezier(.34, 1.56, .64, 1);
}
.card:hover > .card-body > .collab-invite-lede .collab-invite-icon {
    transform: rotate(-3deg) scale(1.04);
}

/* Per-section brand colour tints. The Collaborators page now has
   THREE sections (People you work with · Invite a collaborator ·
   Invite someone to your teams) and the Burofolk wordmark has
   THREE bars (cyan / violet / pink). Mapping one brand colour
   per section gives the page a coherent visual rhythm and gets
   rid of the heavy violet "filled" icons the user flagged as
   ugly. Soft tinted background + matching deeper stroke colour;
   no gradients, no white-on-violet glare. */
.collab-panel-accent--cyan   .collab-invite-icon { background: rgba(6, 182, 212, .12);  color: #0891B2; }
.collab-panel-accent--violet .collab-invite-icon { background: rgba(139, 92, 246, .12); color: #7C3AED; }
.collab-panel-accent--pink   .collab-invite-icon { background: rgba(236, 72, 153, .12); color: #DB2777; }
html[data-theme="dark"] .collab-panel-accent--cyan   .collab-invite-icon { color: #22D3EE; }
html[data-theme="dark"] .collab-panel-accent--violet .collab-invite-icon { color: #A78BFA; }
html[data-theme="dark"] .collab-panel-accent--pink   .collab-invite-icon { color: #F472B6; }

/* Thin coloured rail along the top of each accented card so the
   identity colour is visible at a glance even when the lede icon
   is scrolled out of view. */
.collab-panel-accent {
    position: relative;
    overflow: hidden;
}
.collab-panel-accent::before {
    content: "";
    position: absolute;
    left: 0; top: 0; right: 0;
    height: 3px;
    z-index: 1;
}
.collab-panel-accent--cyan::before   { background: #06B6D4; }
.collab-panel-accent--violet::before { background: #8B5CF6; }
.collab-panel-accent--pink::before   { background: #EC4899; }
.collab-invite-icon svg { width: 22px; height: 22px; }
.collab-invite-panel.is-compact .collab-invite-icon { width: 36px; height: 36px; border-radius: 10px; }
.collab-invite-panel.is-compact .collab-invite-icon svg { width: 18px; height: 18px; }

.collab-invite-text { flex: 1 1 auto; min-width: 0; }
.collab-invite-text h2 {
    margin: 0 0 0.25rem;
    font-size: 1.2rem;
    font-weight: 600;
    color: var(--text);
}
.collab-invite-text p { margin: 0; color: var(--muted); font-size: 0.93rem; line-height: 1.5; }
.collab-invite-panel.is-compact .collab-invite-text h2 { font-size: 1.05rem; }
.collab-invite-panel.is-compact .collab-invite-text p { font-size: 0.88rem; }

/* Chip-based invitee picker -------------------------------------------- */
.chip-input {
    position: relative;
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem;
    min-height: 48px;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    cursor: text;
    transition: border-color 120ms ease, box-shadow 120ms ease;
}
.chip-input.is-focused { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15); }
.chip-input.is-invalid { border-color: #ef4444; animation: chip-shake 240ms ease; }
@keyframes chip-shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-3px); }
    75% { transform: translateX(3px); }
}
.chip-input-chips { display: inline-flex; flex-wrap: wrap; gap: 0.3rem; }
.chip {
    display: inline-flex; align-items: center; gap: 0.35rem;
    padding: 0.2rem 0.35rem 0.2rem 0.25rem;
    border-radius: 999px;
    background: var(--primary-bg);
    color: var(--primary);
    font-size: 0.82rem;
    line-height: 1.3;
    max-width: 280px;
}
.chip-avatar {
    width: 20px; height: 20px; border-radius: 50%;
    background: var(--primary);
    color: #fff;
    font-size: 0.68rem; font-weight: 700;
    display: inline-flex; align-items: center; justify-content: center;
    flex: 0 0 auto;
}
.chip-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
/* Team chip variant: same pill geometry but a richer slug + dual-line
   readout (name + member count). Visually distinct from a user chip
   so the user sees at a glance which entries are individuals and
   which are whole teams. */
.chip.chip-team {
    background: color-mix(in srgb, var(--primary) 14%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--primary) 30%, var(--border));
    padding-right: .55rem;
    max-width: 360px;
}
.chip-team .chip-team-icon {
    background: var(--primary);
    color: #fff;
    width: 22px; height: 22px;
    display: inline-flex; align-items: center; justify-content: center;
}
.chip-team .chip-team-icon svg { width: 12px; height: 12px; }
.chip-team-count {
    margin-left: .35rem;
    padding: 0 .4rem;
    font-size: .65rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase;
    background: color-mix(in srgb, var(--primary) 20%, var(--surface));
    color: var(--primary);
    border-radius: 999px;
    border: 1px solid color-mix(in srgb, var(--primary) 35%, var(--border));
}
.chip-remove {
    background: transparent; border: none; cursor: pointer;
    color: var(--primary);
    font-size: 1rem; line-height: 1; padding: 0 0.25rem;
    opacity: 0.55;
}
.chip-remove:hover { opacity: 1; }

.chip-input-entry {
    flex: 1 1 160px;
    min-width: 140px;
    border: none; outline: none; background: transparent;
    font-size: 0.95rem; padding: 0.35rem 0.25rem;
    color: var(--text);
}

/* Suggestion dropdown: anchors to the input via position: absolute. */
.chip-suggest {
    position: absolute;
    top: calc(100% + 6px);
    left: 0; right: 0;
    max-height: 260px;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
    z-index: 30;
    padding: 0.25rem;
}
.chip-suggest-item {
    display: flex; align-items: center; gap: 0.65rem;
    padding: 0.45rem 0.6rem;
    border-radius: 8px;
    cursor: pointer;
}
.chip-suggest-item.is-active,
.chip-suggest-item:hover { background: var(--primary-bg); }
.chip-suggest-avatar {
    width: 28px; height: 28px; border-radius: 50%;
    background: var(--avatar-bg);
    color: var(--avatar-color);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 600; font-size: 0.78rem;
    flex: 0 0 auto;
}
.chip-suggest-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.chip-suggest-label { font-weight: 600; color: var(--text); font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip-suggest-sub { color: var(--muted); font-size: 0.78rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Team row in the suggester. Same shape as a user row but the avatar
   slot is a team-icon glyph instead of a person photo, and a small
   "team" tag sits next to the name so the user knows picking expands
   to all members. */
.chip-suggest-item.is-team .chip-suggest-team-icon {
    background: var(--primary-bg); color: var(--primary);
    display: inline-flex; align-items: center; justify-content: center;
}
.chip-suggest-team-tag {
    margin-left: .4rem;
    font-size: .62rem; font-weight: 600;
    color: var(--primary); background: var(--primary-bg);
    padding: 1px 6px; border-radius: 999px;
    letter-spacing: .04em; text-transform: uppercase;
    vertical-align: middle;
}

/* Form fields --------------------------------------------------------- */
.collab-invite-form { display: flex; flex-direction: column; gap: 1rem; }
.collab-field { display: flex; flex-direction: column; gap: 0.45rem; }
.collab-field > label,
.collab-field-label-row {
    font-weight: 500;
    font-size: 0.88rem;
    color: var(--text);
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.collab-field .form-control { border-radius: 10px; }
.collab-field .form-control-lg { font-size: 1rem; padding: 0.7rem 0.9rem; }

.collab-pick-all {
    background: none;
    border: none;
    padding: 0;
    font-size: 0.8rem;
    color: var(--primary);
    cursor: pointer;
    font-weight: 500;
}
.collab-pick-all:hover { text-decoration: underline; }

/* Workspace picker ---------------------------------------------------- */
.collab-workspace-picker {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
.collab-ws-option {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.7rem 0.85rem;
    border: 1px solid var(--border);
    border-radius: 10px;
    cursor: pointer;
    transition: background-color 120ms ease, border-color 120ms ease, transform 120ms ease;
    user-select: none;
    position: relative;
}
.collab-ws-option:hover {
    border-color: var(--primary);
    background: var(--primary-bg);
}
.collab-ws-option input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.collab-ws-check {
    width: 20px; height: 20px;
    border: 2px solid var(--border);
    border-radius: 6px;
    background: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    transition: background-color 120ms ease, border-color 120ms ease;
    color: #fff;
}
.collab-ws-check svg { width: 12px; height: 12px; opacity: 0; transition: opacity 120ms ease; }
.collab-ws-option:has(input:checked) { border-color: var(--primary); background: var(--primary-bg); }
.collab-ws-option:has(input:checked) .collab-ws-check { background: var(--primary); border-color: var(--primary); }
.collab-ws-option:has(input:checked) .collab-ws-check svg { opacity: 1; }

.collab-ws-avatar {
    width: 32px; height: 32px;
    border-radius: 8px;
    background: var(--primary);
    color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 600;
    font-size: 0.92rem;
    flex-shrink: 0;
}
/* Variants for the WorkspaceIcon helper output - same pattern as
   .ws-glyph and .account-icon. has-emoji = neutral background, bigger
   glyph; has-svg = primary-tinted background, primary stroke. */
.collab-ws-avatar.has-emoji {
    background: var(--surface-2); color: var(--text);
    border: 1px solid var(--border);
    font-size: 1.15rem;
}
.collab-ws-avatar.has-svg {
    background: var(--primary-bg); color: var(--primary);
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
}
.collab-ws-avatar.has-svg svg { display: block; width: 18px; height: 18px; }
.collab-ws-meta { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; flex: 1 1 auto; }
.collab-ws-email { font-size: 0.95rem; color: var(--text); font-weight: 500; }
.collab-ws-hint { font-size: 0.78rem; color: var(--muted); }

/* Submit row ---------------------------------------------------------- */
.collab-submit-row {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    padding-top: 0.25rem;
}
.collab-submit-hint { flex: 1 1 200px; }

/* Section title ------------------------------------------------------- */
.collab-list-section { margin-top: 1.5rem; }
.collab-list-head {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    margin: 0 0 0.6rem 0.1rem;
}
.collab-section-title {
    font-size: 0.85rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
    margin: 0;
}
.collab-list-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 7px;
    border-radius: 11px;
    background: var(--primary-bg);
    color: var(--primary);
    font-size: 0.72rem;
    font-weight: 600;
}

/* People-you-work-with section: unified list of every user the
   current viewer shares any workspace OR team with. Always rendered
   first on /Collaborators (owner OR viewer); the owner-only invite
   + manage section sits below. */
.coworker-section {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: var(--shadow-sm);
    margin-bottom: 1.5rem;
    overflow: hidden;
}
.coworker-section-head {
    display: flex; align-items: center; gap: .75rem;
    padding: 1rem 1.4rem;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
}
/* Section glyph sits inline with the title, smaller than the
   invite-card lede icon to fit the header bar. Uses the same
   tinted-square paint surface so the brand-colour rhythm carries
   across all three sections. */
.coworker-section-glyph {
    width: 32px !important; height: 32px !important;
    border-radius: 9px !important;
    flex: 0 0 auto;
}
.coworker-section-glyph svg { width: 16px; height: 16px; }
.coworker-section-head h2 {
    margin: 0;
    font-size: 1rem; font-weight: 700;
    letter-spacing: -.01em;
    color: var(--text);
}
.coworker-section-count {
    font-size: .72rem; font-weight: 700;
    color: var(--primary);
    background: var(--primary-bg);
    padding: 1px 9px;
    border-radius: 999px;
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
    line-height: 1.4;
    font-variant-numeric: tabular-nums;
}
.coworker-list { list-style: none; padding: 0; margin: 0; }
.coworker-row {
    display: flex; align-items: center; gap: .85rem;
    padding: .85rem 1.4rem;
    transition: background .12s ease;
}
.coworker-row + .coworker-row { border-top: 1px solid var(--border); }
.coworker-row:hover { background: var(--surface-2); }
.coworker-avatar {
    flex: 0 0 auto;
    width: 40px; height: 40px;
    border-radius: 50%;
    background: var(--avatar-bg);
    color: var(--avatar-color);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: .9rem;
    text-transform: uppercase;
    box-shadow: 0 0 0 2px var(--surface);
    overflow: hidden;
}
img.coworker-avatar { object-fit: cover; }
.coworker-text {
    flex: 1 1 auto; min-width: 0;
    display: flex; flex-direction: column; gap: 1px;
    line-height: 1.3;
}
.coworker-name {
    font-size: .95rem; font-weight: 600; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    display: inline-flex; align-items: center; gap: .45rem;
}
.coworker-email {
    font-size: .78rem; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.coworker-meta {
    font-size: .78rem; color: var(--text-muted);
    display: inline-flex; gap: .25rem; flex-wrap: wrap;
}
.coworker-meta strong { color: var(--text); font-weight: 600; }
.coworker-meta-sep { color: var(--text-subtle); }
.coworker-tag {
    flex: 0 0 auto;
    font-size: .62rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--primary);
    background: var(--primary-bg);
    padding: 1px 7px;
    border-radius: 999px;
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
    line-height: 1.4;
}
/* Clickable variant: full-row button that opens the shared-context modal.
   Resets the default <button> chrome (background, border) so the row
   reads as a card row, not a control. The chevron telegraphs that the
   row leads somewhere - cheap UX cue without needing a tooltip. */
button.coworker-row-clickable {
    width: 100%;
    background: transparent;
    border: 0;
    text-align: left;
    cursor: pointer;
    color: inherit;
    font: inherit;
}
button.coworker-row-clickable:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: -2px;
    background: var(--surface-2);
}
.coworker-row-chevron {
    flex: 0 0 auto;
    color: var(--text-subtle, var(--text-muted));
    opacity: .5;
    transition: opacity .12s ease, transform .12s ease;
}
button.coworker-row-clickable:hover .coworker-row-chevron {
    opacity: 1;
    transform: translateX(2px);
}

/* Coworker shared-context modal -------------------------------------- */
.coworker-modal-header {
    display: flex; align-items: center; gap: .85rem;
    padding: 1.1rem 1.25rem;
    border-bottom: 1px solid var(--border);
}
.coworker-modal-avatar {
    width: 48px; height: 48px; border-radius: 50%;
    background: var(--avatar-bg);
    color: var(--avatar-color);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: 1.05rem;
    text-transform: uppercase;
    overflow: hidden;
    flex: 0 0 auto;
    box-shadow: 0 0 0 3px var(--surface), 0 0 0 4px color-mix(in srgb, var(--primary) 22%, transparent);
}
.coworker-modal-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.coworker-modal-headtext { flex: 1 1 auto; min-width: 0; }
.coworker-modal-headtext .modal-title { margin: 0; font-size: 1.1rem; font-weight: 600; line-height: 1.2; }
.coworker-modal-email { margin-top: 2px; font-size: .8rem; }
.coworker-modal-body { padding: .25rem 0 1rem; }
.coworker-modal-loading,
.coworker-modal-empty { padding: 1.5rem 1.25rem; text-align: center; }

.coworker-modal-section { padding: .85rem 1.25rem .25rem; }
.coworker-modal-section + .coworker-modal-section {
    margin-top: 0;
    border-top: 1px solid var(--border-soft, var(--border));
}
.coworker-modal-section-head {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: .55rem;
}
.coworker-modal-section-head h6 {
    margin: 0;
    font-size: .7rem; font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase;
    color: var(--text-muted);
}
.coworker-modal-section-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 20px; height: 18px; padding: 0 .45rem;
    border-radius: 999px;
    background: var(--primary-bg); color: var(--primary);
    font-size: .68rem; font-weight: 700;
}
.coworker-modal-list { list-style: none; padding: 0; margin: 0; }
.coworker-modal-row {
    display: flex; align-items: center; gap: .7rem;
    padding: .55rem .55rem;
    margin: 0 -.55rem;
    border-radius: 8px;
    text-decoration: none; color: inherit;
    transition: background .12s ease, transform .12s ease;
}
.coworker-modal-row:hover {
    background: var(--surface-2);
    transform: translateX(2px);
}
.coworker-modal-row:focus-visible {
    outline: 2px solid var(--primary); outline-offset: 1px;
    background: var(--surface-2);
}
.coworker-modal-row-icon {
    width: 32px; height: 32px; border-radius: 8px;
    flex: 0 0 auto;
    overflow: hidden;
    background: var(--surface-2);
    display: inline-flex; align-items: center; justify-content: center;
}
/* When the icon span carries a photo, fill it edge-to-edge with the img;
   for SVG / emoji / letter the centered bubble look is the right one. */
.coworker-modal-row-icon.has-photo { background: transparent; }
.coworker-modal-row-icon.has-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.coworker-modal-row-icon.has-svg { background: var(--surface-2); }
.coworker-modal-row-icon.has-svg svg { width: 18px; height: 18px; color: var(--text-muted); }
.coworker-modal-row-icon.has-emoji { font-size: 1.05rem; line-height: 1; background: var(--surface-2); }
/* Letter fallback: gradient bubble matches the .coworker-avatar styling. */
.coworker-modal-row-icon:not(.has-photo):not(.has-svg):not(.has-emoji) {
    background: var(--avatar-bg);
    color: var(--avatar-color);
    font-weight: 700; font-size: .85rem;
}
.coworker-modal-row-text { flex: 1 1 auto; min-width: 0; }
.coworker-modal-row-title {
    display: block;
    font-size: .92rem; font-weight: 500; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.coworker-role-tag {
    flex: 0 0 auto;
    font-size: .62rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    padding: 2px 7px;
    border-radius: 999px;
    border: 1px solid var(--border);
    color: var(--text-muted);
    background: var(--surface-2);
    line-height: 1.4;
}
.coworker-role-tag.is-owner {
    color: var(--primary);
    background: var(--primary-bg);
    border-color: color-mix(in srgb, var(--primary) 25%, var(--border));
}
.coworker-role-tag.is-viewer { color: var(--text-muted); }

/* Flash animation for /Teams#team-N anchor focus. Pulse the border +
   shadow once so the eye locks onto the right card after navigating
   from the coworker-context modal. */
@keyframes team-card-focus-flash {
    0%   { box-shadow: 0 0 0 0    color-mix(in srgb, var(--primary) 50%, transparent); border-color: var(--primary); }
    50%  { box-shadow: 0 0 0 12px color-mix(in srgb, var(--primary) 0%,  transparent); border-color: var(--primary); }
    100% { box-shadow: 0 0 0 0    transparent; }
}
.team-card.is-focus-flash,
.invite-card.is-focus-flash,
.account-card.is-focus-flash,
.note.is-focus-flash {
    animation: team-card-focus-flash 1.6s ease-out;
    border-color: var(--primary) !important;
}

/* Owner-section secondary header (sub-page-title rhythm). */
.collab-header-secondary { margin-top: 2rem; }
.page-title-secondary { font-size: 1.3rem; }

/* Empty-state actions row for the no-collaborators-yet hero. */
.collab-empty-actions {
    display: flex; gap: .65rem; flex-wrap: wrap; justify-content: center;
    margin-top: 1rem;
}

/* Collaborator list --------------------------------------------------- */
.collab-list { list-style: none; padding: 0; margin: 0; }
.collab-row {
    border-bottom: 1px solid var(--border);
    padding: 1.1rem 1.4rem;
}
.collab-row:last-child { border-bottom: none; }

.collab-row-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}
.collab-identity {
    display: flex;
    align-items: flex-start;
    gap: 0.85rem;
    flex: 1 1 auto;
    min-width: 0;
}
.collab-identity-text { min-width: 0; flex: 1 1 auto; }

.collab-avatar {
    width: 40px; height: 40px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--primary) 0%, #7c3aed 100%);
    color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 600;
    font-size: 1rem;
    flex-shrink: 0;
}

.collab-email {
    font-weight: 600;
    color: var(--text);
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    font-size: 0.98rem;
}
.collab-email-sub {
    font-size: 0.85rem;
    color: var(--muted);
    margin-top: 0.05rem;
}
.collab-meta {
    margin-top: 0.3rem;
    font-size: 0.82rem;
    color: var(--muted);
    display: flex;
    gap: 0.35rem;
    align-items: center;
    flex-wrap: wrap;
}
.collab-meta strong { color: var(--text); font-weight: 600; }
.collab-meta-sep { opacity: 0.5; }

/* Badges ------------------------------------------------------------- */
.collab-badge {
    display: inline-block;
    padding: 0.12rem 0.55rem;
    border-radius: 999px;
    font-size: 0.68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    line-height: 1.5;
}
.collab-badge-pending { background: #fef3c7; color: #92400e; }

/* Granted/pending workspace rows ------------------------------------- */
.collab-ws-list {
    margin-top: 0.85rem;
    margin-left: 3.4rem;
    border-left: 2px solid var(--border);
    padding-left: 0.9rem;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}
.collab-ws-list-pending { border-left-color: #fcd34d; }

.collab-ws-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.35rem 0;
    font-size: 0.88rem;
}
.collab-ws-row-icon {
    width: 20px; height: 20px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.collab-ws-row-icon svg { width: 11px; height: 11px; }
.collab-ws-row-icon-granted { background: #d1fae5; color: #047857; }
.collab-ws-row-icon-pending { background: #fef3c7; color: #b45309; }

.collab-ws-email-text { color: var(--text); }
.collab-ws-granted {
    color: var(--muted);
    font-size: 0.78rem;
    margin-left: auto;
}
.collab-ws-row-action {
    display: inline-flex;
    gap: 0.3rem;
    margin-left: 0.5rem;
}
.collab-ws-row .collab-copy.is-copied { color: var(--success, #047857); }

/* Generic icon-only button + copy-link affordance.
   Used by the [data-copy-link] buttons on Resend rows across
   /Notifications, /Teams, and the Workspaces Manage modal. The
   .is-copied class is toggled briefly by Burofolk.copyLink when the
   write-to-clipboard succeeds, giving the operator a green-flash
   confirmation. */
.btn-icon-only {
    padding: 0.3rem 0.45rem;
    line-height: 1;
}
.btn-icon-only svg { display: block; }
.invite-copy-btn {
    color: var(--text-muted);
    transition: color 0.12s ease, background 0.12s ease;
}
.invite-copy-btn:hover { color: var(--primary); }
.invite-copy-btn.is-copied { color: var(--success, #047857); }

/* Grant another workspace ------------------------------------------- */
.collab-grant-form {
    margin: 0.85rem 0 0 3.4rem;
    padding-top: 0.75rem;
    border-top: 1px dashed var(--border);
    display: flex;
    gap: 0.5rem;
    align-items: center;
}
.collab-grant-form .form-select { flex: 1 1 auto; max-width: 360px; }

/* btn-xs (extra-small button) -------------------------------------- */
.btn-xs {
    padding: 0.2rem 0.55rem;
    font-size: 0.75rem;
    border-radius: 6px;
    line-height: 1.3;
}

/* Flash error variant ---------------------------------------------- */
.account-flash-error {
    background: #fee2e2;
    color: #7f1d1d;
    border: 1px solid #fca5a5;
}

/* Workspace permissions modal -------------------------------------- */
.perm-row {
    display: flex; align-items: flex-start; gap: 0.65rem;
    padding: 0.7rem 0.85rem;
    border: 1px solid var(--border); border-radius: 10px;
    margin-bottom: 0.5rem;
    cursor: pointer;
    transition: background-color 120ms ease, border-color 120ms ease;
}
.perm-row:hover { border-color: var(--primary); background: var(--primary-bg); }
/* Custom checkbox styling for the permissions modal. The native
   browser control is replaced with a 20px rounded square: muted
   border at rest, primary-filled with a white tick when checked,
   primary-tinted focus ring for keyboard users. The whole .perm-row
   is the click target (the <label> wraps both); the checkbox just
   needs to look right. */
.perm-row input[type="checkbox"] {
    flex-shrink: 0;
    appearance: none;
    -webkit-appearance: none;
    width: 20px; height: 20px;
    margin: 1px 0 0;
    border: 1.5px solid var(--border-strong);
    border-radius: 6px;
    background: var(--surface);
    cursor: pointer;
    position: relative;
    transition: background .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.perm-row:hover input[type="checkbox"]:not(:checked) {
    border-color: var(--primary);
}
.perm-row input[type="checkbox"]:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 25%, transparent);
}
.perm-row input[type="checkbox"]:checked {
    background: var(--primary);
    border-color: var(--primary);
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>");
    background-size: 14px 14px;
    background-repeat: no-repeat;
    background-position: center;
}
.perm-row input[type="checkbox"]:checked:hover {
    background-color: var(--primary-hover);
    border-color: var(--primary-hover);
}
/* When the row's checkbox is checked, lift the row a tiny bit so
   the active state is obvious at a glance across all rows. */
.perm-row:has(input[type="checkbox"]:checked) {
    background: color-mix(in srgb, var(--primary) 6%, var(--surface));
    border-color: color-mix(in srgb, var(--primary) 30%, var(--border));
}

/* 2FA-locked workspace card. The card itself dims slightly so the
   user sees the workspace exists but it doesn't compete with the
   live ones. The banner across the top says exactly what's required
   and includes a one-click route to the 2FA enrollment page. */
.account-row.is-2fa-locked .account-row-header,
.account-row.is-2fa-locked .account-meta,
.account-row.is-2fa-locked .ws-collab-pile,
.account-row.is-2fa-locked .progress-row {
    opacity: .55;
}
.account-2fa-banner {
    display: flex; align-items: center; gap: .75rem;
    padding: .75rem 1rem;
    margin: 0 0 .9rem;
    background: color-mix(in srgb, var(--warning) 10%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--warning) 35%, var(--border));
    border-radius: 10px;
}
.account-2fa-banner > svg {
    flex: 0 0 auto;
    color: var(--warning);
}
.account-2fa-banner > div {
    flex: 1 1 auto; min-width: 0;
    line-height: 1.45;
}
.account-2fa-banner strong {
    display: block;
    font-size: .9rem; font-weight: 600;
    color: var(--text);
    margin-bottom: 1px;
}
.account-2fa-banner span {
    display: block;
    font-size: .8rem; color: var(--text-muted);
}
.account-2fa-banner .btn { flex: 0 0 auto; }
.perm-text { display: flex; flex-direction: column; gap: 0.15rem; }
.perm-text strong { font-weight: 600; color: var(--text); font-size: 0.92rem; }
.perm-text em { font-style: normal; font-size: 0.82rem; color: var(--muted); line-height: 1.45; }
/* Security-themed perm row (e.g. the Require-2FA toggle). Same shape
   as the others; a small inline pill on the title flags it as a
   policy/security control rather than a viewer convenience. */
.perm-row-security { border-color: color-mix(in srgb, var(--primary) 18%, var(--border)); }
.perm-tag {
    display: inline-block;
    margin-left: .4rem;
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--primary-bg);
    color: var(--primary);
    font-size: .6rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    vertical-align: middle;
}

/* ── Workspace permissions modal: container shape ────────────────────
   Wider dialog to accommodate the search-and-filter toolbar plus
   sectioned body without horizontal cramp. The body is the only
   scrolling region; header + toolbar + footer stay pinned so the
   filter controls and Save/Cancel are always reachable regardless
   of how long the permissions list grows. */
.perm-modal-dialog { max-width: 720px; }
.perm-modal-content {
    /* Fixed height (not max-height) so the dialog stays the SAME size
       regardless of how many sections are visible - clicking a chip
       filter must not jump the modal smaller / taller. Bootstrap's
       modal-dialog-scrollable wraps this; it confines overflow to
       .modal-body which we let flex-grow inside the column.

       The form between .modal-content and the header/body/footer would
       normally break the flex chain (form is display:block by default),
       so .perm-modal-form is forced to display:contents below - its
       children become direct flex items of .modal-content. */
    height: min(86vh, 720px);
    border-radius: 14px;
    overflow: hidden;
}
.perm-modal-form {
    /* Transparent to layout: the form still wraps inputs for submission,
       but its children participate in .modal-content's flex column. */
    display: contents;
}
.perm-modal-head {
    flex: 0 0 auto;
    align-items: flex-start;
    border-bottom: 1px solid var(--surface-2);
    padding: 1rem 1.25rem .85rem;
}
.perm-modal-head-text { flex: 1 1 auto; min-width: 0; }
.perm-modal-head .modal-title {
    font-size: 1.05rem; font-weight: 700; color: var(--text);
    letter-spacing: -.01em;
    margin: 0 0 .15rem;
}
.perm-modal-sub {
    margin: 0;
    color: var(--text-muted);
    font-size: .82rem;
    line-height: 1.45;
}
.perm-modal-sub strong { color: var(--text); font-weight: 600; }

/* ── Toolbar: search + filter chips ───────────────────────────────────
   Sits between the modal header and the scrollable body. Both controls
   filter the visible permission rows client-side; never reaches the
   server. Search matches keywords on each row; chips swap which group
   of sections is visible. */
.perm-modal-toolbar {
    flex: 0 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .65rem;
    padding: .65rem 1.25rem;
    border-bottom: 1px solid var(--surface-2);
    background: var(--surface-2);
}
.perm-search {
    position: relative;
    flex: 0 1 260px;
    min-width: 180px;
    display: inline-flex;
    align-items: center;
}
.perm-search > svg {
    position: absolute;
    left: 9px; top: 50%; transform: translateY(-50%);
    color: var(--text-muted);
    pointer-events: none;
}
.perm-search-input {
    width: 100%;
    padding: .42rem .55rem .42rem 1.85rem;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: var(--surface);
    color: var(--text);
    font-size: .85rem;
    outline: 0;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.perm-search-input:focus {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-bg);
}
.perm-search-input::-webkit-search-cancel-button { display: none; }

.perm-chips {
    display: inline-flex;
    flex-wrap: wrap;
    gap: .3rem;
    flex: 1 1 auto;
}
.perm-chip {
    padding: .26rem .65rem;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text-muted);
    border-radius: 999px;
    font-size: .76rem; font-weight: 600;
    cursor: pointer;
    transition: background .1s ease, color .1s ease, border-color .1s ease;
}
.perm-chip:hover { color: var(--text); border-color: var(--border-strong); }
.perm-chip.is-active {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}
.perm-chip.is-active:hover { background: var(--primary-hover, var(--primary)); color: #fff; }

/* ── Body: scrollable section list ───────────────────────────────── */
.perm-modal-body {
    flex: 1 1 auto;
    min-height: 0;          /* required for flex children to shrink + scroll */
    overflow-y: auto;
    padding: 1rem 1.25rem 1.1rem;
    background: var(--surface);
}
/* Bootstrap's modal-dialog-scrollable ships its own height/overflow rules
   on .modal-content; force ours to win so the dialog stays a fixed size
   and the body is the one and only scroll region. */
.modal-dialog-scrollable .perm-modal-content {
    max-height: none;
    height: min(86vh, 720px);
}

/* ── Section: groups related permissions under a clear heading ───── */
.perm-section {
    border: 0;
    padding: 0;
    margin: 0 0 1.4rem;
    min-width: 0;
}
.perm-section:last-of-type { margin-bottom: 0; }
.perm-section-head {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto auto;
    column-gap: .55rem;
    row-gap: .1rem;
    align-items: baseline;
    width: 100%;
    margin: 0 0 .6rem;
    padding: 0;
    border: 0;
    float: none;
}
.perm-section-title {
    grid-column: 1;
    grid-row: 1;
    font-size: .92rem;
    font-weight: 700;
    color: var(--text);
    letter-spacing: -.005em;
}
.perm-section-tag {
    grid-column: 2;
    grid-row: 1;
    justify-self: start;
    display: inline-block;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: .6rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
    line-height: 1.5;
    align-self: center;
}
.perm-section-tag-security {
    background: color-mix(in srgb, #b91c1c 14%, transparent);
    color: #b91c1c;
}
.perm-section-tag-ai {
    background: color-mix(in srgb, #0ea5e9 14%, transparent);
    color: #0369a1;
}
.perm-section-sub {
    grid-column: 1 / -1;
    grid-row: 2;
    font-size: .78rem;
    color: var(--text-muted);
    font-weight: 400;
    line-height: 1.45;
    margin: 0;
}

/* Empty state when search/filter narrows the list to nothing. */
.perm-empty {
    text-align: center;
    color: var(--text-muted);
    font-size: .85rem;
    padding: 1.4rem .8rem;
    background: var(--surface-2);
    border: 1px dashed var(--border);
    border-radius: 10px;
}

/* Sticky footer: Cancel + Save always reachable. */
.perm-modal-foot {
    flex: 0 0 auto;
    border-top: 1px solid var(--surface-2);
    padding: .75rem 1.25rem;
    background: var(--surface);
    gap: .5rem;
}

@media (max-width: 720px) {
    .perm-modal-toolbar { padding: .55rem .85rem; }
    .perm-modal-body { padding: .85rem; }
    .perm-modal-head { padding: .85rem .85rem .65rem; }
    .perm-modal-foot { padding: .65rem .85rem; }
    .perm-search { flex: 1 1 100%; }
}

/* ======================================================================
   Incoming-invitations inbox on /Workspaces
   Design goals: make it feel like a distinct interactive zone, clear
   inviter identity, obvious Accept / Decline. One card per invitation so
   dense multi-invite inboxes stay scannable.
   ====================================================================== */
.invite-inbox { display: flex; flex-direction: column; gap: 0.75rem; width: 100%; }
.invite-inbox-header {
    display: flex; flex-direction: column; gap: 0.25rem;
    padding: 0 0.15rem;
}
.invite-inbox-pill {
    align-self: flex-start;
    display: inline-flex; align-items: center; gap: 0.4rem;
    padding: 0.3rem 0.75rem;
    border-radius: 999px;
    background: linear-gradient(135deg, rgba(99, 102, 241, 0.14), rgba(139, 92, 246, 0.14));
    color: var(--primary);
    font-size: 0.78rem; font-weight: 600;
    letter-spacing: 0.02em;
}
.invite-inbox-pill svg { width: 12px; height: 12px; }
.invite-inbox-sub { font-size: 0.85rem; color: var(--muted); line-height: 1.4; }

/* Card stack - each invitation its own surface */
.invite-card-stack { display: flex; flex-direction: column; gap: 0.65rem; }
.invite-card {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1.1rem 1.25rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
    position: relative;
    overflow: hidden;
    transition: border-color 140ms ease, box-shadow 140ms ease, transform 140ms ease;
}
.invite-card::before {
    content: "";
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 3px;
    background: linear-gradient(180deg, var(--primary), #7c3aed);
}
.invite-card:hover {
    border-color: var(--primary);
    box-shadow: 0 4px 18px rgba(99, 102, 241, 0.12);
}

.invite-card-main { display: flex; align-items: center; gap: 0.9rem; min-width: 0; flex: 1 1 auto; }
.invite-card-avatar {
    width: 44px; height: 44px;
    border-radius: 50%;
    background: var(--avatar-bg);
    color: var(--avatar-color);
    font-weight: 700;
    font-size: 1.05rem;
    display: inline-flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    box-shadow: 0 2px 8px rgba(99, 102, 241, 0.25);
}

.invite-card-text { min-width: 0; flex: 1 1 auto; }
.invite-card-line {
    font-size: 0.98rem;
    color: var(--text);
    line-height: 1.4;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.25rem;
}
.invite-card-inviter { font-weight: 600; color: var(--text); }
.invite-card-connector { color: var(--muted); font-weight: 400; }
.invite-card-workspace { font-weight: 600; color: var(--primary); }

.invite-card-meta { display: flex; gap: 0.4rem; margin-top: 0.45rem; flex-wrap: wrap; }
.invite-card-chip {
    display: inline-flex; align-items: center; gap: 0.3rem;
    padding: 0.18rem 0.55rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 500;
    border: 1px solid var(--border);
    color: var(--muted);
    background: var(--muted-bg);
}
.invite-card-chip svg { flex-shrink: 0; }
.invite-card-chip-clock { background: #fff7ed; color: #9a3412; border-color: #fed7aa; }

.invite-card-actions {
    display: flex;
    gap: 0.45rem;
    flex-shrink: 0;
    align-items: center;
}

/* Dark theme */
[data-theme="dark"] .invite-card-chip-clock {
    background: rgba(234, 88, 12, 0.12);
    color: #fdba74;
    border-color: rgba(234, 88, 12, 0.35);
}
[data-theme="dark"] .invite-inbox-pill {
    background: linear-gradient(135deg, rgba(99, 102, 241, 0.22), rgba(139, 92, 246, 0.22));
}

/* Responsive: stack actions below meta on narrow viewports */
@media (max-width: 640px) {
    .invite-card { flex-direction: column; align-items: stretch; }
    .invite-card-actions { justify-content: flex-end; }
}

/* ======================================================================
   Universal avatar image rule
   -----------------------------------------------------------------------
   When a user has uploaded a profile photo, AvatarHelper.UserAvatar emits
   `<img class="{existing-bubble-class} avatar-img" src=...>` - same
   container class as the initial bubble (so size/border-radius/shadow
   carry over) plus this one rule that makes the photo fill the circle
   correctly without stretching.
   ====================================================================== */
img.avatar-img {
    object-fit: cover;
    object-position: center;
    background: var(--surface-2);
}

/* ======================================================================
   Profile page
   ====================================================================== */
.profile-page { max-width: 720px; margin: 0 auto; }
.profile-card .card-body { padding: 2rem 2.2rem; }
.profile-header { display: flex; align-items: center; gap: 1.25rem; margin-bottom: 1.75rem; }

/* Avatar + "click to change" camera button overlay. The wrap is
   position:relative so the edit button can sit in the bottom-right. */
.profile-avatar-wrap { position: relative; flex: 0 0 auto; }
.profile-avatar {
    width: 72px; height: 72px; border-radius: 50%;
    background: linear-gradient(135deg, var(--primary) 0%, #7c3aed 100%);
    color: #fff;
    font-size: 1.9rem; font-weight: 700;
    display: inline-flex; align-items: center; justify-content: center;
    flex: 0 0 auto;
    box-shadow: 0 4px 14px rgba(99, 102, 241, 0.2);
    overflow: hidden;
}
.profile-avatar-img { object-fit: cover; }
.profile-avatar-edit-btn {
    position: absolute; right: -2px; bottom: -2px;
    width: 28px; height: 28px; border-radius: 50%;
    background: var(--surface);
    color: var(--primary);
    border: 2px solid var(--surface);
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(15, 23, 42, 0.15);
    transition: transform 120ms ease, background-color 120ms ease;
}
.profile-avatar-edit-btn:hover { background: var(--primary); color: #fff; transform: scale(1.05); }
.profile-avatar-remove { margin-top: 0.4rem; }
.profile-header-meta { min-width: 0; flex: 1 1 auto; }
.profile-email { font-size: 1.05rem; font-weight: 600; color: var(--text); }
.profile-meta { font-size: 0.82rem; color: var(--muted); margin-top: 0.2rem; display: flex; gap: 0.4rem; flex-wrap: wrap; align-items: center; }
.profile-meta-sep { opacity: 0.5; }

.profile-form .form-control-lg { font-size: 1rem; padding: 0.7rem 0.9rem; border-radius: 10px; }
/* Two-column row for short profile fields - role+location and
   linkedin+website each share width on desktop, stack under 720px. */
.profile-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .75rem 1rem;
}
@media (max-width: 720px) {
    .profile-row { grid-template-columns: 1fr; }
}

.profile-hint {
    background: var(--muted-bg);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.9rem 1.1rem;
    margin: 1rem 0 1.5rem;
}
.profile-hint-title { font-weight: 600; font-size: 0.85rem; color: var(--text); margin-bottom: 0.25rem; }

.profile-actions { display: flex; justify-content: flex-end; gap: 0.5rem; padding-top: 0.25rem; border-top: 1px solid var(--border); margin-top: 0.75rem; padding-top: 1rem; }

.profile-danger .card-body { padding: 1.5rem 2rem; }

/* Avatar crop modal ------------------------------------------------ */
.avatar-picker {
    display: flex; align-items: center; gap: 0.75rem;
    flex-wrap: wrap;
    padding: 0.75rem 0.9rem;
    border: 1px dashed var(--border);
    border-radius: 10px;
    background: var(--muted-bg);
    margin-bottom: 1rem;
}
.avatar-picker-btn { cursor: pointer; margin: 0; }
.avatar-picker-hint { flex: 1 1 180px; }

.avatar-crop-area {
    position: relative;
    width: 256px; height: 256px;
    margin: 0 auto 1rem;
    background: #000;
    border-radius: 12px;
    overflow: hidden;
    touch-action: none;
}
.avatar-crop-area canvas {
    display: block;
    width: 256px; height: 256px;
    cursor: grab;
    user-select: none;
}
.avatar-crop-area canvas:active { cursor: grabbing; }
/* Circular preview mask - the crop is actually saved as a 256x256 PNG
   (square) but we show the circle so the user sees how it will look
   everywhere else in Burofolk. */
.avatar-crop-mask {
    position: absolute; inset: 0;
    pointer-events: none;
    background:
        radial-gradient(circle at center, transparent 0 50%, rgba(15, 23, 42, 0.55) 51% 100%);
    border-radius: 12px;
}

.avatar-zoom { display: flex; align-items: center; gap: 0.7rem; }
.avatar-zoom-label { font-size: 0.82rem; color: var(--muted); flex-shrink: 0; }
.avatar-zoom input[type="range"] { flex: 1 1 auto; }

/* Dark theme overrides --------------------------------------------- */
[data-theme="dark"] .collab-ws-check { background: var(--surface); }
[data-theme="dark"] .collab-badge-pending { background: rgba(252, 211, 77, 0.15); color: #fcd34d; }
[data-theme="dark"] .collab-ws-row-icon-granted { background: rgba(16, 185, 129, 0.15); color: #34d399; }
[data-theme="dark"] .collab-ws-row-icon-pending { background: rgba(252, 211, 77, 0.15); color: #fcd34d; }
[data-theme="dark"] .account-flash-error { background: rgba(239, 68, 68, 0.12); color: #fca5a5; border-color: rgba(239, 68, 68, 0.35); }

/* Responsive ------------------------------------------------------- */
@media (max-width: 640px) {
    .collab-invite-panel.is-hero .card-body,
    .collab-invite-panel.is-compact .card-body { padding: 1.25rem; }
    .collab-invite-lede { flex-direction: column; }
    .collab-invite-icon { width: 36px; height: 36px; }
    .collab-ws-list, .collab-grant-form { margin-left: 0; border-left: none; padding-left: 0; }
    .collab-header { flex-direction: column; }
}

/* Avatars rendered with a userId become clickable profile-card triggers
   via the global JS in _Layout.cshtml. The class adds the affordances
   (pointer cursor, focus ring, gentle hover lift) without touching the
   per-surface bubble styling. */
.is-clickable {
    cursor: pointer;
    transition: transform 120ms ease, box-shadow 120ms ease, filter 120ms ease;
}
.is-clickable:hover { transform: translateY(-1px); filter: brightness(1.05); }
.is-clickable:focus-visible {
    outline: 2px solid var(--bs-primary, #4f46e5);
    outline-offset: 2px;
}

/* Profile card loading skeleton. While the /Users/{id}/profile fetch
   is in flight the modal carries .is-loading and we draw shimmer
   placeholders in each slot - feels much faster than a blank card.
   The skeleton blocks are sized to the typical content they replace
   so the resolved content doesn't shift the layout. */
@keyframes profile-skeleton-shimmer {
    0%   { background-position: -200% 0; }
    100% { background-position:  200% 0; }
}
.profileCardModal--skeleton-bg,
[data-profile-card].is-loading [data-profile-name]::after,
[data-profile-card].is-loading [data-profile-email]::after,
[data-profile-card].is-loading .collab-profile-avatar::after {
    background: linear-gradient(90deg,
        var(--surface-2) 0%,
        color-mix(in srgb, var(--primary) 8%, var(--surface-2)) 50%,
        var(--surface-2) 100%);
    background-size: 200% 100%;
    animation: profile-skeleton-shimmer 1.2s ease-in-out infinite;
}
[data-profile-card].is-loading [data-profile-name],
[data-profile-card].is-loading [data-profile-email] { position: relative; min-height: 1.2em; }
[data-profile-card].is-loading [data-profile-name]::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    width: 60%; height: 1.2em; transform: translate(-50%, -50%);
    border-radius: 6px;
}
[data-profile-card].is-loading [data-profile-email]::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    width: 75%; height: .85em; transform: translate(-50%, -50%);
    border-radius: 4px;
}
[data-profile-card].is-loading .collab-profile-avatar { position: relative; color: transparent; }
[data-profile-card].is-loading .collab-profile-avatar::after {
    content: ""; position: absolute; inset: 0; border-radius: 50%;
}
[data-profile-card].is-error .collab-profile-avatar {
    background: var(--surface-2);
    color: var(--text-subtle);
    box-shadow: 0 0 0 5px var(--surface), 0 8px 18px rgba(15, 23, 42, .15);
}
[data-profile-card].is-error .collab-profile-avatar::before {
    content: "?";
    font-size: 2rem; font-weight: 700;
}
[data-profile-card].is-error .collab-profile-name { color: var(--text-muted); font-size: 1.05rem; }
[data-profile-card].is-error .collab-profile-email { color: var(--text-subtle); font-size: .8rem; line-height: 1.5; }

/* ============================================================
   Home page premium redesign (brand moves 1-6).
   Asymmetric hero, brand-color stat numbers, per-workspace
   identity stripes, illustrated empty state, brand-tinted
   backdrop, and a sticky bottom utility rail. Each block
   overrides the centered/generic version above; nothing
   here changes layout outside .home-landing.
   ============================================================ */

/* Subtle brand-tinted backdrop on every non-inbox page (the
   inbox keeps a clean reading surface). Pinned via fixed
   positioning so it stays put as the page scrolls; pointer-
   events disabled so it never intercepts a click. body class
   `is-inbox` opts out (set by _Layout.cshtml on /Mail routes). */
body.is-app::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background:
        radial-gradient(ellipse 80% 60% at 15% 10%, rgba(6, 182, 212, .06), transparent 60%),
        radial-gradient(ellipse 70% 60% at 85% 30%, rgba(139, 92, 246, .07), transparent 60%),
        radial-gradient(ellipse 90% 70% at 50% 110%, rgba(236, 72, 153, .05), transparent 60%);
    pointer-events: none;
}
html[data-theme="dark"] body.is-app::before {
    background:
        radial-gradient(ellipse 80% 60% at 15% 10%, rgba(6, 182, 212, .14), transparent 60%),
        radial-gradient(ellipse 70% 60% at 85% 30%, rgba(139, 92, 246, .16), transparent 60%),
        radial-gradient(ellipse 90% 70% at 50% 110%, rgba(236, 72, 153, .12), transparent 60%);
}

/* ──────────────────────────────────────────────────────────
   Move 1 + 2: asymmetric hero. Greeting on the left with a
   3-bar gradient rail; key stats stacked on the right in
   Plus Jakarta Sans ExtraBold. Replaces the centered generic
   "Welcome back." block.
   ────────────────────────────────────────────────────────── */
.home-hero-split {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 3rem;
    padding: 3.5rem 0 2.5rem;
    text-align: left;
    min-height: auto;
}
/* Override the older .home-hero-rotating min-height + flex
   centering so split layout owns its own dimensions. */
.home-hero-compact.home-hero-rotating.home-hero-split {
    min-height: auto;
    display: grid;
    align-items: center;
    justify-content: stretch;
}
.home-hero-text-col {
    position: relative;
    padding-left: 1.5rem;
    min-width: 0;
}
.home-hero-rail {
    position: absolute;
    left: 0; top: .35rem; bottom: .35rem;
    width: 4px;
    display: flex; flex-direction: column;
    border-radius: 4px;
    overflow: hidden;
}
.home-hero-rail span {
    flex: 1 1 0;
    display: block;
}
.home-hero-split .home-hero-headline {
    font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
    font-weight: 800;
    font-size: 2rem;
    letter-spacing: -.025em;
    line-height: 1.1;
    margin: 0 0 .5rem;
    color: var(--text);
    display: inline-flex; align-items: baseline; gap: 1px;
}
.home-hero-split .home-hero-sub {
    font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
    font-size: 1rem;
    color: var(--text-muted);
    max-width: 560px;
    margin: 0;
    line-height: 1.5;
    /* CLS guard: the rotating welcome cycles between 1-line and
       2-line copy ("You're in the loop." → "Best-in-class at
       structuring the unstructured. The other vendors are still
       drafting their roadmap."). Without a reservation the page
       below jumps every rotation. Reserve exactly two lines so
       short messages keep the same vertical footprint as long
       ones; clamp longer copy to two lines so it never blows
       past the reservation either. */
    min-height: calc(1rem * 1.5 * 2);
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
}

/* Stat tiles in the right column. Each gets a 2px gradient-color
   underline. Numbers are huge, in Plus Jakarta Sans, with tabular
   nums so re-renders never jiggle the column width. */
.home-hero-stats-col {
    display: grid;
    grid-auto-flow: column;
    gap: 1.6rem;
    align-items: end;
    flex: 0 0 auto;
}
.home-hero-stat {
    display: inline-flex; flex-direction: column; gap: .25rem;
    text-decoration: none; color: inherit;
    padding: .25rem 0 .55rem;
    border-bottom: 2px solid var(--border);
    transition: border-color .15s ease, transform .08s ease;
    min-width: 4.5rem;
}
.home-hero-stat:hover { transform: translateY(-1px); }
.home-hero-stat-cyan   { border-bottom-color: #06B6D4; }
.home-hero-stat-violet { border-bottom-color: #8B5CF6; }
.home-hero-stat-pink   { border-bottom-color: #EC4899; }
.home-hero-stat.is-static { cursor: default; }
.home-hero-stat-value {
    font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
    font-weight: 800;
    font-size: 2.4rem;
    letter-spacing: -.035em;
    line-height: 1;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}
.home-hero-stat-label {
    font-size: .72rem;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: .06em;
}

@media (max-width: 767.98px) {
    .home-hero-split {
        grid-template-columns: 1fr;
        gap: 1.75rem;
        padding: 2.25rem 0 1.5rem;
    }
    .home-hero-stats-col {
        grid-auto-flow: column;
        gap: 1.25rem;
        justify-content: start;
    }
    .home-hero-split .home-hero-headline { font-size: 1.65rem; }
    .home-hero-stat-value { font-size: 2rem; }
}

/* ──────────────────────────────────────────────────────────
   Move 3: per-workspace identity stripe on each card. Hue is
   passed in as a CSS custom property by the Razor view (from
   IndexModel.WorkspaceHue). 4px stripe on the leading edge,
   60% saturation / 60% lightness so it reads on both light
   and dark themes.
   ────────────────────────────────────────────────────────── */
.quick-open-card { position: relative; overflow: hidden; }
.quick-open-stripe {
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 4px;
    background: var(--ws-color, hsl(var(--ws-hue, 220), 65%, 58%));
    border-radius: 4px 0 0 4px;
}
.home-card-workspaces .quick-open-card { padding-left: 1.1rem; }

/* Ghost "connect another" tile gets a neutral grey stripe so it
   doesn't read as a new workspace with a real identity. */
.quick-open-card.quick-open-add .quick-open-stripe {
    background: var(--border-strong);
    opacity: .5;
}

/* ──────────────────────────────────────────────────────────
   Move 4: illustrated empty-state for Recent activity.
   Replaces the apologetic one-liner with a centered 3-bar
   mark, friendly heading, and a single CTA.
   ────────────────────────────────────────────────────────── */
.home-activity-empty {
    display: flex; flex-direction: column; align-items: center;
    text-align: center;
    padding: 2.5rem 1.25rem;
    gap: .85rem;
}
.home-activity-empty-mark {
    width: 56px; height: 56px;
    opacity: .85;
    transition: transform .25s cubic-bezier(.34, 1.56, .64, 1);
}
.home-card-activity:hover .home-activity-empty-mark { transform: rotate(-4deg) scale(1.04); }
.home-activity-empty-title {
    font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--text);
    margin: 0;
    letter-spacing: -.01em;
}
.home-activity-empty-sub {
    font-size: .88rem;
    color: var(--text-muted);
    line-height: 1.55;
    max-width: 320px;
    margin: 0;
}
.home-activity-empty-cta {
    margin-top: .35rem;
    display: inline-flex; align-items: center; gap: .4rem;
    padding: .5rem .9rem;
    border-radius: 999px;
    background: var(--text);
    color: var(--surface);
    font-weight: 600; font-size: .85rem;
    text-decoration: none;
    transition: background .15s ease, transform .08s ease;
}
.home-activity-empty-cta:hover {
    background: var(--primary);
    color: #fff;
    transform: translateY(-1px);
}
.home-activity-empty-cta svg { transition: transform .15s ease; }
.home-activity-empty-cta:hover svg { transform: translateX(2px); }

/* ──────────────────────────────────────────────────────────
   "What's next" tile row below the main grid. Fills vertical
   space and surfaces high-leverage actions (connect, invite,
   secure, browse) as compact tiles instead of leaving the
   page to die into footer whitespace.
   ────────────────────────────────────────────────────────── */
.home-next {
    margin-top: 2rem;
}
.home-next-head {
    display: flex; align-items: baseline;
    gap: 1rem;
    margin: 0 0 1rem;
}
.home-next-head h2 {
    font-family: "Plus Jakarta Sans", "Inter", -apple-system, system-ui, sans-serif;
    font-weight: 700;
    font-size: 1.05rem;
    letter-spacing: -.01em;
    color: var(--text);
    margin: 0;
}
.home-next-sub {
    font-size: .82rem;
    color: var(--text-subtle);
}
.home-next-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: .85rem;
}
.home-next-tile {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: .9rem;
    padding: 1rem 1.1rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    text-decoration: none;
    color: inherit;
    transition: border-color .15s ease, transform .12s ease, box-shadow .15s ease;
    position: relative;
    overflow: hidden;
}
.home-next-tile::before {
    content: "";
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 3px;
    background: var(--primary);
    transform: scaleY(0);
    transform-origin: top;
    transition: transform .2s cubic-bezier(.32, .72, 0, 1);
}
.home-next-tile[data-accent="cyan"]::before   { background: #06B6D4; }
.home-next-tile[data-accent="violet"]::before { background: #8B5CF6; }
.home-next-tile[data-accent="pink"]::before   { background: #EC4899; }
.home-next-tile:hover {
    border-color: var(--border-strong);
    transform: translateY(-1px);
    box-shadow: 0 8px 18px rgba(15, 23, 42, .06);
}
.home-next-tile:hover::before { transform: scaleY(1); }
.home-next-icon {
    width: 36px; height: 36px;
    border-radius: 10px;
    background: var(--surface-2);
    color: var(--text);
    display: inline-flex; align-items: center; justify-content: center;
    flex: 0 0 auto;
}
.home-next-tile[data-accent="cyan"]   .home-next-icon { background: rgba(6, 182, 212, .12);  color: #0891B2; }
.home-next-tile[data-accent="violet"] .home-next-icon { background: rgba(139, 92, 246, .12); color: #7C3AED; }
.home-next-tile[data-accent="pink"]   .home-next-icon { background: rgba(236, 72, 153, .12); color: #DB2777; }
html[data-theme="dark"] .home-next-tile[data-accent="cyan"]   .home-next-icon { color: #22D3EE; }
html[data-theme="dark"] .home-next-tile[data-accent="violet"] .home-next-icon { color: #A78BFA; }
html[data-theme="dark"] .home-next-tile[data-accent="pink"]   .home-next-icon { color: #F472B6; }

/* Photo hero variant of the tile icon. A real photograph
   fills a small rounded square, kept tightly bounded so the
   body text dominates the row - the image is a tasteful
   accent, not the whole tile. */
.home-next-photo {
    flex: 0 0 auto;
    width: 56px; height: 56px;
    border-radius: 12px;
    overflow: hidden;
    display: block;
    background: var(--surface-2);
    box-shadow: 0 1px 2px rgba(15, 23, 42, .08);
}
.home-next-photo img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
    transition: transform .35s cubic-bezier(.32, .72, 0, 1);
}
.home-next-tile:hover .home-next-photo img { transform: scale(1.06); }

/* Custom brand-gradient picto. Same footprint as .home-next-photo
   so the tile layout is unchanged, but the SVG fills the slot
   with the brand cyan→violet→pink wash defined inline per icon.
   No background tint - the gradient IS the visual. */
.home-next-picto {
    flex: 0 0 auto;
    width: 56px; height: 56px;
    border-radius: 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: transform .35s cubic-bezier(.32, .72, 0, 1);
}
.home-next-picto svg {
    width: 100%; height: 100%;
    display: block;
}
.home-next-tile:hover .home-next-picto { transform: rotate(-4deg) scale(1.06); }
.home-next-body {
    display: flex; flex-direction: column; gap: .15rem;
    min-width: 0;
}
.home-next-title {
    font-weight: 600;
    font-size: .92rem;
    color: var(--text);
    letter-spacing: -.005em;
}
.home-next-desc {
    font-size: .8rem;
    color: var(--text-muted);
    line-height: 1.4;
}
.home-next-arrow {
    color: var(--text-subtle);
    transition: color .15s ease, transform .15s ease;
}
.home-next-tile:hover .home-next-arrow {
    color: var(--primary);
    transform: translateX(2px);
}

/* ============================================================
   Workspace identity stripes + accent picker. Same hue source
   as the home-page workspace tiles (Burofolk.Services.WorkspaceAccent),
   so a workspace reads with the same color across surfaces.
   ============================================================ */

/* Stripe on /Workspaces card. Anchored top-to-bottom on the
   leading edge. The .account-row already has overflow visible
   for status dots etc., so the stripe is positioned absolutely
   inside the row's box - no clip needed. */
.account-row { position: relative; }
.account-row-stripe {
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 4px;
    background: var(--ws-color, hsl(var(--ws-hue, 220), 65%, 58%));
    border-radius: 16px 0 0 16px;
    pointer-events: none;
}
html[data-theme="dark"] .account-row-stripe {
    background: var(--ws-color, hsl(var(--ws-hue, 220), 70%, 62%));
}

/* Color trigger button on each owner card. A dot in the
   workspace's accent color opens the accent modal. Sits in the
   icon-button row beside the gear and remove buttons. */
.ws-accent-trigger { padding: .35rem !important; }
.ws-accent-dot {
    display: inline-block;
    width: 16px; height: 16px;
    border-radius: 50%;
    background: var(--ws-color, hsl(var(--ws-hue, 220), 65%, 58%));
    box-shadow: 0 0 0 2px var(--surface), 0 0 0 3px hsla(var(--ws-hue, 220), 65%, 58%, .25);
    transition: transform .12s ease;
}
.ws-accent-trigger:hover .ws-accent-dot { transform: scale(1.1); }

/* Accent picker modal -------------------------------------- */
.accent-picker { margin: 0; }
.accent-preview {
    display: flex;
    align-items: center;
    gap: .85rem;
    padding: .85rem .95rem;
    background: var(--surface-2);
    border-radius: 12px;
    margin-bottom: 1rem;
    position: relative;
    overflow: hidden;
}
.accent-preview-stripe {
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 4px;
    background: var(--ws-color, hsl(var(--ws-hue, 220), 65%, 58%));
    border-radius: 4px 0 0 4px;
}
.accent-preview-text {
    flex: 1 1 auto;
    min-width: 0;
    display: flex; flex-direction: column;
    gap: .15rem;
    padding-left: .35rem;
}
.accent-preview-label {
    font-weight: 600;
    color: var(--text);
    font-size: .92rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.accent-preview-name {
    font-size: .8rem;
    color: var(--text-muted);
}
.accent-preview-swatch {
    flex: 0 0 auto;
    width: 28px; height: 28px;
    border-radius: 50%;
    background: var(--ws-color, hsl(var(--ws-hue, 220), 65%, 58%));
    box-shadow: inset 0 0 0 2px rgba(255,255,255,.4), 0 1px 3px rgba(15,23,42,.15);
}

/* 4 x 3 swatch grid. Smaller circles than before so they read
   as a palette, not a clown poster. */
.accent-swatches {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: .5rem;
}
.accent-swatch {
    aspect-ratio: 1 / 1;
    border-radius: 50%;
    border: 0;
    background: var(--ws-color, hsl(var(--ws-hue, 220), 65%, 58%));
    cursor: pointer;
    padding: 0;
    position: relative;
    transition: transform .12s ease;
}
.accent-swatch:hover { transform: scale(1.08); }
/* Active state: white check inside a soft inner ring - no
   secondary box-shadow halo (the previous "green ring around a
   yellow swatch" was hue-derived but read as a UI bug). */
.accent-swatch.is-active {
    box-shadow: inset 0 0 0 3px rgba(255,255,255,.55);
}
.accent-swatch.is-active::after {
    content: "";
    position: absolute; inset: 0;
    background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>") center/45% no-repeat;
    filter: drop-shadow(0 1px 1px rgba(0,0,0,.25));
}

/* Custom-colour swatch: rainbow conic gradient at rest with a
   hidden native <input type="color"> on top so the OS picker
   takes over on click. */
.accent-swatch-custom {
    background: conic-gradient(
        from 0deg,
        #EF4444, #F59E0B, #EAB308, #84CC16, #22C55E,
        #14B8A6, #06B6D4, #3B82F6, #8B5CF6, #EC4899, #EF4444);
    color: var(--text);
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
    overflow: hidden;
}
.accent-swatch-custom svg {
    width: 18px; height: 18px;
    background: var(--surface);
    border-radius: 50%;
    padding: 4px;
    box-shadow: 0 1px 2px rgba(15,23,42,.2);
}
.accent-swatch-custom input[type="color"] {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
    border: 0;
    padding: 0;
}

/* Footer row inside the modal-body: Reset on the left,
   Shuffle on the right. Both are text buttons, no heavy
   chrome; the Shuffle one gets the primary tint to read
   as the "do something different" action. */
.accent-modal-foot {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 1.1rem;
    padding-top: .85rem;
    border-top: 1px solid var(--border);
}
.accent-text-btn {
    display: inline-flex; align-items: center; gap: .35rem;
    background: transparent;
    border: 0;
    padding: .35rem .55rem;
    border-radius: 8px;
    font-size: .85rem;
    color: var(--text-muted);
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.accent-text-btn:hover { background: var(--surface-2); color: var(--text); }
.accent-text-btn-primary { color: var(--primary); }
.accent-text-btn-primary:hover { background: var(--primary-bg); color: var(--primary); }

/* ----- Notifications page (/Notifications) -----
   Personal-notifications hub. Three sections stack vertically:
   action-required (Awaiting), cancellable-by-me (Sent by you),
   and read-only stream (Recent). Visual hierarchy: the awaiting
   section wraps each row in a tinted card so it pops; the others
   use surface-1. */
/* Match the constrained reading width used by /Workspaces (880px)
   so the page doesn't sprawl across a 1440px display. The rows are
   single-line entries - the same width that works for the workspace
   cards reads well here too. */
.notif-page { max-width: 880px; padding-bottom: 3rem; }
.notif-section { margin-bottom: 1.75rem; }
.notif-section-header {
    display: flex; align-items: center; gap: .65rem;
    margin: .25rem 0 .65rem;
}
.notif-section-header h2 {
    font-size: .92rem; font-weight: 600; margin: 0;
    color: var(--text);
}
.notif-count-pill {
    font-size: .75rem; font-weight: 600;
    background: var(--surface-2); color: var(--text-muted);
    border-radius: 999px;
    min-width: 22px; padding: 1px 8px;
    display: inline-flex; align-items: center; justify-content: center;
}
.notif-section-awaiting .notif-count-pill {
    background: var(--primary-bg); color: var(--primary);
}
.notif-empty {
    padding: .9rem 1rem;
    color: var(--text-muted); font-size: .88rem;
    background: var(--surface); border: 1px dashed var(--border);
    border-radius: 10px;
}

.notif-list {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-direction: column; gap: .5rem;
}
.notif-row {
    display: flex; align-items: center; gap: .85rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: .75rem 1rem;
    transition: background .12s ease, border-color .12s ease, transform .12s ease;
}
.notif-row.is-unread {
    background: color-mix(in srgb, var(--primary-bg) 50%, var(--surface));
    border-color: color-mix(in srgb, var(--primary) 25%, var(--border));
}
.notif-row-action {
    /* Action rows live in the "Awaiting" section. Tinted bg makes
       them pop above the rest of the page so the user can spot
       what needs their attention without scanning the whole list. */
    background: color-mix(in srgb, var(--primary-bg) 60%, var(--surface));
    border-color: color-mix(in srgb, var(--primary) 25%, var(--border));
}
.notif-row-link {
    display: flex; align-items: center; gap: .85rem;
    flex: 1; min-width: 0;
    color: inherit; text-decoration: none;
}
.notif-row-link:hover { text-decoration: none; color: inherit; }
.notif-avatar {
    flex: 0 0 auto;
    width: 36px; height: 36px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--surface-2);
    color: var(--text-muted);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 600; font-size: .85rem;
}
.notif-avatar-blank {
    width: 36px; height: 36px; border-radius: 50%;
    background: var(--surface-2); color: var(--text-muted);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 600; font-size: .85rem;
    flex: 0 0 auto;
}
.notif-body { flex: 1; min-width: 0; }
.notif-line {
    display: flex; align-items: baseline; flex-wrap: wrap; gap: .35rem;
    font-size: .92rem; color: var(--text);
}
.notif-line strong { font-weight: 600; }
.notif-line em {
    font-style: normal; font-weight: 500;
    color: var(--text);
}
.notif-time {
    font-size: .78rem; color: var(--text-muted);
    margin-top: .15rem;
}
.notif-time-sep { margin: 0 .25rem; }
.notif-actions {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: .35rem;
}
.notif-reaction-chip {
    background: var(--surface-2);
    border-radius: 999px;
    padding: 1px 7px;
    font-size: .9rem;
    margin-left: .25rem;
}

/* Role chip on access-granted history rows. Color tracks the role
   so the audit reads at a glance: Admin = primary tint (notable),
   User = neutral, Member = neutral. Same chip shape across roles
   so the visual rhythm is preserved when scanning a long history. */
.notif-role-chip {
    display: inline-flex; align-items: center;
    background: var(--surface-2);
    color: var(--text-muted);
    border-radius: 999px;
    padding: 1px 8px;
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    margin-left: .35rem;
}
.notif-role-chip.notif-role-admin {
    background: var(--primary-bg);
    color: var(--primary);
}
.notif-role-chip.notif-role-owner {
    background: var(--primary);
    color: #fff;
}

/* ----- Status / Error pages -----
   Used by /Status/{code} and /Error. Centered single-column layout
   inside the auth chrome. Branded but neutral - no stack traces, no
   request internals beyond the optional request id (which we surface
   so a user reporting a bug can quote a value we can correlate to
   logs). Mirrors the auth pages' visual rhythm. */
.status-page {
    max-width: 480px;
    margin: 5vh auto 0;
    padding: 2.5rem 1.5rem;
    text-align: center;
}
.status-glyph {
    font-size: 4rem; line-height: 1;
    margin-bottom: .75rem;
    filter: grayscale(.1);
}
.status-code {
    font-family: "Plus Jakarta Sans", "Inter", sans-serif;
    font-weight: 800;
    font-size: 1rem;
    letter-spacing: .25em;
    color: var(--text-muted);
    margin-bottom: .25rem;
}
.status-title {
    font-family: "Plus Jakarta Sans", "Inter", sans-serif;
    font-size: 1.65rem;
    font-weight: 800;
    letter-spacing: -.02em;
    color: var(--text);
    margin: 0 0 .65rem;
}
.status-message {
    color: var(--text-muted);
    font-size: 1rem;
    line-height: 1.55;
    margin: 0 auto 1.5rem;
    max-width: 36ch;
}
.status-request-id {
    color: var(--text-subtle);
    font-size: .82rem;
    margin: 0 0 1.5rem;
}
.status-request-id code {
    background: var(--surface-2);
    padding: 1px 6px;
    border-radius: 5px;
    font-size: .8rem;
}
.status-actions {
    display: inline-flex; gap: .55rem;
    justify-content: center;
}

/* ========================================================================
   MOBILE & TABLET RESPONSIVE LAYER
   ========================================================================
   Single block of @media overrides that targets every layout flagged in
   the mobile audit. Kept at the end of the file (cascade wins) so the
   desktop rules stay readable above and the mobile concerns are easy
   to find. Three breakpoints:
     · max-width: 1024px → tablet (iPad landscape down)
     · max-width: 768px  → tablet portrait + small laptop
     · max-width: 540px  → mobile (iPhone, narrow Android)
     · max-width: 380px  → very narrow mobile
   Plus a (hover: none) block for touch devices regardless of width.
   ======================================================================== */

/* ----- Touch devices: reveal hover-only actions -----
   Notes today hide delete / resolve / react buttons until the parent
   is hovered. Touch has no hover, so on a phone the actions are
   permanently invisible. Pin them visible on any pointer:coarse
   device. Tradeoff: slightly noisier visual on tablets, worth it. */
@media (hover: none) {
    .note .note-del,
    .note .note-resolve,
    .note .note-react-btn {
        opacity: 1;
    }
    /* Tap targets: bump every icon-only button to a 44px minimum so
       fingers can hit them comfortably (iOS HIG / Android M3 both
       recommend 44dp / 48dp respectively). Padding extends the hit
       area without enlarging the visual icon. */
    .btn.icon-only,
    .nav-icon-btn,
    .sidebar-toggle-btn,
    .ws-members-pip,
    .notif-actions .btn {
        min-width: 44px;
        min-height: 44px;
    }
}

/* ----- Tablet (≤1024px) ----- */
@media (max-width: 1024px) {
    /* The dashboard hero stat tiles overflow the viewport on iPad
       portrait when 4-5 stats stack horizontally. Wrap them into a
       2-column grid that scales gracefully. */
    .home-hero-stats-col {
        flex-wrap: wrap;
    }
    .home-hero-stat {
        flex: 1 1 45%;
    }

    /* Compact navbar between the navbar-expand-md breakpoint (768px)
       and 1024px. The expanded navbar holds a lot of horizontal
       content (brand + 4-5 primary pills + presence stack + Actions
       Ctrl-K + Activity + Bell + Approvals + Shield + Theme + User
       menu). At iPad-portrait widths the right-side group spills
       past the viewport and the rightmost icon gets cropped. We
       trim the heaviest items (the cmd-k label/kbd, navbar
       horizontal padding, gap between tools) so everything fits
       inline without falling back to the mobile drawer.
       Below 768px Bootstrap collapses to the drawer (handled by
       the `Mobile navbar drawer` block above) so this rule only
       fires in the awkward 768-1024px band. */
    .app-nav .navbar {
        padding-left: .85rem;
        padding-right: .85rem;
    }
    .app-nav .nav-tools {
        gap: 1px;
        padding-left: .25rem;
        margin-left: .25rem;
    }
    /* "Actions Ctrl K" -> just the magnifier icon. The keyboard
       shortcut is still discoverable via the title attribute on
       hover, and Ctrl+K still works. */
    .app-nav .nav-cmdk-btn span,
    .app-nav .nav-cmdk-btn kbd {
        display: none;
    }
    .app-nav .nav-cmdk-btn {
        padding: .35rem .45rem;
    }
    /* Drop the user's display-name text next to the avatar - at
       this width the avatar alone identifies the menu, the name
       was eating ~150-200px and pushing the avatar off the right
       edge. The dropdown still shows the full email + name on
       open. The drawer mode (≤767.98px) re-surfaces the email
       inside the user-row card with its own rule. */
    .app-nav .user-menu .user-email {
        display: none;
    }
    /* The default Bootstrap dropdown chevron (::after triangle) on
       the user-menu adds ~12-15px of dead width once the email
       label is gone - it's just sitting next to the avatar.
       Suppress it: tapping the avatar already implies the menu. */
    .app-nav .user-menu.dropdown-toggle::after {
        display: none;
    }
    .app-nav .user-menu {
        padding: .15rem .25rem !important;
    }
    /* Pill labels on the primary-nav pills (Workspaces / Collaborators
       / Teams / Inboxes) - keep visible at iPad-landscape but the
       count pills + text are still wide. We don't hide labels
       (they're discoverable affordances) but tighten the inner
       padding so the row fits without crowding the right group. */
    .app-nav .nav-link-pill {
        padding: .4rem .55rem !important;
    }
}

/* ----- Tablet portrait + small laptop (≤768px) ----- */
@media (max-width: 768px) {
    /* Reduce page-inner padding so we recover horizontal space for
       content. Default is 2rem 1.5rem. */
    .app-page-inner {
        padding: 1.25rem 1rem;
    }

    /* Workspace card panel: relax the label column width and drop the
       reserved-right padding so the avatar pile gets full width. The
       Manage button moves into its own grid row instead of being
       absolute - on a phone, having the button stack below the
       members reads more clearly than overlapping. */
    .ws-members {
        grid-template-columns: 64px 1fr;
        padding: .65rem .7rem;
    }
    .ws-members-people {
        padding-right: 0;
    }
    .ws-members-manage {
        position: static;
        margin: .35rem 0 0;
        align-self: start;
        grid-column: 1 / -1;
        justify-self: end;
    }

    /* Message detail: 2rem horizontal padding on a 768px viewport
       eats too much. Trim. */
    .msg-container {
        padding: 1rem 1.25rem 2rem;
    }

    /* Activity / notifications feed entries: wrap long workspace
       labels onto a second line instead of clipping. */
    .activity-line,
    .notif-line {
        flex-wrap: wrap;
    }

    /* Notifications page: tighter padding + smaller section gaps */
    .notif-section {
        margin-bottom: 1.25rem;
    }

    /* Message-nav preview card was 380px fixed width. Cap to viewport. */
    .msg-nav-preview {
        max-width: calc(100vw - 32px);
        width: auto;
    }
}

/* ----- Mobile (≤540px) ----- */
@media (max-width: 540px) {
    /* Page chrome: tighten further on phones. */
    .app-page-inner {
        padding: 1rem .75rem;
    }
    .page-title {
        font-size: 1.4rem;
    }

    /* Workspace card: drop the role-label column width even more so
       the avatar pile gets every available pixel. Owner avatar is
       still 32px which is the minimum we want. */
    .ws-members {
        grid-template-columns: 56px 1fr;
        padding: .6rem;
    }
    .ws-members-rolelabel {
        font-size: .65rem;
    }

    /* Message detail: drop padding to 1rem all-round so emails with
       inline tables don't horizontal-scroll on a 360px viewport. */
    .msg-container {
        padding: .75rem .85rem 1.5rem;
    }

    /* Notifications: incoming-action rows had two side-by-side buttons
       (Accept + Decline). Stack them vertically on phones so each
       has full width and a 44px hit area. */
    .notif-row {
        flex-wrap: wrap;
        gap: .5rem .85rem;
    }
    .notif-actions {
        width: 100%;
        justify-content: stretch;
    }
    .notif-actions form {
        flex: 1;
    }
    .notif-actions .btn {
        width: 100%;
    }

    /* Mail: archive header stacks. Identity + count summary on top,
       command bar (filters / mode toggle / sort) on its own line. */
    .archive-header {
        flex-direction: column;
        align-items: stretch;
        gap: .65rem;
    }
    .archive-commandbar {
        flex-wrap: wrap;
    }
    /* Identity row (workspace name + Inbox/GROUPED pills + conv count)
       was 443px wide on a 375px viewport - overflowed and the count
       got clipped. Allow wrap so each piece can fall to a second line. */
    .archive-identity {
        flex-wrap: wrap;
        gap: .35rem;
    }

    /* Modals: every dialog gets a viewport-respecting width fallback.
       Bootstrap's .modal-dialog already does most of this but our
       custom dialogs (kbd-help, profile-card, etc.) need an explicit
       fallback. */
    .modal-dialog {
        margin: .5rem;
        max-width: calc(100vw - 1rem);
    }
    .modal-body {
        padding: 1rem;
    }
    .perm-modal-body,
    .share-modal-body {
        padding: 1rem;
    }

    /* Footer: stack zones vertically and shrink the status indicator
       so it doesn't push the brand wordmark off-screen. */
    .app-footer {
        flex-direction: column;
        align-items: center;
        gap: .65rem;
        text-align: center;
    }

    /* Hero stat tiles: single column on phones - 45% wraps to two
       cramped columns at 360px. Force one. */
    .home-hero-stat {
        flex: 1 1 100%;
    }
    .home-hero-split {
        flex-direction: column;
        gap: 1rem;
    }

    /* Activity feed avatar: shrink so the body has room. */
    .activity-avatar,
    .notif-avatar {
        width: 32px;
        height: 32px;
    }

    /* Workspace card header: the action buttons row (Open / Resume /
       settings / icon / trash) holds its full width via flex: 0 0 auto.
       On a 360px viewport that left only ~43px for the identity (name
       + email), making the email render one character per line. Stack
       identity and actions vertically so each gets the full row. */
    .account-row-header {
        flex-direction: column;
        align-items: stretch;
        gap: .55rem;
        padding: .75rem .85rem .65rem;
    }
    .account-actions {
        flex-wrap: wrap;
        justify-content: flex-end;
        gap: .35rem;
    }
    .account-identity {
        width: 100%;
    }
    /* Email subline: word-break:break-all is the safety net for the
       rare case the identity STILL can't fit (e.g. a 240px viewport
       like a folded Galaxy Z Flip). Without it, an unbreakable email
       string would push the layout wider than the viewport. */
    .account-sub.account-email-sub,
    .account-identity-text > * {
        word-break: break-word;
        overflow-wrap: anywhere;
    }

    /* Status pages: tighten margins for tiny viewports. */
    .status-page {
        margin-top: 2vh;
        padding: 1.5rem 1rem;
    }
    .status-glyph {
        font-size: 3rem;
    }
    .status-title {
        font-size: 1.35rem;
    }
}

/* ----- Very narrow mobile (≤380px) ----- */
@media (max-width: 380px) {
    /* iPhone SE sized devices and similar. Push everything to the
       absolute minimum that's still usable. */
    .app-page-inner {
        padding: .75rem .5rem;
    }

    /* Workspace card: collapse role label above the avatars instead
       of beside them - 56px column eats half the screen at 320px. */
    .ws-members {
        grid-template-columns: 1fr;
        gap: .35rem;
    }
    .ws-members-rolelabel {
        margin-top: .35rem;
    }
    .ws-members-line {
        /* display:contents passes through; rolelabel + people each
           occupy a full row in the 1-column grid above. */
    }

    /* Buttons inside notifications scroll horizontally when they
       can't fit. Force wrap. */
    .notif-line {
        font-size: .85rem;
    }
}

/* ----- Edge-pin floating popovers on small viewports -----
   The sync popover and search-help popover use absolute positioning
   that can shoot off-screen on narrow viewports. Cap their right
   edge to the viewport. */
@media (max-width: 768px) {
    .sync-pop-popover,
    .search-help-pop {
        right: .5rem !important;
        left: .5rem !important;
        max-width: calc(100vw - 1rem);
    }
}

/* ----- /Admin/Workspaces super-admin panel -----
   Dense table layout for platform-wide workspace inventory. Distinct
   from the user-facing /Workspaces card layout: rows are scannable
   horizontally so an operator can compare states at a glance. Color
   coding is deliberately desaturated; the most-saturated thing on
   the page is the Force-remove button so eyes go there only when
   the operator is looking. */
.admin-page { max-width: 1280px; }
/* Wide variant for admin pages whose content is dominated by a
   wide tabular log (Exceptions stack traces, EmailAudit failure
   reasons + URLs). Lifts the max-width near the viewport so the
   columns don't horizontally scroll on a normal laptop screen.
   Capped at 1920px so on a huge monitor the line lengths stay
   readable. */
.admin-page.admin-page-wide { max-width: min(1920px, 100%); }
.admin-counts { display: inline-flex; gap: .35rem; align-items: center; }
.admin-count-pill {
    display: inline-flex; align-items: center;
    background: var(--surface-2);
    color: var(--text-muted);
    font-size: .72rem;
    font-weight: 600;
    padding: 3px 9px;
    border-radius: 999px;
}
.admin-count-pill.admin-count-bad  { background: color-mix(in srgb, #ef4444 18%, var(--surface-2)); color: #b91c1c; }
.admin-count-pill.admin-count-warn { background: color-mix(in srgb, #f59e0b 18%, var(--surface-2)); color: #b45309; }

.admin-table-wrap {
    overflow-x: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
}
.admin-table {
    width: 100%;
    border-collapse: collapse;
    font-size: .88rem;
}

/* Sessions table: lock column widths via <colgroup> so the three
   stacked tables (Online / Recent / All accounts) align column-by-
   column. table-layout: fixed forces the browser to honour the
   col widths instead of auto-sizing on content. */
.admin-sessions-table { table-layout: fixed; }
.admin-sessions-table .col-user   { width: 22%; }
.admin-sessions-table .col-ping   { width: 14%; }
.admin-sessions-table .col-tabs   { width: 6%;  }
.admin-sessions-table .col-where  { width: 22%; }
.admin-sessions-table .col-device { width: 12%; }
.admin-sessions-table .col-ip     { width: 12%; }
.admin-sessions-table .col-login  { width: 12%; }
.admin-sessions-table td { word-break: break-word; }
.admin-sessions-table td code {
    font-size: .72rem;
    word-break: break-all;
}

/* Device-class pill: a small colored badge so tablet vs mobile vs
   desktop is glanceable in the admin overview. */
.admin-device-pill {
    display: inline-block;
    padding: 0 7px;
    border-radius: 999px;
    font-size: .68rem;
    font-weight: 600;
    line-height: 1.5;
    letter-spacing: .02em;
    background: var(--surface-2);
    color: var(--text-muted);
}
.admin-device-desktop { background: color-mix(in srgb, #6366f1 16%, var(--surface-2)); color: #4338ca; }
.admin-device-mobile  { background: color-mix(in srgb, #10b981 16%, var(--surface-2)); color: #047857; }
.admin-device-tablet  { background: color-mix(in srgb, #f59e0b 18%, var(--surface-2)); color: #b45309; }
.admin-device-bot     { background: color-mix(in srgb, #ef4444 16%, var(--surface-2)); color: #b91c1c; }
.admin-table th {
    text-align: left;
    padding: .75rem .85rem;
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-muted);
    background: var(--surface-2);
}
.admin-table td {
    padding: .75rem .85rem;
    border-bottom: 1px solid var(--border);
    vertical-align: top;
}
.admin-row.is-corrupt   td { background: color-mix(in srgb, #ef4444 4%, var(--surface)); }
.admin-row.is-orphaned  td { background: color-mix(in srgb, #f59e0b 4%, var(--surface)); }

.admin-ws-email { font-weight: 600; color: var(--text); }
.admin-ws-meta {
    color: var(--text-muted);
    font-size: .76rem;
    margin-top: 2px;
}
/* Cache-freshness meta line under the storage cell. Default is the
   muted ws-meta look; "stale" (>36h) goes amber, "never" goes red so
   the operator can scan a long table and spot rows that aren't being
   refreshed. Hover surfaces the longer explanation via title=. */
.admin-cache-meta {
    font-size: .7rem;
    margin-top: 4px;
    color: color-mix(in srgb, var(--text-muted) 80%, transparent);
}
.admin-cache-meta.is-stale {
    color: var(--warning, #B45309);
    font-weight: 500;
}
.admin-cache-meta.is-never {
    color: var(--danger, #B91C1C);
    font-weight: 500;
}
.admin-ws-id {
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: .68rem;
    margin-left: .35rem;
    color: var(--text-subtle);
}
.admin-orphan {
    color: #b45309;
    font-weight: 600;
    font-size: .8rem;
}
.admin-state {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: .7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    background: var(--surface-2);
    color: var(--text-muted);
}
.admin-state-idle      { background: color-mix(in srgb, #10b981 16%, var(--surface-2)); color: #047857; }
.admin-state-error     { background: color-mix(in srgb, #ef4444 16%, var(--surface-2)); color: #b91c1c; }
.admin-state-paused    { background: color-mix(in srgb, #f59e0b 16%, var(--surface-2)); color: #b45309; }
.admin-state-pending,
.admin-state-initial,
.admin-state-incremental { background: color-mix(in srgb, var(--primary) 16%, var(--surface-2)); color: var(--primary); }

.admin-sync-err {
    color: #b91c1c;
    font-size: .76rem;
    margin-top: 4px;
    max-width: 280px;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
}

.admin-col-actions { white-space: nowrap; }
.admin-col-actions form { margin-left: 4px; }

/* ----- Admin menu (dropdown from the shield icon in the top-right) ----- */
.admin-menu {
    min-width: 240px;
    padding: .25rem 0;
}
.admin-menu-header {
    padding: .65rem 1rem .35rem;
    border-bottom: 1px solid var(--border);
    margin-bottom: .25rem;
}
.admin-menu-header h6 {
    margin: 0; font-size: .9rem; font-weight: 700;
}
.admin-menu-sub {
    color: var(--text-muted); font-size: .72rem;
}
.admin-menu .dropdown-item {
    display: flex; align-items: center; gap: .55rem;
    padding: .45rem 1rem;
    font-size: .9rem;
}
.admin-menu .admin-menu-icon {
    width: 18px; text-align: center;
    filter: grayscale(.15);
}

/* ────────────────────────────────────────────────────────────────
   Super-admin dropdown - simple grouped list.
   Bootstrap-native show/hide. Two columns of plain rows. No
   tiles, no banners, no gradients, no fixed positioning. The
   compound 4-class selector with .show wins specificity against
   Bootstrap's defaults so display:grid only applies when open;
   the panel stays hidden the rest of the time.
   ──────────────────────────────────────────────────────────────── */

/* CSS columns instead of grid: each .admin-menu-group is treated as
   an atomic block (break-inside: avoid) and the browser's auto-
   balancing layouts them across two columns based on TOTAL HEIGHT,
   not group-count. The previous grid approach gave each group its
   own grid cell, so when one group had 1 item and another had 3
   the cells didn't visually align - leaving the right column with
   awkward bottom whitespace. Columns balance heights automatically
   so the panel reads as a clean two-column index. */
.dropdown-menu.admin-menu.admin-menu-grouped.show {
    display: block !important;
    column-count: 2;
    column-fill: balance;
    column-gap: 0;
}
/* Static chrome: applies whether the menu is open or closed; the
   browser only paints them when display goes from none → block.
   No position:fixed games - Bootstrap/Popper handles placement. */
.dropdown-menu.admin-menu.admin-menu-grouped {
    width: min(440px, calc(100vw - 32px));
    min-width: 0;
    max-width: calc(100vw - 32px);
    max-height: calc(100vh - 80px);
    overflow-y: auto;
    padding: .35rem 0;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, .12);
}

/* Header spans both columns. Without column-span:all the header
   would land in just the left column at the top. */
.admin-menu-grouped > .admin-menu-header {
    column-span: all;
    margin: 0;
    padding: .65rem 1rem .55rem;
    border-bottom: 1px solid var(--border);
}
.admin-menu-grouped .admin-menu-header h6 {
    margin: 0; font-size: .9rem; font-weight: 700;
    color: var(--text);
}
.admin-menu-grouped .admin-menu-sub {
    color: var(--text-muted); font-size: .72rem;
    display: block;
    margin-top: .1rem;
}

/* Group: simple vertical stack of rows. break-inside: avoid keeps
   each group as an atomic block - the browser's column balancer
   may move a group between columns to balance heights, but it
   never splits a group's items across columns (which would
   orphan a header from its rows). */
.admin-menu-grouped .admin-menu-group {
    padding: .35rem .25rem .25rem;
    min-width: 0;
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
    page-break-inside: avoid;
}
.admin-menu-grouped .admin-menu-group-label {
    padding: .35rem .85rem .15rem;
    font-size: .65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--text-subtle);
}
.admin-menu-grouped .dropdown-item {
    display: flex; align-items: center; gap: .55rem;
    padding: .4rem .85rem;
    font-size: .85rem;
    font-weight: 500;
    color: var(--text);
    border-radius: 0;
    background: transparent;
    border: 0;
    text-align: left;
    transition: background .12s ease, color .12s ease;
}
.admin-menu-grouped .dropdown-item:hover,
.admin-menu-grouped .dropdown-item:focus {
    background: var(--surface-2);
    color: var(--text);
}
.admin-menu-grouped .admin-menu-icon {
    width: 18px; text-align: center;
    filter: none;
    flex: 0 0 auto;
}

/* Phone: collapse to a single column. column-count:1 reflows the
   groups into a single vertical list - natural reading order on
   narrow screens. */
@media (max-width: 480px) {
    .dropdown-menu.admin-menu.admin-menu-grouped.show {
        column-count: 1;
    }
}

/* ─────────── Logo-color icon cycle (cyan / purple / pink) ─────────── */
/* The shield is brand-blue (operator/security); the panel underneath
   uses the 3-bar logo palette to signal "still inside the brand."
   Each row carries .mc-1/.mc-2/.mc-3 in markup order so the cycle is
   visually consistent regardless of grid column flow. Colors are
   #06B6D4 / #8B5CF6 / #EC4899 - the same hex used in the SVG mark. */
.admin-menu-grouped .dropdown-item.mc-1 .admin-menu-icon { color: #06B6D4; }
.admin-menu-grouped .dropdown-item.mc-2 .admin-menu-icon { color: #8B5CF6; }
.admin-menu-grouped .dropdown-item.mc-3 .admin-menu-icon { color: #EC4899; }

/* ─────────── Vertical hairline between the 2 columns ─────────── */
/* CSS `column-rule` paints the divider between the auto-balanced
   columns. Replaces the previous nth-child border-right approach
   (which was tied to the now-removed CSS Grid layout). The rule
   only renders when there's actually a second column with content,
   so the phone single-column collapse takes care of itself. */
@media (min-width: 481px) {
    .dropdown-menu.admin-menu.admin-menu-grouped.show {
        column-rule: 1px solid var(--border);
    }
}

/* ─────────── Active-state highlight (current page) ─────────── */
/* When the operator is already on, e.g., /Admin/Users, the Users row
   gets a left-edge accent stripe and a subtly tinted background so
   their bearing is obvious at a glance. The accent uses the same
   badge-blue as the shield itself for a coherent "operator zone"
   palette. */
.admin-menu-grouped .dropdown-item.is-active {
    background: color-mix(in srgb, #2563eb 8%, transparent);
    color: var(--text);
    box-shadow: inset 3px 0 0 0 #2563eb;
}
.admin-menu-grouped .dropdown-item.is-active:hover,
.admin-menu-grouped .dropdown-item.is-active:focus {
    background: color-mix(in srgb, #2563eb 14%, transparent);
}

/* ─────────── Count badges (Exceptions, Email audit) ─────────── */
/* Right-aligned pill that appears only when count > 0. Two color
   variants: red for unacknowledged exceptions (operator action
   required), amber for email-send failures (informational, may
   self-resolve). */
.admin-menu-grouped .admin-menu-badge {
    margin-left: auto;
    flex: 0 0 auto;
    min-width: 1.4rem;
    padding: 0 .4rem;
    height: 1.1rem;
    border-radius: .55rem;
    font-size: .68rem;
    font-weight: 700;
    line-height: 1.1rem;
    text-align: center;
    color: #fff;
    letter-spacing: .02em;
}
.admin-menu-grouped .admin-menu-badge.admin-menu-badge-error {
    background: #dc2626;
}
.admin-menu-grouped .admin-menu-badge.admin-menu-badge-warn {
    background: #d97706;
}

/* Shield button: filled in a saturated badge-blue - the
   conventional "security / protection" color used on police and
   security iconography. Distinct from the brand primary purple
   (which reads as a nav-action) AND from the neutral nav icons
   so the operator-only affordance is unmistakable at a glance. */
.app-nav .nav-icon-btn.admin-shield-btn {
    color: #2563eb;
}
.app-nav .nav-icon-btn.admin-shield-btn:hover,
.app-nav .nav-icon-btn.admin-shield-btn:focus-visible,
.app-nav .nav-icon-btn.admin-shield-btn.is-active {
    color: #1d4ed8;
    background: color-mix(in srgb, #2563eb 14%, transparent);
}

/* ----- Admin dashboard stat tiles ----- */
.admin-section-title {
    font-size: .82rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-muted);
    margin: 1.5rem 0 .65rem;
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
}
/* Live-progress meta tag next to a section title.
   Used by the runtime cards (live · 22:34:01) and the
   integrity-probe hydrator ("probing 3 / 12", "done · all healthy"). */
.admin-runtime-meta {
    font-size: .68rem;
    font-weight: 500;
    text-transform: none;
    letter-spacing: .01em;
    color: color-mix(in srgb, var(--text-muted) 80%, transparent);
    font-style: italic;
}
/* Integrity-probe placeholder + result states. The placeholder
   pulses softly so the operator knows a row is in flight; the
   resolved states match the eager-loop copy ("✓ OK" / "✗ X"). */
.admin-integrity-status.is-checking {
    color: var(--text-muted);
    font-style: italic;
    animation: admin-integrity-pulse 1.4s ease-in-out infinite;
}
.admin-integrity-status.is-good {
    color: var(--success, #047857);
    font-weight: 500;
}
.admin-integrity-status.is-bad {
    color: var(--danger, #B91C1C);
    font-weight: 600;
}
@keyframes admin-integrity-pulse {
    0%, 100% { opacity: 0.55; }
    50%      { opacity: 1; }
}
.admin-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: .65rem;
    margin-bottom: 1rem;
}
.admin-stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: .85rem 1rem;
}
.admin-stat-label {
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-muted);
    font-weight: 600;
}
.admin-stat-value {
    font-family: "Plus Jakarta Sans", "Inter", sans-serif;
    font-size: 1.7rem;
    font-weight: 700;
    color: var(--text);
    line-height: 1.1;
    margin: .35rem 0 .2rem;
}
.admin-stat-sub {
    font-size: .72rem;
    color: var(--text-muted);
}
.admin-stat.is-bad { border-color: color-mix(in srgb, #ef4444 30%, var(--border)); background: color-mix(in srgb, #ef4444 6%, var(--surface)); }
.admin-stat.is-bad .admin-stat-value { color: #b91c1c; }
.admin-stat.is-warn { border-color: color-mix(in srgb, #f59e0b 30%, var(--border)); background: color-mix(in srgb, #f59e0b 6%, var(--surface)); }
.admin-stat.is-warn .admin-stat-value { color: #b45309; }

/* Search + filter bars on Users / AuditLog */
.admin-search,
.admin-filter-bar {
    display: inline-flex; align-items: center; gap: .35rem;
    margin-bottom: 1rem;
}
.admin-filter-bar .form-select,
.admin-filter-bar .form-control,
.admin-search .form-control {
    width: auto; min-width: 200px;
}

/* Files page breadcrumb */
.admin-breadcrumb {
    font-size: .82rem;
    color: var(--text-muted);
    margin-bottom: .5rem;
    word-break: break-all;
}
.admin-breadcrumb code {
    background: var(--surface-2);
    padding: 1px 6px;
    border-radius: 4px;
}

/* Highlight super-admin user row */
.admin-row.is-superadmin td {
    background: color-mix(in srgb, var(--primary) 4%, var(--surface));
}

/* Birthday 3-select picker. Three side-by-side dropdowns sized to
   their content - capped via max-width so on a wide form the
   picker reads as a compact widget rather than spanning the whole
   row. Stacks vertically below 480px so each select gets full
   width on a phone. */
.birthday-picker-row {
    display: flex;
    gap: .5rem;
    max-width: 360px;
}
.birthday-picker-row .form-select:nth-child(1) { flex: 0 0 80px;  } /* Day */
.birthday-picker-row .form-select:nth-child(2) { flex: 1 1 auto;  } /* Month */
.birthday-picker-row .form-select:nth-child(3) { flex: 0 0 100px; } /* Year */
@media (max-width: 480px) {
    .birthday-picker-row {
        flex-direction: column;
        max-width: none;
    }
    .birthday-picker-row .form-select { flex: 1 1 auto; }
}

/* ----- /Admin/Database -----
   Database admin page: file stats, table inventory, row browser.
   The browse table can be very wide (some tables have 15+ columns)
   so it scrolls horizontally inside its wrapper instead of pushing
   the page itself wide. */
.admin-ops-row {
    display: inline-flex; align-items: center; gap: .35rem;
    margin-bottom: 1rem;
}
.admin-sql {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: .55rem .75rem;
    margin: .35rem 0 0;
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: .78rem;
    line-height: 1.4;
    overflow-x: auto;
    white-space: pre-wrap;
    word-break: break-word;
    max-width: 600px;
}
.admin-browse-wrap {
    /* Horizontal scroll inside the panel keeps the rest of the
       layout sane when a wide table is shown. */
    overflow-x: auto;
    max-height: 70vh;
    overflow-y: auto;
}
.admin-browse {
    font-size: .82rem;
    /* Allow column auto-sizing so identifier columns stay narrow
       while wide TEXT columns get the room they need. */
    table-layout: auto;
    min-width: 100%;
}
.admin-browse th {
    position: sticky; top: 0;
    z-index: 1;
    background: var(--surface-2);
    /* Sticky header alongside vertical scroll so the column
       names stay visible while the operator scrolls through
       a thousand rows. */
}
.admin-browse .admin-cell {
    /* Cap individual cell width visually; the underlying value
       stays in the DOM (already truncated to 240 chars by the
       handler) so a long token doesn't blow out the column. */
    display: inline-block;
    max-width: 360px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
}
.admin-pagination {
    display: inline-flex; align-items: center; gap: .65rem;
    margin: .65rem 0 1rem;
    padding: .35rem .55rem;
}
.admin-pagination .is-disabled {
    pointer-events: none;
    opacity: .45;
}
