/* ============================================ */
/* Odysseus UI — Consolidated Stylesheet        */
/* ============================================ */


/* ── Variables ──
 *
 * Theme-public variables (override via theme.js or custom themes):
 *   Core:     --bg, --fg, --panel, --border, --red
 *   Syntax:   --hl-keyword, --hl-string, --hl-comment, --hl-function,
 *             --hl-number, --hl-builtin, --hl-variable, --hl-params,
 *             --hl-bg, --hl-fg
 *   Accents:  --accent-primary, --accent-error (set by theme.js)
 *   Semantic: --color-error, --color-success, --color-warning, --color-danger,
 *             --color-accent, --color-muted, --color-muted-alt
 */

:root {
  /* Core palette */
  --bg: #282c34;
  --fg: #9cdef2;
  --panel: #111;
  --border: #355a66;
  --red: #e06c75;
  /* Were `var(--green)` / `var(--warn)` — self-referential, so they
     resolved to invalid and every site fell back to its own literal
     (or, for sites with no fallback, painted as transparent/inherit).
     Anchor them to real hex so the token layer actually works. */
  --green: #50fa7b;
  --warn: #f0ad4e;

  /* Syntax highlighting */
  --hl-bg: #1e2228;
  --hl-fg: #9cdef2;
  --hl-keyword: #c678dd;
  --hl-string: #e5c07b;
  --hl-comment: #828997;
  --hl-function: #61afef;
  --hl-number: #d19a66;
  --hl-builtin: #56b6c2;
  --hl-variable: #abb2bf;
  --hl-params: #a8c0d4;

  /* Semantic colors */
  --color-error: #ff4444;
  --color-error-light: #ff6666;
  --color-success: #4caf50;
  --color-warning: #f0ad4e;
  --color-danger: #c0392b;
  --color-recording: #ff3b30;
  --color-recording-hover: #d63031;
  --color-muted: #888;
  --color-muted-alt: #6b7280;
  --color-accent: #00aaff;
  --color-agent-active: #00ff00;
  --color-brand-blue: #3b82f6;
  --color-blind-orange: #ff9800;
  --color-save-green: var(--color-success);
  --color-link-hover: #66c7ff;
  --color-subheader: #6b8a94;
  --select-bg: var(--bg);
  --select-fg: var(--fg);
  --select-option-bg: color-mix(in srgb, var(--panel) 74%, var(--bg));
  --select-option-fg: var(--fg);
  --select-option-active-bg: color-mix(in srgb, var(--accent, var(--red)) 24%, var(--panel));
  /* Warm accent — used by the Goals/Today UI in Notes. Lives as a token so
     themes can override without touching the goal CSS. */
  --accent-warm: #d19a66;
}

:root.light {
  --bg: #f5f5f5;
  --fg: #2b2b2b;
  --panel: #fff;
  --border: #bbb;
  --hl-bg: #f9f9f9;
  --hl-fg: #2b2b2b;
  --hl-keyword: #7928a1;
  --hl-string: #986801;
  --hl-comment: #6a737d;
  --hl-function: #005cc5;
  --hl-number: #986801;
  --hl-builtin: #0070a0;
  --hl-variable: #383a42;
  --hl-params: #4a4f5c;
  --select-bg: #eaeaea;
  --select-fg: var(--fg);
  --select-option-bg: var(--panel);
  --select-option-fg: var(--fg);
  --select-option-active-bg: color-mix(in srgb, var(--red) 16%, var(--panel));
}

/* ── Reset & Base ── */

* { box-sizing: border-box; }
html, body { overflow-x: hidden; height: 100%; margin: 0; overscroll-behavior: none; }
body {
  background-color: var(--bg);
  color: var(--fg);
  /* Animate the dock push BOTH ways. Keeping the transition on the base body
     (not on .right/left-dock-active) means removing the class on undock also
     animates padding back to 0 — otherwise the chat snapped back instantly. */
  transition: padding-left 160ms cubic-bezier(0.22, 0.61, 0.36, 1),
              padding-right 160ms cubic-bezier(0.22, 0.61, 0.36, 1);
  font-family: var(--font-family, 'Fira Code', monospace);
  display: flex;
  height: 100%;
  height: 100dvh; /* dynamic viewport height — adapts when mobile keyboard opens */
  overflow: hidden;
}

/* Self-hosted Fira Code font */
@font-face { font-family: 'Fira Code'; font-weight: 300; font-style: normal; font-display: swap; src: url('/static/fonts/FiraCode-Light.woff2') format('woff2'); }
@font-face { font-family: 'Fira Code'; font-weight: 400; font-style: normal; font-display: swap; src: url('/static/fonts/FiraCode-Regular.woff2') format('woff2'); }
@font-face { font-family: 'Fira Code'; font-weight: 600; font-style: normal; font-display: swap; src: url('/static/fonts/FiraCode-SemiBold.woff2') format('woff2'); }

/* Code block baseline */
pre, code, .hljs {
  font-size: 0.95em;
  line-height: 1.5;
}

/* Scrollbar styling */
@supports selector(::-webkit-scrollbar) {
  ::-webkit-scrollbar {
    width: 8px;
    height: 8px;
  }
  ::-webkit-scrollbar-track {
    background: var(--panel);
  }
  ::-webkit-scrollbar-thumb {
    background-color: var(--red);
    border-radius: 4px;
    border: 2px solid var(--panel);
  }
  ::-webkit-scrollbar-thumb:hover {
    background-color: color-mix(in srgb, var(--red) 80%, white);
  }
}

html {
  scrollbar-color: var(--red) var(--panel);
  scrollbar-width: thin;
}

/* Utility */
.red-text { color: var(--red); }

/* ── Density Overrides ── */

:root.density-compact { font-size: 13px; }
:root.density-compact .msg { padding: 6px 10px; margin-bottom: 4px; }
:root.density-compact .list-item { padding: 4px 8px; }
:root.density-compact .sidebar .section { padding: 0; }
:root.density-spacious { font-size: 16px; }
:root.density-spacious .msg { padding: 14px 18px; margin-bottom: 12px; }
:root.density-spacious .list-item { padding: 8px 12px; }
:root.density-spacious .sidebar .section { padding: 0; }

/* ── Background Patterns ── */

:root { --bg-effect-intensity: 1; }

/* Canvas-based effects — single source of truth for intensity */
#synapse-canvas, #rain-canvas, #constellations-canvas,
#perlin-flow-canvas, #petals-canvas, #sparkles-canvas,
#embers-canvas {
  opacity: var(--bg-effect-intensity, 1);
}

body.bg-pattern-dots {
  background-image: radial-gradient(color-mix(in srgb, var(--bg-effect-color, var(--fg)) calc(5% * var(--bg-effect-intensity, 1)), transparent) 1px, transparent 1px);
  background-size: 20px 20px; background-attachment: fixed;
}
body.bg-pattern-synapse {
  /* CSS grid as base, canvas pulses overlay */
  background-image: linear-gradient(color-mix(in srgb, var(--bg-effect-color, var(--fg)) calc(3.5% * var(--bg-effect-intensity, 1)), transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--bg-effect-color, var(--fg)) calc(3.5% * var(--bg-effect-intensity, 1)), transparent) 1px, transparent 1px);
  background-size: 24px 24px; background-attachment: fixed;
}
body.bg-pattern-perlin-flow,
body.bg-pattern-petals,
body.bg-pattern-sparkles {
  /* canvas-only backgrounds */
}


/* ── Layout ── */

    /* Top bar — session meta + incognito, single row */
    .chat-top-bar {
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
      position: relative;
      z-index: 2;
      padding: 5px 0 0;
      min-height: 25px;
      box-sizing: border-box;
    }
    .chat-top-bar::after {
      content: none;
    }
    body.sidebar-collapsed.hamburger-left .chat-top-bar {
      padding-left: 38px;
    }
    body.sidebar-collapsed.hamburger-right .chat-top-bar {
      padding-right: 38px;
    }
    .incognito-indicator {
      position: absolute;
      left: 12px;
      top: 50%;
      transform: translateY(-50%);
      background: color-mix(in srgb, var(--accent) 12%, transparent);
      border: 1px solid var(--accent);
      color: var(--accent);
      cursor: pointer;
      padding: 4px;
      border-radius: 6px;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: opacity 0.15s, background 0.15s, left 0.15s;
      z-index: 1;
      opacity: 0.7;
    }
    body.sidebar-collapsed.hamburger-left .incognito-indicator {
      left: 12px;
    }
    .incognito-indicator:hover {
      opacity: 1;
      background: color-mix(in srgb, var(--accent) 20%, transparent);
    }

    .chat-new-btn {
      position: absolute;
      right: 7px;
      top: 50%;
      transform: translateY(-50%);
      background: none;
      border: none;
      color: var(--fg);
      opacity: 0.6;
      cursor: pointer;
      padding: 4px;
      border-radius: 6px;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: opacity 0.08s, background 0.08s, left 0.15s, right 0.15s;
      flex-shrink: 0;
      z-index: 1;
    }
    .chat-new-btn:hover {
      opacity: 1;
      color: var(--accent);
    }
    /* Flip new-chat and incognito when sidebar is on the right */
    body:has(.sidebar.right-side) .chat-new-btn {
      right: auto;
      left: 12px;
    }
    body:has(.sidebar.right-side) .incognito-indicator {
      left: auto;
      right: 12px;
    }
    body.sidebar-collapsed.hamburger-right .incognito-indicator {
      right: 42px;
    }

    /* Session meta — sits at top of chat area, scrolls with content */
    .chat-meta-overlay {
      position: absolute;
      left: 0;
      right: 0;
      top: 50%;
      transform: translateY(calc(-50% - 2px));
      font-size: 0.75em;
      line-height: 1;
      /* 70% mix keeps the chat title clearly above the WCAG AA 4.5:1
         contrast threshold (40% only reached ~2.8:1). */
      color: color-mix(in srgb, var(--fg) 70%, transparent);
      white-space: nowrap;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 2px;
      pointer-events: none;
    }
    .chat-meta-overlay > * {
      pointer-events: auto;
    }
    .chat-meta-overlay #current-meta {
      overflow: hidden;
      text-overflow: ellipsis;
      cursor: pointer;
    }
    .chat-meta-overlay:hover {
      color: color-mix(in srgb, var(--fg) 75%, transparent);
    }
    /* Offline model rows */
    .models-row-offline {
      opacity: 0.4;
      pointer-events: none;
    }
    .models-row-offline .model-chat-btn {
      pointer-events: auto;
    }
    /* Offline badge next to endpoint name */
    .endpoint-offline-badge {
      color: var(--danger, #f44);
      font-size: 0.8em;
      margin-left: 4px;
      opacity: 0.7;
    }
    .chat-meta-overlay:empty,
    .chat-meta-overlay:not(:has(#current-meta:not(:empty))) {
      display: none;
    }
    .export-dropdown-wrap {
      position: relative;
      display: inline-flex;
      flex-shrink: 0;
      margin-left: -4px;
      margin-right: -20px;
    }
    .export-dl-btn {
      background: none; border: none;
      color: inherit;
      cursor: pointer;
      padding: 4px 5px; border-radius: 4px;
      display: flex; align-items: center;
      transition: color 0.08s, background 0.08s;
    }
    .export-dl-btn:hover {
      color: var(--accent);
      background: color-mix(in srgb, var(--accent) 12%, transparent);
    }
    .export-dropdown-menu {
      display: none;
      position: fixed;
      background: var(--panel); border: 1px solid var(--border);
      border-radius: 8px; padding: 4px; min-width: 120px;
      box-shadow: 0 4px 16px rgba(0,0,0,0.4);
      z-index: 300;
      color: var(--fg);
    }
    .export-dropdown-menu.open { display: block; }
    .export-dropdown-item {
      padding: 6px 8px; font-size: 11px; cursor: pointer;
      border-radius: 6px; color: var(--fg); white-space: nowrap;
      display: flex; align-items: center; gap: 10px;
      transition: background 0.1s;
    }
    .export-dropdown-item:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
    .export-dropdown-item .dropdown-icon {
      width: 14px; height: 14px;
      display: flex; align-items: center; justify-content: center;
      flex-shrink: 0; opacity: 0.5;
    }
    /* On mobile the chat-header export dropdown was cramped — items
       were 11px font with 6px padding. Bump to readable touch targets:
       larger min-width, taller rows, bigger icons + text. */
    @media (max-width: 768px) {
      .export-dropdown-menu {
        min-width: 200px;
        padding: 6px;
        border-radius: 10px;
      }
      .export-dropdown-item {
        padding: 12px 14px;
        font-size: 14px;
        gap: 12px;
        min-height: 44px;
      }
      .export-dropdown-item .dropdown-icon {
        width: 18px; height: 18px; opacity: 0.7;
      }
      .export-dropdown-item .dropdown-icon svg {
        width: 18px; height: 18px;
      }
    }
    .sidebar {
      width: 240px;
      background: var(--sidebar-bg, var(--panel));
      border-right: 1px solid var(--border);
      transition: width 0.25s ease, opacity 0.2s ease, padding 0.25s ease;
      display: flex;
      flex-direction: column;
      flex-shrink: 0;
      overflow: hidden;
      min-height: 0;
      margin: 0;
      padding: 0;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
      backdrop-filter: blur(10px);
      border: 1px solid color-mix(in srgb, var(--fg) 11%, transparent);
      position: relative;
    }
    .sidebar-resize-handle {
      position: absolute;
      top: 0;
      right: -3px;
      width: 6px;
      height: 100%;
      cursor: col-resize;
      z-index: 10;
      background: transparent;
    }
    .sidebar-resize-handle:hover,
    .sidebar-resize-handle.dragging {
      background: var(--accent);
      opacity: 0.6;
    }
    .sidebar.right-side .sidebar-resize-handle {
      right: auto;
      left: -3px;
    }
    .sidebar.resizing {
      transition: none;
      user-select: none;
    }
    .sidebar.right-side {
      order: 2;
      margin: 0;
      border-right: none;
      border-left: 1px solid var(--border);
    }
    .sidebar.hidden {
      /* !important so it beats the inline width init.js restores from storage —
         otherwise the width never changes and only opacity animates, making the
         collapse look instant. With this, width animates from the inline value
         down to 0 via the .sidebar width transition. */
      width: 0 !important;
      padding: 0 !important;
      border: none;
      overflow: hidden;
      opacity: 0;
    }
    /* ===== Sidebar User Bar ===== */
    .sidebar-user-bar {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 12px 12px;
      flex-shrink: 0;
      gap: 4px;
      min-height: 48px;
    }

    .user-bar-left {
      display: flex;
      align-items: center;
      gap: 10px;
      flex: 1;
      min-width: 0;
      cursor: pointer;
      padding: 6px 8px;
      border-radius: 8px;
      transition: background 0.15s;
    }

    .user-bar-left:hover {
      background: color-mix(in srgb, var(--fg) 6%, transparent);
    }

    .user-bar-avatar {
      width: 24px;
      height: 24px;
      border-radius: 50%;
      background: color-mix(in srgb, var(--fg) 12%, transparent);
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 10px;
      font-weight: 600;
      color: var(--fg);
      opacity: 0.7;
      flex-shrink: 0;
      text-transform: uppercase;
    }

    .user-bar-name {
      font-size: 9.75px;
      font-weight: 500;
      color: var(--fg);
      opacity: 0.8;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .user-bar-actions {
      display: flex;
      gap: 1px;
      flex-shrink: 0;
    }

    .user-bar-btn {
      background: none;
      border: none;
      color: var(--fg);
      opacity: 0.35;
      cursor: pointer;
      padding: 6px;
      border-radius: 6px;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: opacity 0.12s, background 0.12s;
    }

    .user-bar-btn:hover {
      opacity: 1;
      background: color-mix(in srgb, var(--fg) 8%, transparent);
    }


    .sidebar.hidden .sidebar-user-bar {
      display: none;
    }

    /* Sticky sidebar header — logo, never scrolls */
    .sidebar-header {
      display: flex;
      align-items: center;
      justify-content: flex-end; /* right-align when sidebar is on left */
      gap: 8px;
      padding: 15px 10px 0 40px; /* top padding aligns logo with fixed hamburger */
      flex-shrink: 0;
      min-height: 40px;
      border: none !important;
      box-shadow: none !important;
      position: relative;
      z-index: 3;
      background: var(--sidebar-bg, var(--panel));
    }
    .sidebar-hamburger {
      display: none !important; /* external #hamburger-btn is the only toggle */
    }
    .sidebar-inner {
      flex: 1;
      overflow-y: auto;
      overflow-x: hidden;
      overscroll-behavior-y: none;
      scrollbar-width: none;
      display: flex;
      flex-direction: column;
      gap: 0;
      padding: 10px 8px 8px;
      min-height: 0;
      transition: transform 0.3s cubic-bezier(0.25, 1, 0.5, 1);
    }
    .sidebar-brand {
      display: flex;
      align-items: center;
      flex-shrink: 0;
      min-height: 24px;
    }
    .sidebar-brand-title {
      font-size: 1rem;
      font-weight: 600;
      line-height: 1.35;
      color: var(--brand-color, var(--red));
      white-space: nowrap;
      user-select: none;
      position: relative;
      top: 0;
      left: -10px;
    }
    .sidebar-sep {
      display: none;
    }
    #sidebar-search-btn,
    #sidebar-new-chat-btn {
      margin: 0;
      padding: 8px 8px;
    }
    #sessions-section {
      margin-top: -1px;
    }
    #tools-section {
      margin-top: 1px;
    }
    #tools-section .list-item {
      padding: 8px 8px;
    }
    .sidebar.right-side .sidebar-header {
      justify-content: flex-start;
      padding-left: 10px;
      padding-right: 40px;
    }
    .sidebar.right-side .sidebar-inner {
      padding: 8px;
    }
    .sidebar.right-side .sidebar-brand {
      justify-content: flex-start;
      padding: 2px 30px 4px 4px;
    }
    .sidebar.right-side .sidebar-brand-title {
      margin-left: 10px;
    }
    /* Fixed hamburger — always visible, toggles sidebar */
    .hamburger-btn {
      position: fixed;
      top: 12px;
      left: 9px;
      right: auto;
      z-index: 210;
      width: 30px;
      height: 30px;
      align-items: center;
      justify-content: center;
      background: none;
      border: none;
      color: var(--hamburger-color, var(--fg));
      cursor: pointer;
      opacity: 0.5;
      -webkit-tap-highlight-color: transparent;
      outline: none;
      transition: opacity 0.15s;
      padding: 0;
      display: flex;
    }
    body.hamburger-right .hamburger-btn {
      left: auto;
      right: 9px;
    }
    .mobile-new-chat-btn {
      display: none;
    }
    .hamburger-btn:hover {
      opacity: 1;
    }
    /* Icon rail — mini sidebar that replaces the wide .sidebar when it's
       hidden (mutually exclusive — see sidebar-layout.js:57). Fullscreen
       panels reserve this strip of width via `left: var(--icon-rail-w)`
       so the rail stays visible without needing a z-index hack (which
       used to cover the fixed-position hamburger button). */
    .icon-rail {
      width: 48px;
      flex-shrink: 0;
      background: var(--panel);
      border-right: 1px solid var(--border);
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 48px 4px 8px 4px;
      gap: 4px;
      margin: 0;
      position: relative;
      box-sizing: border-box;
      /* Allow hover labels (e.g. the rail-new-chat "New" tooltip) to extend
         outside the 48px column. overflow:hidden was clipping them. */
      overflow: visible;
    }
    .rail-resize-handle {
      position: absolute;
      top: 0;
      right: -3px;
      width: 6px;
      height: 100%;
      cursor: col-resize;
      z-index: 10;
      background: transparent;
    }
    .rail-resize-handle:hover,
    .rail-resize-handle.dragging {
      background: var(--accent);
      opacity: 0.6;
    }
    .icon-rail.right-side .rail-resize-handle {
      right: auto;
      left: -3px;
    }
    .icon-rail.right-side {
      order: 2;
      margin: 0;
      border-right: none;
      border-left: 1px solid var(--border);
    }
    .icon-rail-divider {
      width: 24px;
      height: 1px;
      background: var(--border);
      margin: 4px 0;
    }
    .icon-rail-btn {
      position: relative;
      width: 34px;
      height: 34px;
      border: none;
      background: transparent;
      color: var(--accent, var(--red));
      font-size: 16px;
      border-radius: 6px;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      opacity: 0.5;
      transition: opacity 0.08s, background 0.08s;
    }
    .rail-notes-badge {
      position: absolute;
      top: 1px;
      right: 1px;
      min-width: 14px;
      height: 14px;
      padding: 0 3px;
      border-radius: 7px;
      background: color-mix(in srgb, var(--accent) 85%, var(--bg));
      color: var(--bg);
      font-size: 9px;
      font-weight: 700;
      line-height: 14px;
      text-align: center;
      box-sizing: border-box;
      pointer-events: none;
      box-shadow: 0 0 0 1px var(--bg);
    }
    .rail-notes-badge.fired {
      background: var(--red);
      animation: rail-notes-pulse 1.6s ease-in-out infinite;
    }
    @keyframes rail-notes-pulse {
      0%, 100% { box-shadow: 0 0 0 1px var(--bg), 0 0 0 0 color-mix(in srgb, var(--red) 50%, transparent); }
      50% { box-shadow: 0 0 0 1px var(--bg), 0 0 0 4px color-mix(in srgb, var(--red) 15%, transparent); }
    }
    /* Main sidebar notes button — dot when a reminder has fired */
    .tool-notes-dot {
      display: inline-block;
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: var(--red);
      /* Match the Deep Research badge: right-aligned (auto) with the same
         4px left nudge, so both sidebar buttons' dots line up identically. */
      margin-left: auto;
      position: relative;
      left: -4px;
      flex-shrink: 0;
      align-self: center;
      animation: rail-notes-pulse 1.6s ease-in-out infinite;
      pointer-events: none;
    }
    /* Individual card — subtle accent border tint when a reminder has fired */
    .note-card.note-card-reminder-due .note-card-reminder {
      background: color-mix(in srgb, var(--red) 22%, transparent);
      color: var(--red);
      font-weight: 600;
    }
    .icon-rail-btn:hover { opacity: 1; background: color-mix(in srgb, var(--accent) 12%, transparent); }
    .icon-rail-btn.active-section { opacity: 1; background: color-mix(in srgb, var(--color-accent) 15%, transparent); }
    /* Unified "minimized" indicator for any rail/sidebar button whose modal is held open */
    .rail-minimized { position: relative; }
    .rail-minimized::after {
      content: ''; position: absolute;
      /* Default for inline spans like #email-section-title — sit just to the
         right of the element so it never overlaps the icon. */
      top: 50%; right: -10px; transform: translateY(-50%);
      width: 6px; height: 6px; border-radius: 50%;
      background: var(--accent, var(--red));
      box-shadow: 0 0 0 2px var(--bg);
      pointer-events: none;
      animation: rail-min-pulse 2s ease-in-out infinite;
    }
    /* Compact icon-rail buttons: top-right corner of the icon */
    .icon-rail-btn.rail-minimized::after { top: 4px; right: 4px; transform: none; }
    /* Sidebar list-items are wider — anchor right-aligned vertically centered */
    .list-item.rail-minimized::after { top: 50%; right: 8px; transform: translateY(-50%); }
    #tool-memory-btn.rail-minimized::after { right: 12px; }
    /* Per-user nudge: the listed tools' minimized dot sits 2px further
       in from the right edge so it doesn't look glued to the border. */
    #tool-theme-btn.rail-minimized::after,
    #tool-tasks-btn.rail-minimized::after,
    #tool-notes-btn.rail-minimized::after,
    #tool-library-btn.rail-minimized::after,
    #tool-gallery-btn.rail-minimized::after,
    #tool-compare-btn.rail-minimized::after,
    #tool-calendar-btn.rail-minimized::after { right: 12px; }
    /* Cookbook already shows its own running/served-status dot
       (#cookbook-notif-dot, toggled with .cookbook-notif-active on the
       button). Don't stack the tabbed-down pulse on top of it — the two
       dots overlap. Suppress the minimized dot while the status dot is up. */
    .list-item.rail-minimized.cookbook-notif-active::after { display: none; }
    @media (max-width: 768px) {
      /* On mobile the list-items are taller (touch-sized), so the tabbed-down
         pulsing dot reads a hair low and right. Email uses its own dot and is
         already aligned — only the tool list-item dots need the nudge:
         4px left (right 8 → 12) and 2px up. */
      .list-item.rail-minimized::after {
        right: 13px;
        transform: translateY(calc(-50% - 2px));
      }
      #tool-memory-btn.rail-minimized::after { right: 17px; }
    }
    @keyframes rail-min-pulse {
      0%,100% { opacity: 1; }
      50%     { opacity: 0.4; }
    }
    /* Compact `_` minimize button for modal headers — matches the close
       button's bordered square so the two read as a paired control. The
       header's h4 carries margin-right:auto, which groups minimize + close
       on the right; this button just needs a small gap before close. */
    .modal-minimize-btn {
      background: var(--bg);
      color: var(--fg);
      border: 1px solid var(--fg);
      cursor: pointer;
      width: 24px;
      height: 24px;
      padding: 0;
      margin-left: 0;
      margin-right: 4px;
      line-height: 1;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      border-radius: 4px;
      flex-shrink: 0;
      transition: background 0.15s, color 0.15s;
    }
    .modal-minimize-btn:hover { background: var(--fg); color: var(--bg); }
    .modal.modal-minimized { display: none !important; }
    /* Window tile snap ghost (desktop only) */
    #tile-ghost {
      position: fixed; pointer-events: none; z-index: 9000;
      background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
      border: 2px solid color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
      border-radius: 8px;
      opacity: 0; transform: scale(0.96);
      transition: left 0.12s ease, top 0.12s ease, width 0.12s ease, height 0.12s ease, opacity 0.12s, transform 0.12s;
    }
    #tile-ghost.visible { opacity: 1; transform: scale(1); }
    /* Bottom dock — chip per minimized modal */
    #minimized-dock {
      position: fixed; bottom: var(--composer-clearance, 12px); left: 50%; transform: translateX(-50%);
      display: flex; gap: 6px; flex-wrap: wrap;
      max-width: calc(100vw - 24px);
      padding: 4px;
      z-index: 10020;
      pointer-events: none;
    }
    .minimized-dock-chip {
      pointer-events: auto;
      display: inline-flex; align-items: center; gap: 6px;
      padding: 6px 8px 6px 10px;
      background: var(--panel, var(--bg));
      border: 1px solid var(--border);
      border-radius: 999px;
      color: var(--fg); font-family: inherit; font-size: 12px;
      cursor: grab; touch-action: none; user-select: none;
      box-shadow: 0 4px 14px rgba(0,0,0,0.35);
      transition: transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.15s, border-color 0.15s;
      animation: dock-chip-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) both;
    }
    .minimized-dock-chip:hover {
      background: color-mix(in srgb, var(--accent, var(--red)) 12%, var(--panel, var(--bg)));
      border-color: color-mix(in srgb, var(--accent, var(--red)) 40%, var(--border));
    }
    .minimized-dock-chip:active { cursor: grabbing; }
    .minimized-dock-chip.dragging {
      cursor: grabbing; z-index: 1000;
      box-shadow: 0 8px 24px rgba(0,0,0,0.55);
      transition: none;
      opacity: 0.95;
    }
    /* Whole-dock drag (grabbed an edge chip) */
    #minimized-dock.dock-dragging { cursor: grabbing; }
    #minimized-dock.dock-dragging .minimized-dock-chip {
      transition: none;
      box-shadow: 0 8px 24px rgba(0,0,0,0.45);
    }
    /* Subtle visual cue that edge chips drag the whole dock */
    #minimized-dock .minimized-dock-chip:first-child:not(:only-child),
    #minimized-dock .minimized-dock-chip:last-child:not(:only-child) {
      box-shadow: 0 4px 14px rgba(0,0,0,0.35),
                  inset 0 0 0 1px color-mix(in srgb, var(--accent, var(--red)) 25%, transparent);
    }
    .minimized-dock-chip svg { opacity: 0.7; flex-shrink: 0; }
    .minimized-dock-label { white-space: nowrap; }

    /* Mobile: chips are icon-only round pills. Tap to restore, drag toward
       the trash zone at top-center to close. touch-action:none lets the
       pointermove listener claim the gesture instead of the page scroll. */
    @media (max-width: 768px) {
      .minimized-dock-chip {
        width: 40px; height: 40px;
        padding: 0 !important;
        border-radius: 50% !important;
        justify-content: center;
        position: relative;
        overflow: visible;
        touch-action: none;
      }
      .minimized-dock-chip svg { width: 18px; height: 18px; opacity: 0.9; }
      .minimized-dock-label,
      .minimized-dock-x { display: none !important; }
      .minimized-dock-chip.chip-free-drag {
        box-shadow: 0 8px 22px rgba(0,0,0,0.55),
                    0 0 0 2px color-mix(in srgb, var(--accent, var(--red)) 50%, transparent) !important;
      }
      /* Long-press hint — chip swells and settles while the detach timer
         counts down so the user feels feedback before the bubble peels out
         of the chain. Returns to scale(1) at the end so there's no visual
         jump when the timer fires and the chip becomes a free-drag puck. */
      .minimized-dock-chip.chip-long-press {
        background:
          radial-gradient(circle at 30% 25%, color-mix(in srgb, #fff 28%, transparent), transparent 36%),
          linear-gradient(135deg,
            color-mix(in srgb, var(--accent, var(--red)) 34%, var(--panel, var(--bg))),
            color-mix(in srgb, #7dd3fc 26%, var(--panel, var(--bg))) 52%,
            color-mix(in srgb, #f0abfc 22%, var(--panel, var(--bg))));
        border-color: color-mix(in srgb, var(--accent, var(--red)) 72%, #fff 12%) !important;
        animation: chip-long-press-pulse 0.82s ease-in-out infinite;
        z-index: 10030;
      }
      .minimized-dock-chip.chip-long-press::before {
        content: '';
        position: absolute;
        inset: -96px;
        border-radius: inherit;
        background:
          radial-gradient(circle,
            color-mix(in srgb, var(--accent, var(--red)) 42%, transparent) 0 18%,
            color-mix(in srgb, #7dd3fc 34%, transparent) 34%,
            color-mix(in srgb, #f0abfc 30%, transparent) 50%,
            transparent 72%);
        pointer-events: none;
        z-index: -1;
        animation: chip-long-press-ripple 0.82s ease-out infinite;
      }
      @keyframes chip-long-press-pulse {
        0%, 100% { transform: scale(1); }
        50%      { transform: scale(1.42);
                   box-shadow: 0 14px 42px rgba(0,0,0,0.66),
                               0 0 0 16px color-mix(in srgb, var(--accent, var(--red)) 42%, transparent),
                               0 0 72px color-mix(in srgb, #7dd3fc 44%, transparent),
                               0 0 118px color-mix(in srgb, #f0abfc 32%, transparent); }
      }
      @keyframes chip-long-press-ripple {
        0%   { opacity: 0.92; transform: scale(0.08); }
        72%  { opacity: 0.28; transform: scale(3.6); }
        100% { opacity: 0; transform: scale(5.4); }
      }
      /* Chip whose modal is currently open — accent ring so the user can
         tell at a glance which floating bubble belongs to the visible
         modal. Tap it to minimize. */
      .minimized-dock-chip.chip-active {
        border-color: var(--accent, var(--red)) !important;
        box-shadow: 0 4px 14px rgba(0,0,0,0.35),
                    0 0 0 2px color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
      }
      .minimized-dock-chip.chip-active svg { opacity: 1; }
    }
    /* Magnetic close zone — slides in from the side opposite the chip's
       starting position so it never overlaps the dock. Always accent
       color, larger than the chips, snappy spring transition. */
    #dock-trash-zone {
      position: fixed;
      left: 50%;
      width: 88px; height: 88px;
      border-radius: 50%;
      background: var(--accent, var(--red, #e53935));
      color: #fff;
      display: flex; align-items: center; justify-content: center;
      pointer-events: none;
      opacity: 0;
      z-index: 9000;
      box-shadow: 0 8px 28px color-mix(in srgb, var(--accent, var(--red, #e53935)) 55%, transparent);
      transition: opacity 0.18s ease,
                  transform 0.26s cubic-bezier(0.34, 1.56, 0.64, 1),
                  box-shadow 0.18s ease;
    }
    /* Off-screen start position depends on which side the chip is on so
       the X slides in from the opposite edge with a snappy overshoot. */
    #dock-trash-zone[data-side="top"]    { transform: translateX(-50%) translateY(-180%) scale(0.7); }
    #dock-trash-zone[data-side="bottom"] { transform: translateX(-50%) translateY(180%) scale(0.7); }
    #dock-trash-zone.visible {
      opacity: 1;
      transform: translateX(-50%) translateY(0) scale(1);
    }
    #dock-trash-zone.engaged {
      transform: translateX(-50%) translateY(0) scale(1.22);
      box-shadow: 0 0 0 14px color-mix(in srgb, var(--accent, var(--red, #e53935)) 22%, transparent),
                  0 12px 36px color-mix(in srgb, var(--accent, var(--red, #e53935)) 60%, transparent);
    }
    /* Whirlpool ring — fades in when chip is in capture range and spins
       continuously, then bursts on drop. */
    #dock-trash-zone .whirlpool {
      position: absolute; inset: -8px;
      border-radius: 50%;
      border: 2px solid transparent;
      border-top-color: rgba(255,255,255,0.9);
      border-right-color: rgba(255,255,255,0.55);
      border-bottom-color: rgba(255,255,255,0.25);
      opacity: 0;
      pointer-events: none;
      animation: whirlpool-spin 0.85s linear infinite;
      transition: opacity 0.15s ease;
    }
    #dock-trash-zone.engaged .whirlpool { opacity: 1; }
    #dock-trash-zone.dropping .whirlpool {
      opacity: 1;
      animation: whirlpool-burst 0.36s cubic-bezier(0.4, 0, 0.2, 1) forwards;
    }
    @keyframes whirlpool-spin { to { transform: rotate(360deg); } }
    @keyframes whirlpool-burst {
      0%   { transform: rotate(0deg) scale(1);   opacity: 1; }
      60%  { transform: rotate(540deg) scale(1.5); opacity: 0.6; }
      100% { transform: rotate(900deg) scale(2.2); opacity: 0; }
    }

    /* Email chip badges:
       - email-lib-modal chip: "1" badge when an email is expanded
         inside (JS sets data-has-expanded).
       - email-reader-* chips: auto-numbered 1, 2, 3 … via CSS counter,
         so multiple opened-email windows are visually distinguishable. */
    .minimized-dock-chip[data-modal-id="email-lib-modal"],
    .minimized-dock-chip[data-modal-id^="email-reader-"] {
      position: relative;
    }
    .minimized-dock-chip[data-modal-id^="email-reader-"][data-tab-num]::after {
      content: attr(data-tab-num);
      position: absolute;
      top: -4px;
      right: -4px;
      min-width: 16px;
      height: 16px;
      padding: 0 4px;
      background: var(--accent, var(--red));
      color: #fff;
      font-size: 9px;
      font-weight: 700;
      line-height: 16px;
      text-align: center;
      border-radius: 8px;
      box-shadow: 0 0 0 2px var(--bg);
      pointer-events: none;
    }
    .minimized-dock-chip[data-modal-id="email-lib-modal"][data-email-unread-label]::after {
      content: attr(data-email-unread-label);
      position: absolute;
      top: -6px;
      right: 10px;
      height: 16px;
      padding: 0 6px;
      background: var(--accent, var(--red));
      color: #fff;
      font-size: 9px;
      font-weight: 700;
      line-height: 16px;
      white-space: nowrap;
      border-radius: 8px;
      box-shadow: 0 0 0 2px var(--bg);
      pointer-events: none;
    }

    .minimized-dock-x {
      display: inline-flex; align-items: center; justify-content: center;
      width: 16px; height: 16px; border-radius: 50%;
      font-size: 14px; line-height: 1; opacity: 0.4;
      margin-left: 3px;
    }
    .minimized-dock-x:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 12%, transparent); }
    @keyframes dock-chip-in {
      from { opacity: 0; transform: translateY(20px) scale(0.85); }
      to   { opacity: 1; transform: translateY(0)   scale(1); }
    }
    .rail-new-chat svg { transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1); }
    .rail-new-chat:hover svg { transform: rotate(90deg); }
    /* A "New" label slides out from the right of the + as it spins, so users
       discover what the icon does without needing the tooltip. */
    .rail-new-chat { position: relative; }
    .rail-new-chat::after {
      content: 'New';
      position: absolute;
      left: 100%;
      top: 50%;
      margin-left: 6px;
      transform: translateY(-50%) translateX(-6px);
      opacity: 0;
      pointer-events: none;
      white-space: nowrap;
      font-size: 11px;
      font-weight: 600;
      color: var(--fg);
      background: var(--panel);
      padding: 2px 6px;
      border-radius: 4px;
      box-shadow: 0 2px 6px rgba(0,0,0,0.25);
      z-index: 20;
      transition: opacity 0.25s ease, transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
    }
    .rail-new-chat:hover::after {
      opacity: 1;
      transform: translateY(-50%) translateX(0);
    }
    #rail-admin,
    #rail-settings { color: var(--fg); }
    .rail-separator { width: 20px; height: 1px; background: var(--border); margin: 4px auto; }
    .rail-dynamic {
      position: relative;
    }
    .rail-dynamic::after {
      content: '';
      position: absolute;
      bottom: 2px;
      left: 50%;
      transform: translateX(-50%);
      width: 4px;
      height: 4px;
      border-radius: 50%;
      background: var(--accent, var(--red));
      opacity: 0.7;
    }
    /* ── Sidebar sections (flat layout) ── */
    .section { padding: 0; border: none; background: none; border-radius: 0; box-shadow: none; margin: 0; }

    /* Section header row — identical sizing to .list-item */
    .section-header-flex {
      display: flex; align-items: center; gap: 6px;
      padding: 8px 8px; margin: 1px 0; border-radius: 4px;
      background: transparent; cursor: pointer;
      transition: background 0.08s;
      height: 29px;
      box-sizing: border-box;
    }
    .section-header-flex:hover { background: color-mix(in srgb, var(--red) 8%, transparent); }
    .section-header-flex::after { content: none; }
    .section-header-flex h4::before { content: none; }

    /* Section title text — same font as .list-item .grow */
    .section-header-flex h4,
    .section-header-flex .section-title {
      flex: 1; cursor: pointer; user-select: none;
      display: flex; align-items: center; gap: 6px;
      margin: 0; padding: 0; border: none;
      font-size: 10px; font-weight: 400; font-family: inherit;
      /* 1.3 (not 1) so Fira Code's tall glyph box isn't vertically clipped in
         Chromium/Edge — mirrors the .list-item fix. The title is flex-centred
         in a fixed-height (29px) header, so this adds headroom without reflow. */
      line-height: 1.3; letter-spacing: 0; text-transform: none;
      color: var(--fg);
    }
    .section-icon,
    .sidebar-action-icon {
      flex-shrink: 0;
      stroke: var(--accent, var(--red));
      position: relative;
      left: -1px;
      color: var(--accent, var(--red));
    }
    /* Shared notification dot for sidebar section titles. Single source of
       truth so chats / email / assistant / future-section dots all sit at
       the same offset from their label. */
    .sidebar-notif-dot {
      display: inline-block;
      width: 6px;
      height: 6px;
      /* Push to the right edge of the flex section-title so chats / email
         / assistant dots all line up vertically in the same column
         instead of trailing right after each (differently-sized) label. */
      margin-left: auto;
      border-radius: 50%;
      background: var(--accent, var(--red));
      flex-shrink: 0;
      vertical-align: middle;
    }
    /* The email notification gets a soft breathing glow so new-mail catches
       the eye without being shouty. Vertical alignment stays with the
       inherited .sidebar-notif-dot rule so it lines up with chats/assistant. */
    #email-unread-dot.sidebar-notif-dot {
      animation: email-notif-breathe 2.2s ease-in-out infinite;
      /* Nudge in from the far-right edge so it doesn't crowd the corner. */
      margin-right: 4px;
      /* Tiny vertical nudge to center with the email label. */
      position: relative;
      top: 0.1px;
    }
    @keyframes email-notif-breathe {
      0%, 100% {
        box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent, var(--red)) 60%, transparent);
        opacity: 0.85;
      }
      50% {
        box-shadow: 0 0 6px 2px color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
        opacity: 1;
      }
    }
    @media (prefers-reduced-motion: reduce) {
      #email-unread-dot.sidebar-notif-dot { animation: none; }
    }
    @media (max-width: 768px) {
      /* Nudge the sidebar notification dot up 1px on mobile so it lines
         up with the bigger section titles. vertical-align:middle drifts
         a hair low against the larger touch-friendly text. */
      .sidebar-notif-dot {
        position: relative;
        top: -1px;
      }
      /* Cookbook's sidebar status dot carries an inline top:-1px, so override
         with the ID + !important to nudge it 2px left / 1px up on mobile. */
      #cookbook-notif-dot {
        left: -1px !important;
        top: -2px !important;
      }
    }
    #sidebar-new-chat-btn svg {
      transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
    }
    #sidebar-new-chat-btn:hover svg {
      transform: rotate(90deg);
    }

    /* Sort/select buttons in header — compact */
    .section-header-flex > div { display: flex; align-items: center; gap: 2px; }
    .section-header-btn {
      all: unset;
      cursor: pointer; opacity: 0.4; padding: 1px 3px; border-radius: 4px;
      display: inline-flex; align-items: center; justify-content: center;
      transition: opacity 0.08s, background 0.08s;
    }
    .section-header-btn:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 7%, transparent); }
    .section-header-btn.active { opacity: 0.9; color: var(--accent); }
    .section-header-btn svg { width: 12px; height: 12px; }

    /* Chats library — grid icon, hover-reveal so the header only toggles collapse */
    #sessions-section .chats-manage-btn {
      opacity: 0;
      transition: opacity 0.12s, background 0.08s;
    }
    #sessions-section .section-header-flex:hover .chats-manage-btn,
    #sessions-section .chats-manage-btn:hover,
    #sessions-section .chats-manage-btn:focus-visible {
      opacity: 0.45;
    }
    #sessions-section .chats-manage-btn:hover,
    #sessions-section .chats-manage-btn:focus-visible {
      opacity: 1;
    }
    @media (hover: none) {
      #sessions-section .chats-manage-btn { opacity: 0.35; }
      #sessions-section .chats-manage-btn:active { opacity: 1; }
    }

    /* Collapse chevron */
    .section-collapse-btn {
      all: unset;
      cursor: pointer; display: inline-flex; align-items: center; padding: 0 2px; border-radius: 4px;
    }
    .section-collapse-btn:hover { background: color-mix(in srgb, var(--fg) 8%, transparent); }
    .section-collapse-chevron { display: inline-flex; opacity: 0.3; transition: transform 0.2s, opacity 0.15s; }
    .section-collapse-btn:hover .section-collapse-chevron { opacity: 0.6; }
    .section.collapsed .section-collapse-chevron { transform: rotate(-90deg); opacity: 0.5; }
    .section-header-flex:has(.section-header-btn) .section-collapse-btn { display: none; }
    .section.collapsed .section-collapse-btn { display: inline-flex !important; }

    /* Collapsed state */
    .section.collapsed > *:not(h4):not(.section-title):not(.section-header-flex) { display: none !important; }
    .section.collapsed .section-header-flex { margin-bottom: 0; }
    .section.collapsed .section-header-btn { display: none; }
    .section.collapsed { cursor: pointer; }

    /* Domino expand: every time a section goes from .collapsed → open
       (toggleCollapse in section-management.js adds .section-just-expanded
       for ~700ms), the .list-item children cascade in one after another,
       same feel as the chat input's tools menu. Each row springs in
       from a tiny offset below + scaled-down, staggered by nth-child. */
    .section.section-just-expanded :is(.list-item, .models-row) {
      animation: section-domino-in 0.36s cubic-bezier(0.22, 1.61, 0.36, 1) backwards;
    }
    .section.section-just-expanded :is(.list-item, .models-row):nth-child(1)  { animation-delay: 0.04s; }
    .section.section-just-expanded :is(.list-item, .models-row):nth-child(2)  { animation-delay: 0.08s; }
    .section.section-just-expanded :is(.list-item, .models-row):nth-child(3)  { animation-delay: 0.12s; }
    .section.section-just-expanded :is(.list-item, .models-row):nth-child(4)  { animation-delay: 0.16s; }
    .section.section-just-expanded :is(.list-item, .models-row):nth-child(5)  { animation-delay: 0.20s; }
    .section.section-just-expanded :is(.list-item, .models-row):nth-child(6)  { animation-delay: 0.24s; }
    .section.section-just-expanded :is(.list-item, .models-row):nth-child(7)  { animation-delay: 0.28s; }
    .section.section-just-expanded :is(.list-item, .models-row):nth-child(8)  { animation-delay: 0.32s; }
    .section.section-just-expanded :is(.list-item, .models-row):nth-child(9)  { animation-delay: 0.36s; }
    .section.section-just-expanded :is(.list-item, .models-row):nth-child(10) { animation-delay: 0.40s; }
    .section.section-just-expanded :is(.list-item, .models-row):nth-child(11) { animation-delay: 0.44s; }
    .section.section-just-expanded :is(.list-item, .models-row):nth-child(12) { animation-delay: 0.48s; }
    @keyframes section-domino-in {
      0%   { opacity: 0; transform: translateY(8px) translateX(-4px) scale(0.92); }
      60%  { opacity: 1; }
      100% { opacity: 1; transform: translateY(0)   translateX(0)    scale(1); }
    }

    /* Domino collapse: when toggleCollapse goes open → closed, JS adds
       .section-just-collapsing for ~530ms before flipping in .collapsed.
       During that window the items fade/slide DOWN one after another so
       you see them peel off instead of vanishing as a block. Uses
       nth-last-child so the BOTTOM item leaves first and the cascade
       rolls upward — mirrors the "stacked deck" feeling of the open
       animation reversed. */
    .section.section-just-collapsing :is(.list-item, .models-row) {
      animation: section-domino-out 0.22s ease-in forwards;
    }
    .section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(1)  { animation-delay: 0.00s; }
    .section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(2)  { animation-delay: 0.025s; }
    .section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(3)  { animation-delay: 0.05s; }
    .section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(4)  { animation-delay: 0.075s; }
    .section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(5)  { animation-delay: 0.10s; }
    .section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(6)  { animation-delay: 0.125s; }
    .section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(7)  { animation-delay: 0.15s; }
    .section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(8)  { animation-delay: 0.175s; }
    .section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(9)  { animation-delay: 0.20s; }
    .section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(10) { animation-delay: 0.225s; }
    .section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(11) { animation-delay: 0.25s; }
    .section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(12) { animation-delay: 0.275s; }
    @keyframes section-domino-out {
      0%   { opacity: 1; transform: translateY(0)   translateX(0)   scale(1); }
      100% { opacity: 0; transform: translateY(6px) translateX(-3px) scale(0.94); }
    }
    @keyframes spin { to { transform: rotate(360deg); } }
    .row { display:flex; gap:6px; align-items:center; }
    .list-item:hover,
    .models-row:hover {
      background: color-mix(in srgb, var(--red) 8%, transparent);
      border-color: var(--red);
    }
    /* Disabled tool — dimmed to signal its feature is turned off globally */
    .list-item.tool-disabled {
      opacity: 0.4;
    }
    .list-item.tool-disabled:hover {
      opacity: 0.7;
    }
    /* Session bulk select mode */
    .session-bulk-bar {
      display: flex;
      align-items: center;
      gap: 6px;
      padding: 4px 8px;
      border-bottom: 1px solid var(--border);
      background: var(--sidebar-bg, var(--panel));
      position: sticky;
      top: 0;
      z-index: 3;
      font-size: 11px;
    }
    .session-bulk-bar.hidden { display: none; }
    #session-select-all-dot {
      display: inline-block;
      position: relative;
      top: -2px;
    }
    .session-bulk-btn {
      background: none;
      border: none;
      border-radius: 4px;
      color: var(--fg);
      padding: 4px;
      font-family: inherit;
      cursor: pointer;
      opacity: 0.4;
      transition: opacity 0.1s;
      display: inline-flex;
      align-items: center;
      justify-content: center;
    }
    .session-bulk-btn:hover { opacity: 1; }
    .session-bulk-btn-danger { color: var(--red); opacity: 0.5; }
    .session-bulk-btn-danger:hover { opacity: 1; }
    /* Checkbox in select mode */
    .session-select-cb {
      accent-color: var(--accent, var(--red));
      margin: 0 4px 0 0;
      flex-shrink: 0;
      cursor: pointer;
    }

    .session-icon,
    .model-icon {
      flex-shrink: 0;
      opacity: 0.35;
      display: inline-flex;
      align-items: center;
    }
    .session-icon.has-docs {
      color: inherit;
      opacity: 0.5;
    }
    .session-star {
      width: 10px; height: 10px;
      border-radius: 50%;
      border: 1.5px solid color-mix(in srgb, var(--fg) 22%, transparent);
      flex-shrink: 0;
      position: relative;
    }
    .session-star.processing {
      animation: research-pulse 1.5s ease-in-out infinite;
    }
    .session-star.notify {
      animation: research-done-pulse 1.2s ease-in-out 5;
      opacity: 1;
      /* Bigger, solid status dot so "research done" reads clearly. Use the
         defined accent (bare --accent is undefined here → no colour). */
      width: 14px; height: 14px;
      background: var(--accent-primary, var(--red));
      border-color: var(--accent-primary, var(--red));
    }
    /* The dot doubles as the provider-logo holder; hide that logo in the
       done state so the solid notif dot doesn't collide with the SVG behind it. */
    .session-star.notify svg { display: none; }
    @keyframes research-done-pulse {
      0%, 100% { transform: scale(1); opacity: 1; }
      50% { transform: scale(1.5); opacity: 0.7; }
    }
    .session-fav {
      flex-shrink: 0;
      cursor: pointer;
      color: var(--red);
      opacity: 0.6;
      display: inline-flex;
      align-items: center;
      transition: opacity 0.1s;
    }
    .session-fav:hover {
      opacity: 1;
    }
    .list-item.active-session {
      background: color-mix(in srgb, var(--red) 10%, transparent);
      border-color: var(--red);
      border-left-color: var(--red);
    }
    .list-item .provider-logo { opacity: 0.4 !important; }
    .list-item:has(.session-star.processing) .provider-logo { opacity: 1 !important; }
    .list-item.stream-complete .provider-logo { opacity: 1 !important; }
    .list-item.active {
      background: color-mix(in srgb, var(--red) 10%, transparent);
      border-color: var(--red);
      border-left-color: var(--red);
    }
    /* Only show the red focus ring for keyboard navigation (Tab). Mouse
       clicks shouldn't leave a sticky red outline on a sidebar item. */
    .list-item:focus { outline: none; }
    .list-item:focus-visible {
      outline: none;
      border-color: var(--red, var(--color-error));
      box-shadow: 0 0 0 1px var(--red, var(--color-error));
    }
    /* Drag handles hidden by default, shown via body.rearrange-mode */
    .drag-handle, .item-drag-handle, .folder-drag-handle { display: none; }
    body.rearrange-mode .drag-handle,
    body.rearrange-mode .item-drag-handle,
    body.rearrange-mode .folder-drag-handle { display: inline; }

    /* Drag sorting styles for list items */
    .item-drag-handle {
      cursor: grab;
      opacity: 0.4;
      font-size: 10px;
      padding: 0 4px;
      user-select: none;
      letter-spacing: -2px;
      transition: opacity 0.15s;
    }
    .item-drag-handle:hover {
      opacity: 1;
      color: var(--red);
    }
    .item-drag-handle:active {
      cursor: grabbing;
    }
    .list-item.dragging,
    .models-row.dragging,
    .session-folder.dragging {
      opacity: 0.95;
      box-shadow: 0 8px 24px color-mix(in srgb, var(--red) 30%, transparent);
      border-color: var(--red);
      background: var(--panel);
      cursor: grabbing;
    }
    .list-item.dragging .item-drag-handle,
    .models-row.dragging .item-drag-handle,
    .session-folder.dragging .folder-drag-handle {
      opacity: 1;
      color: var(--red);
    }
    /* ── Model category grouping ── */
    .models-category-header {
      display: flex; align-items: center; gap: 6px;
      padding: 4px 8px; cursor: pointer;
      font-size: 0.85em; font-weight: 600;
      color: color-mix(in srgb, var(--fg) 55%, transparent);
      border-radius: 4px; user-select: none;
      margin-top: 4px;
      transition: opacity 0.08s, background 0.08s, color 0.08s;
    }
    .models-category-header:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 4%, transparent); }
    .models-category-header .folder-count { font-weight: 400; opacity: 0.5; font-size: 0.9em; }
    .models-endpoint-label {
      display: flex; align-items: center; gap: 6px;
      padding: 4px 8px; cursor: pointer;
      font-size: 0.85em; font-weight: 600;
      color: color-mix(in srgb, var(--fg) 55%, transparent);
      border-radius: 4px; user-select: none;
      transition: opacity 0.08s, background 0.08s;
    }
    .models-endpoint-label:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 4%, transparent); }
    .models-endpoint-label .folder-count { font-weight: 400; opacity: 0.5; }
    .models-group-content.indented {
      padding-left: 4px;
    }
    .models-group-content.indented .models-row { border-bottom: 1px solid color-mix(in srgb, var(--fg) 7%, transparent); }
    .models-group-content.indented .models-row:last-child { border-bottom: none; }
    /* ── Session folders ── */
    .session-folder { margin: 2px 0; }
    .session-folder-header {
      display: flex; align-items: center; gap: 6px;
      padding: 4px 8px; cursor: pointer;
      font-size: 0.88em; font-weight: 600;
      color: color-mix(in srgb, var(--fg) 55%, transparent);
      border-radius: 4px; user-select: none;
      transition: opacity 0.08s, background 0.08s;
    }
    .session-folder-header:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 4%, transparent); }
    .session-folder-header .folder-count { font-weight: 400; opacity: 0.5; }
    .session-folder-header.drag-over {
      outline: 2px dashed var(--red); background: color-mix(in srgb, var(--red) 13%, transparent); opacity: 1;
    }
    .session-folder.dragging-folder { opacity: 0.4; }
    .folder-toggle { font-size: 0.7em; width: 10px; text-align: center; }
    .folder-name { font-weight: inherit; flex: 1; }
    .folder-count { font-size: 0.85em; opacity: 0.5; }
    .folder-drag-handle {
      cursor: grab; opacity: 0.3; font-size: 0.8em; padding: 0 2px;
      transition: opacity 0.08s;
    }
    .session-folder-header:hover .folder-drag-handle { opacity: 0.7; }
    .folder-delete-btn {
      background: none; border: none; color: var(--fg); opacity: 0;
      cursor: pointer; font-size: 1.1em; padding: 0 2px; line-height: 1;
      min-height: 0; height: auto;
      transition: opacity 0.08s, color 0.08s;
    }
    .session-folder-header:hover .folder-delete-btn { opacity: 0.5; }
    .folder-delete-btn:hover { opacity: 1 !important; color: var(--color-error); }
    .session-folder-content { padding-left: 4px; }
    .session-folder-content .list-item { border-bottom: 1px solid color-mix(in srgb, var(--fg) 7%, transparent); }
    .session-folder-content .list-item:last-child { border-bottom: none; }
    .session-show-more-btn {
      display: block;
      width: 100%;
      background: none;
      border: none;
      color: var(--fg);
      opacity: 0.4;
      font-size: 0.8em;
      padding: 6px 8px;
      cursor: pointer;
      text-align: center;
      transition: opacity 0.15s;
    }
    .session-show-more-btn:hover { opacity: 0.8; }
    .drag-folder-placeholder {
      height: 4px; margin: 2px 0; border-radius: 2px;
      background: var(--red); opacity: 0.6;
    }
    .unfiled-drop-zone {
      min-height: 8px; border-radius: 4px; transition: all 0.15s;
    }
    .unfiled-drop-zone.drag-over {
      min-height: 24px; outline: 2px dashed var(--red);
      background: color-mix(in srgb, var(--red) 9%, transparent);
    }

    /* Mobile swipe-to-delete */
    .swipe-delete-action {
      position: absolute;
      right: 0;
      top: 0;
      bottom: 0;
      width: 60px;
      background: color-mix(in srgb, var(--color-error) 12%, transparent);
      color: var(--color-error);
      display: flex;
      align-items: center;
      justify-content: center;
      border-radius: 0 4px 4px 0;
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.15s;
      cursor: pointer;
      z-index: 1;
    }
    .swipe-delete-action::before {
      content: '';
      display: block;
      width: 18px;
      height: 18px;
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23ff4444' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 6 5 6 21 6'/%3E%3Cpath d='M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2'/%3E%3C/svg%3E");
      background-size: contain;
      background-repeat: no-repeat;
    }
    .swipe-delete-action:active {
      background: color-mix(in srgb, var(--color-error) 22%, transparent);
    }
    .drag-placeholder {
      background: color-mix(in srgb, var(--red) 10%, transparent);
      border: 2px dashed color-mix(in srgb, var(--color-accent) 40%, transparent);
      border-radius: 6px;
      margin: 4px 0;
      transition: height 0.15s ease;
    }
    .list-item,
    .models-row {
      display: flex;
      gap: 6px;
      align-items: center;
      padding: 3px 8px;
      margin: 0;
      border-radius: 4px;
      border: none;
      line-height: 1.3;
      font-size: 13px;
      background: transparent;
      transition: background 0.08s;
      cursor: pointer;
      touch-action: pan-y;
    }
    .list-item button {
      margin: 0;
      height: 24px;
      padding: 0 8px;
      font-size: 9px;
    }
    /* Inline "+" action on a tool/section row (Library → new document,
       Email → compose). Sizing forced + min-* zeroed so the mobile
       touch-target rule (44px) can't inflate it. Selector intentionally
       NOT scoped to `.list-item` so the same class also styles buttons
       sitting in `.section-header-flex` (e.g. #email-compose-btn). */
    .list-item-plus-btn {
      all: unset;
      box-sizing: border-box;
      flex-shrink: 0;
      position: relative;
      left: 4px;
      display: inline-flex !important;
      align-items: center;
      justify-content: center;
      height: 14px !important;
      min-height: 0 !important;
      width: auto !important;
      min-width: 0 !important;
      padding: 0 5px !important;
      border-radius: 4px;
      color: var(--fg);
      /* Always visible at rest. On hover (devices that have it) the + spins
         90° and "new" reveals to its right — neat expand affordance. */
      opacity: 1 !important;
      cursor: pointer;
      gap: 0;
      overflow: visible;
      z-index: 1;
      transition: background 0.12s, color 0.12s, gap 0.18s ease;
    }
    .list-item-plus-btn svg.list-item-plus-icon {
      width: 13px; height: 13px;
      transition: transform 0.22s ease;
    }
    /* Legacy fallback for plus-btns without the `.list-item-plus-icon` class. */
    .list-item-plus-btn svg:not(.list-item-plus-icon) { width: 13px; height: 13px; }
    /* Label is absolutely positioned to the LEFT of the icon so the + stays
       put — it just appears beside it on hover instead of pushing it. The
       button's intrinsic width remains the icon, so neighbours don't shift. */
    .list-item-plus-label {
      position: absolute;
      right: 100%;
      top: 50%;
      transform: translateY(-50%);
      padding-right: 5px;
      opacity: 0;
      pointer-events: none;
      white-space: nowrap;
      font-size: 9.5px;
      line-height: 1;
      letter-spacing: 0.02em;
      transition: opacity 0.18s ease, transform 0.22s ease;
    }
    @media (hover: hover) {
      .list-item-plus-btn:hover .list-item-plus-icon {
        transform: rotate(90deg);
      }
      .list-item-plus-btn:hover .list-item-plus-label {
        opacity: 1;
        transform: translateY(-50%) translateX(0);
        pointer-events: auto;
      }
      .list-item-plus-btn .list-item-plus-label {
        transform: translateY(-50%) translateX(6px);
      }
    }
    .list-item-plus-btn:hover {
      background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
      color: var(--accent, var(--red));
    }
    .list-item span {
      color: var(--fg);
      font-size: 9.75px;
    }
    .grow {
      flex: 1;
      overflow: hidden;
      text-overflow: clip;
      white-space: nowrap;
    }
    input, textarea, button, select {
      background:var(--bg);
      color:var(--fg);
      border:1px solid var(--border);
      font-family:inherit;
      padding:6.9px;
      border-radius: 4px;
      transition: background 0.08s, border-color 0.08s, color 0.08s, opacity 0.08s;
    }
    input:hover, textarea:hover, button:hover, select:hover {
      border-color: var(--fg);
      background-color: var(--panel);
    }
    :root.light input,
    :root.light textarea,
    :root.light button,
    :root.light select {
      background:#eaeaea;
      color-scheme: light;
    }
    input[type="text"] { height:32px; width:100%; }
    textarea { width:100%; min-height:32px; height:auto; max-height:30lh; overflow-y:auto; resize:none; }
    button { height:32px; padding:0 10px; }
    #chat-form button[type="submit"] { height:38px; }
    select {
      height:32px;
      color-scheme: dark;
      background-color: var(--select-bg);
      color: var(--select-fg);
    }
    select option,
    select optgroup {
      background-color: var(--select-option-bg);
      color: var(--select-option-fg);
    }
    select option:checked {
      background-color: var(--select-option-active-bg);
      color: var(--select-option-fg);
    }
    :root.light select { color-scheme: light; }
    .chat-container {
      flex:1;
      display:flex;
      flex-direction:column;
      padding:0 16px;
      overflow:hidden;
      position:relative;
      min-height:0;
      min-width:0;
      margin-top:8px;
      margin-bottom: 0;
    }
    .chat-meta { font-size:12px; color:color-mix(in srgb, var(--fg) 60%, transparent); margin-bottom:6px; }
    .chat-history {
      flex:1;
      overflow-y:auto;
      overflow-x:hidden;
      overscroll-behavior-y: none;
      margin-bottom:8px;
      white-space:normal;
      min-height:0;
      --chat-max: 800px;
      padding-left: max(0px, calc((100% - var(--chat-max)) / 2));
      padding-right: max(12px, calc((100% - var(--chat-max)) / 2 + 12px));
    }
    /* Sortable Cookbook column headers had no visual cue, so users couldn't tell
       a header was clickable (the Newest sort on the Model column was invisible).
       Show a pointer + hover highlight, and underline the active sort column. */
    .hwfit-header .hwfit-sortable { cursor: pointer; transition: color .12s; }
    .hwfit-header .hwfit-sortable:hover { color: var(--fg); text-decoration: underline dotted; }
    .hwfit-header .hwfit-sort-active { color: var(--fg); font-weight: 600; }
    /* Welcome screen — centered in available space above input bar */
    #welcome-screen {
      position:absolute;
      top:40%;
      left:50%;
      transform:translate(-50%,-50%);
      display:flex;
      flex-direction:column;
      align-items:center;
      text-align:center;
      pointer-events:none;
      animation: welcome-enter 0.4s ease-out both;
      transition: top 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease, transform 0.3s ease;
    }
    #welcome-screen .welcome-tip,
    #welcome-screen .welcome-sub,
    #welcome-screen .welcome-version {
      transition: opacity 0.25s ease, max-height 0.25s ease, margin 0.25s ease;
      max-height: 60px;
      overflow: hidden;
    }
    /* The tip is a full sentence that wraps to 4-5 lines on narrow phones,
       where the welcome block shrink-wraps small; the shared 60px ceiling
       (sized for the one-line sub/version) clipped its last line. Give the tip
       a taller ceiling so it isn't truncated. Kept above the max-height:650px
       block below so that rule's max-height:0 still collapses it on short
       viewports. */
    #welcome-screen .welcome-tip {
      max-height: 120px;
    }
    @media (max-height: 650px) {
      #welcome-screen { top: 28%; }
      #welcome-screen .welcome-tip { opacity: 0; max-height: 0; margin: 0; }
      #welcome-screen .welcome-version { margin-top: 6px; }
      .incognito-btn { margin-top: 8px; }
    }
    @media (max-height: 500px) {
      #welcome-screen { top: 22%; }
      #welcome-screen .welcome-name { font-size: 1.4rem; margin-bottom: 0; }
      #welcome-screen .welcome-sub { opacity: 0; max-height: 0; margin: 0; }
      .incognito-btn { margin-top: 4px !important; }
    }
    @media (max-height: 380px) {
      #welcome-screen { opacity: 0; pointer-events: none; }
    }
    #welcome-screen.hidden { display:none; }
    #welcome-screen.kb-hidden {
      opacity: 0;
      pointer-events: none;
      transform: translate(-50%, -50%) scale(0.95);
    }
    @media (max-width: 768px) {
      #welcome-screen { top: 42%; }
      #welcome-screen .welcome-name { margin-bottom: 2px; }
      #welcome-screen .welcome-sub { margin-bottom: 0; }
      #welcome-screen .incognito-btn { margin-top: 6px; }
    }
    #welcome-screen .welcome-name {
      font-size:2.2rem;
      font-weight:700;
      background: linear-gradient(135deg, var(--brand-color, var(--red)), color-mix(in srgb, var(--brand-color, var(--red)) 60%, var(--fg)));
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      background-clip: text;
      letter-spacing:0.03em;
      margin-bottom:10px;
    }
    .welcome-boat {
      width: 1.8rem;
      height: 1.8rem;
      margin-right: 0.4rem;
      vertical-align: -0.15em;
      color: var(--brand-color, var(--red));
    }
    #welcome-screen .welcome-sub {
      font-size:0.85rem;
      color:color-mix(in srgb, var(--fg) 60%, transparent);
      opacity:0.6;
      line-height:1.5;
      max-width:300px;
    }
    #welcome-screen .welcome-version {
      font-size:0.7rem;
      opacity:0.25;
      margin-top:12px;
      pointer-events:none;
      user-select:none;
    }
    #welcome-screen .welcome-tip {
      font-size:0.75rem;
      color:var(--fg);
      opacity:0.2;
      margin-top:24px;
      max-width:320px;
      line-height:1.4;
    }
    /* Incognito toggle — on welcome screen */
    .incognito-btn {
      pointer-events: auto;
      background: none;
      border: 1px solid var(--border);
      color: var(--fg);
      opacity: 0.25;
      cursor: pointer;
      padding: 6px 12px;
      border-radius: 20px;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      transition: all 0.15s;
      margin-top: 16px;
      font-family: inherit;
      font-size: 11px;
    }
    .incognito-btn:hover {
      opacity: 0.6;
      background: color-mix(in srgb, var(--fg) 6%, transparent);
    }
    .incognito-btn.active {
      opacity: 1;
      color: var(--accent);
      border-color: var(--accent);
      background: color-mix(in srgb, var(--accent) 10%, transparent);
    }
    .incognito-btn.active .eye-open { display: none; }
    .incognito-btn.active .eye-blinded { display: inline !important; }
    .incognito-label {
      font-size: 11px;
      font-weight: 500;
      letter-spacing: 0.3px;
    }
    /* When welcome is active, push input bar up toward center */
    .chat-container .chat-input-bar {
      transition: margin 0.3s ease, max-width 0.3s ease;
    }
    .chat-container.welcome-active .chat-input-bar {
      margin-bottom:30vh;
      margin-left:auto;
      margin-right:auto;
      max-width: 800px;
      width: 100%;
    }
    @media (max-width:768px) {
      #welcome-screen .welcome-name { font-size:1.8rem; }
      #welcome-screen .welcome-sub { font-size:0.8rem; }
      .chat-container.welcome-active .chat-input-bar {
        margin-bottom:0;
        margin-left:0;
        margin-right:0;
      }
    }
    .msg {
      margin: 8px 0;
      position: relative;
      display: flex;
      flex-direction: column;
      width: fit-content;
      max-width: 85%;
      min-width: 80px;
      border-radius: 12px;
      padding: 10px 12px;
      line-height: 1.4;
      word-wrap: break-word;
      overflow-wrap: break-word;
      overflow: hidden;
      animation: msg-enter 0.3s ease-out both;
    }
    .msg-user {
      align-items: flex-end;
      margin-left: auto;
      margin-right: 8px;
      background: var(--user-bubble-bg, color-mix(in srgb, var(--fg) 8%, var(--bg)));
      border: 1px solid var(--bubble-border, var(--border));
      border-radius: 18px 18px 0 18px;
      align-self: flex-end;
      width: fit-content;
      max-width: 85%;
      min-width: 80px;
      word-wrap: break-word;
      overflow-wrap: break-word;
      overflow: hidden;
    }
    .msg-ai {
      align-items: flex-start;
      margin-right: auto;
      margin-left: 8px;
      background: var(--ai-bubble-bg, var(--panel));
      border: 1px solid var(--bubble-border, var(--border));
      border-radius: 18px 18px 18px 0;
      align-self: flex-start;
      width: 85%;
      max-width: 85%;
      min-width: 80px;
      word-wrap: break-word;
      overflow-wrap: break-word;
      overflow: hidden;
      transition: min-height 0.25s ease-out;
    }
    .msg .role {
      font-weight: 600;
      margin-bottom: 6px;
      display: flex;
      align-items: center;
      gap: 6px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .msg .role::before {
      content: '';
      width: 8px;
      height: 8px;
      border-radius: 50%;
      background: var(--model-dot, color-mix(in srgb, var(--fg) 30%, transparent));
      flex-shrink: 0;
    }
    .msg .role.has-logo::before { display: none; }
    .role-provider-logo { display: inline-flex; align-items: center; flex-shrink: 0; }
    .role-provider-logo svg { width: 12px; height: 12px; }
    .msg-user .role {
      color: color-mix(in srgb, var(--fg) 60%, transparent);
    }
    .msg-user .role::before {
      background: color-mix(in srgb, var(--fg) 40%, transparent);
    }
    .msg .body {
      width: 100%;
      white-space: normal;
      word-break: break-word;
      overflow-wrap: anywhere;
      line-height: 1.5;
      font-size: 0.95em;
      margin: 0;
      overflow: hidden;
      max-width: 100%;
    }
    .msg .body > * {
      margin-top: 8px;
      margin-bottom: 8px;
    }
    .msg .body > *:first-child {
      margin-top: 0;
    }
    .msg .body > *:last-child {
      margin-bottom: 0;
    }
    .msg .body p:empty {
      display: none;
    }
    .msg-user .body {
      color: var(--fg);
    }
    .msg-ai .body {
      color: var(--fg);
    }
    .rag-sources {
      margin-top: 12px;
      border: 1px solid var(--border);
      border-radius: 6px;
      padding: 8px;
      font-size: 12px;
    }
    .rag-sources summary {
      cursor: pointer;
      color: var(--red);
      font-weight: bold;
      font-size: 11px;
    }
    .rag-source-item {
      margin-top: 8px;
      padding: 6px;
      background: color-mix(in srgb, var(--fg) 4%, transparent);
      border-radius: 4px;
      border-left: 2px solid var(--red);
    }
    .rag-similarity {
      color: color-mix(in srgb, var(--fg) 50%, transparent);
      font-size: 10px;
      margin-left: 6px;
    }
    .rag-snippet {
      color: color-mix(in srgb, var(--fg) 65%, transparent);
      font-size: 11px;
      margin-top: 4px;
      white-space: pre-wrap;
      max-height: 60px;
      overflow: hidden;
    }
    .rag-file-delete {
      background: none;
      border: 1px solid var(--border);
      border-radius: 4px;
      color: color-mix(in srgb, var(--fg) 50%, transparent);
      cursor: pointer;
      font-size: 10px;
      padding: 1px 5px;
    }
    .rag-file-delete:hover { color: var(--fg); border-color: var(--fg); }
    .rag-file-size {
      color: color-mix(in srgb, var(--fg) 50%, transparent);
      font-size: 10px;
      margin-left: 4px;
    }
    .rag-upload-zone {
      border: 2px dashed var(--border);
      border-radius: 6px;
      padding: 12px;
      text-align: center;
      color: color-mix(in srgb, var(--fg) 45%, transparent);
      font-size: 11px;
      cursor: pointer;
      transition: border-color 0.2s;
    }
    .rag-upload-zone.dragover {
      border-color: var(--red);
      color: var(--red);
    }
    .msg .timestamp {
      font-size: 10px;
      color: color-mix(in srgb, var(--fg) 45%, transparent);
      margin-top: 4px;
      text-align: right;
      opacity: 0.7;
    }
    .msg-user .timestamp {
      color: color-mix(in srgb, var(--fg) 72%, transparent);
    }
    pre {
      overflow: hidden;
      padding: 6px 4px 2px 4px;
      border: 1px solid var(--border);
      background: var(--bg);
      position: relative;
      line-height: 1.4;
      font-size: 0.95em;
      font-family: 'Fira Code', 'Courier New', monospace;
      min-height: 38px;
      max-width: 100%;
      margin: 8px 0;
      white-space: pre-wrap;
      word-break: break-word;
      border-radius: 4px;
      min-height: 20px;
      min-width: 80px;
    }
    
    /* Ensure code block headers are only slightly larger than regular text */
    pre h1, pre h2, pre h3, pre h4, pre h5, pre h6 {
      font-size: 1.1em;
      font-weight: bold;
      margin: 8px 0 4px 0;
      color: var(--fg);
    }
    .code-lang {
      font-size:0.8em;
      color:color-mix(in srgb, var(--fg) 60%, transparent);
      display:block;
      margin-bottom:2px;
      font-style:italic;
    }
    code { font-family:inherit; }
    .loading { color:var(--red); font-style:italic; }
    #chat-form { display:none; }

    /* Unified chat input bar */
    .chat-input-bar {
      background: var(--input-bg, var(--panel));
      border: 1px solid var(--input-border, var(--border));
      border-radius: 16px;
      padding: 10px 12px;
      display: flex;
      flex-direction: column;
      gap: 8px;
      max-width: 800px;
      margin-left: auto;
      margin-right: auto;
      width: 100%;
    }
    .chat-input-top {
      width: 100%;
      position: relative;
    }
    .chat-input-top > .model-picker-wrap {
      position: absolute;
      top: 0;
      right: 0;
      z-index: 2;
      transform-origin: top right;
      transition: opacity 0.22s ease, transform 0.22s ease;
      will-change: opacity, transform;
    }
    .chat-input-top > .model-picker-wrap.model-picker-autohide {
      opacity: 0;
      pointer-events: none;
      transform: translateY(-4px) scale(0.96);
    }
    .ghost-text-overlay {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      pointer-events: none;
      white-space: pre-wrap;
      overflow-wrap: break-word;
      font-family: inherit;
      font-size: 14px;
      line-height: 1.5;
      padding: 0;
      border: 1px solid transparent;
      color: transparent;
      z-index: 1;
    }
    .ghost-text-overlay .ghost-suggestion {
      color: color-mix(in srgb, var(--fg) 30%, transparent);
    }
    .chat-input-bar textarea#message {
      width: 100%;
      background: transparent;
      border: none;
      outline: none;
      resize: vertical;
      font-size: 14px;
      line-height: 1.5;
      color: var(--fg);
      min-height: 24px;
      max-height: min(60vh, 600px);
      padding: 0;
      font-family: inherit;
      transition: height 0.12s ease-out;
    }
    .chat-input-bar textarea#message::placeholder {
      color: color-mix(in srgb, var(--fg) 35%, transparent);
    }
    .chat-input-bottom {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-top: 4px;
    }
    /* Progressive shrinkage: as the chat input bar gets narrow, sacrifice
       chrome before the typing area. First the Agent/Chat toggle drops out,
       then the model picker — textarea + send button always stay. Container
       queries so it responds to *bar width*, not viewport width (the bar
       can be in a split pane). */
    .chat-input-bar { container-type: inline-size; container-name: chatbar; }
    @container chatbar (max-width: 340px) {
      .chat-input-right .mode-toggle { display: none !important; }
    }
    @container chatbar (max-width: 260px) {
      #model-picker-wrap { display: none !important; }
    }
    .chat-input-left {
      display: flex;
      gap: 4px;
      align-items: center;
      flex-wrap: nowrap;
      overflow: hidden;
      min-width: 0;
      flex: 1;
    }
    /* Collapsible buttons shrink away; collapsed ones disappear */
    .chat-input-left > .input-icon-btn {
      flex-shrink: 0;
    }
    .chat-input-left > .input-icon-btn.toolbar-collapsed {
      display: none !important;
    }
    /* Always keep overflow wrapper visible and leftmost */
    .chat-input-left > .overflow-wrapper {
      flex-shrink: 0;
      position: relative;
      z-index: 1;
    }
    .input-divider {
      width: 1px;
      height: 16px;
      background: var(--border);
      opacity: 0.4;
      margin: 0 2px;
      flex-shrink: 0;
    }
    .chat-input-right {
      display: flex;
      gap: 8px;
      align-items: center;
      flex-shrink: 0;
    }
    .input-icon-btn {
      background: none;
      border: none;
      color: var(--fg);
      opacity: 0.5;
      cursor: pointer;
      padding: 6px;
      border-radius: 8px;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: opacity 0.15s, background 0.15s, color 0.15s;
      position: relative;
    }
    .input-icon-btn:hover {
      opacity: 0.8;
      background: color-mix(in srgb, var(--accent) 8%, transparent);
    }
    .input-icon-btn.active,
    .input-icon-btn.expanded {
      opacity: 1;
      color: var(--fg);
      background: color-mix(in srgb, var(--fg) 9%, transparent);
    }
    /* While the menu is open the chevron stays in its highlighted state
       — don't run the opacity fade transition so we never flash from
       0.5 → hover-1.0 → drop-back. The state holds steady. */
    .input-icon-btn.expanded { transition: none; }
    /* Accent color for Research, Compare toolbar indicators */
    #research-toggle-btn.active,
    .tool-indicator.active {
      color: var(--red);
      background: color-mix(in srgb, var(--red) 12%, transparent);
    }
    /* Research button glow while actively running */
    #research-toggle-btn.research-running {
      opacity: 1 !important;
      color: var(--red);
      animation: research-pulse 2s ease-in-out infinite;
    }
    @keyframes research-pulse {
      0%, 100% { background: color-mix(in srgb, var(--red) 12%, transparent); }
      50% { background: color-mix(in srgb, var(--red) 22%, transparent); }
    }
    .tool-indicator-x {
      margin-left: 2px;
      opacity: 0.4;
      flex-shrink: 0;
      transition: opacity 0.15s;
    }
    .tool-indicator:hover .tool-indicator-x {
      opacity: 1;
    }
    /* Character indicator — hide name text below 768px, icon always visible */
    @media (max-width: 768px) {
      #character-indicator-name { display: none !important; }
    }

    /* Document indicator — hidden by default, shown when docs exist */
    #doc-indicator-btn { display: none !important; }
    #doc-indicator-btn.visible { display: inline-flex !important; }
    /* On mobile, the minimized-dock chip replaces this indicator — keep
       it hidden regardless of `.visible`. */
    @media (max-width: 768px) {
      #doc-indicator-btn,
      #doc-indicator-btn.visible { display: none !important; }
    }
    .doc-indicator-active {
      color: var(--accent, var(--accent-primary, var(--red))) !important;
      opacity: 1 !important;
    }
    #doc-indicator-btn.active {
      color: var(--accent, var(--accent-primary, var(--red))) !important;
      opacity: 1 !important;
      background: color-mix(in srgb, var(--fg) 9%, transparent) !important;
    }
    #overflow-doc-btn.has-docs,
    #overflow-doc-btn.active {
      color: var(--accent, var(--red));
      opacity: 1;
    }
    #overflow-doc-btn.has-docs.active {
      background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
    }
    .send-btn {
      /* Prefer the theme accent (--red). A stored custom `--send-btn-bg`
         override only kicks in if it's set to a non-white value — guards
         against ChatGPT-style themes where the stored override leaked
         to white and rendered the send button invisible. */
      background: var(--send-btn-bg, var(--red));
      color: #fff;
      border: none;
      border-radius: 8px;
      min-width: 32px;
      width: 32px;
      height: 32px !important;
      padding: 0;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: background 0.25s, border-color 0.25s, color 0.25s, width 0.3s cubic-bezier(0.34, 1, 0.64, 1), padding 0.3s, border-radius 0.3s, opacity 0.1s;
      flex-shrink: 0;
      overflow: hidden;
    }
    /* Instant feedback while the send handler is spinning up before streaming begins */
    .send-btn.send-pending {
      opacity: 0.55;
      pointer-events: none;
      animation: send-pending-pulse 0.9s ease-in-out infinite;
    }
    @keyframes send-pending-pulse {
      0%, 100% { opacity: 0.55; }
      50%      { opacity: 0.85; }
    }
    /* Send button icon transitions — send mode forces no animation */
    .send-btn[data-mode="send"] svg { animation: none !important; }
    /* Spin out: + rotates away, then arrow spins in via anim-spin */
    .send-btn.anim-spin-swap svg { animation: btn-spin-out 0.15s ease-in forwards; }
    .send-btn.anim-spin-swap .send-btn-label { opacity: 0; transition: opacity 0.1s; }
    @keyframes btn-spin-out {
      0%   { transform: rotate(0deg) scale(1); opacity: 1; }
      100% { transform: rotate(90deg) scale(0); opacity: 0; }
    }
    .send-btn.anim-spin svg { animation: btn-spin-in 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); }
    .send-btn.anim-launch svg { animation: btn-launch 0.3s cubic-bezier(0.6, 0, 0.4, 1) forwards; }
    .send-btn.anim-land svg { animation: btn-land 0.25s cubic-bezier(0.34, 1.56, 0.64, 1); }
    @keyframes btn-spin-in {
      0%   { transform: rotate(-180deg) scale(0); opacity: 0; }
      100% { transform: rotate(0deg) scale(1); opacity: 1; }
    }
    /* Arrow flies UP and OUT of the button before the stop icon lands in.
       Stays opaque most of the way so it reads as a real launch instead of a
       fade-in-place. .anim-launch temporarily lifts the button's overflow so
       the icon escapes the 32px box. */
    @keyframes btn-launch {
      0%   { transform: translateY(0)    scale(1);    opacity: 1; }
      70%  { transform: translateY(-30px) scale(0.95); opacity: 1; }
      100% { transform: translateY(-58px) scale(0.55); opacity: 0; }
    }
    .send-btn.anim-launch { overflow: visible !important; }
    .send-btn.anim-launch svg { transform-origin: 50% 50%; }
    @keyframes btn-land {
      0%   { transform: translateY(10px) scale(0.3); opacity: 0; }
      100% { transform: translateY(0) scale(1); opacity: 1; }
    }
    /* Stop button — Processing: Siren pulse, Receiving: Quarter turn spin */
    .send-btn[data-mode="streaming"][data-phase="processing"] svg {
      animation: siren-icon 1.5s ease-in-out infinite;
    }
    .send-btn[data-mode="streaming"][data-phase="receiving"] svg {
      animation: quarter-turn 2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
    }
    @keyframes siren-icon {
      0%, 100% { transform: scale(1); }
      50%      { transform: scale(0.8); }
    }
    @keyframes quarter-turn {
      0%, 15%  { transform: rotate(0deg); }
      25%, 40% { transform: rotate(90deg); }
      50%, 65% { transform: rotate(180deg); }
      75%, 90% { transform: rotate(270deg); }
      100%     { transform: rotate(360deg); }
    }
    .send-btn:hover {
      background: var(--send-btn-hover, color-mix(in srgb, var(--red) 80%, white));
      color: #fff;
    }
    .send-btn.mic-mode,
    .send-btn.newchat-mode {
      /* Idle / new-chat / mic states blend the picked Send Btn color into
         the panel so the user's color choice is visible across every state,
         not only briefly while typing. */
      background: color-mix(in srgb, var(--send-btn-bg, var(--red)) 30%, var(--panel, #2a2a2a));
      color: var(--fg);
      border: 1px solid var(--border);
      transition: width 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.25s, border-color 0.25s, color 0.25s, border-radius 0.3s;
    }
    .send-btn.mic-mode:hover,
    .send-btn.newchat-mode:hover {
      background: color-mix(in srgb, var(--send-btn-hover, var(--send-btn-bg, var(--red))) 85%, var(--panel, #2a2a2a));
      color: #fff;
    }
    /* Hover: just spin the + 90° (no size change). The "New chat" affordance
       is the tooltip (title="New chat") plus the icon rotation.
       Gated on data-mode="newchat" so the arrow variant (empty-session state
       which keeps the newchat-mode class but shows the send arrow) does NOT
       rotate on hover. */
    .send-btn[data-mode="newchat"] svg {
      transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
    }
    .send-btn[data-mode="newchat"]:hover svg {
      transform: rotate(90deg);
    }
    /* "New Session" expanding label */
    .send-btn-label {
      width: 0;
      max-width: 0;
      overflow: hidden;
      white-space: nowrap;
      font-size: 11px;
      font-weight: 500;
      letter-spacing: 0.3px;
      opacity: 0;
      padding: 0;
      margin: 0;
      transition: max-width 0.35s cubic-bezier(0.34, 1.2, 0.64, 1), width 0.35s, opacity 0.25s, margin-left 0.25s;
    }
    .send-btn.newchat-expanded .send-btn-label {
      width: auto;
      max-width: 50px;
      opacity: 1;
      margin-left: 4px;
    }
    .send-btn.newchat-expanded {
      width: 68px;
      padding: 0 10px 0 8px;
    }
    .send-btn.recording {
      background: var(--red) !important;
      color: #fff !important;
      border: none !important;
      animation: pulse-recording 1.5s infinite;
    }
    @keyframes pulse-recording {
      0%, 100% { opacity: 1; }
      50% { opacity: 0.7; }
    }
    /* Mode toggle — Agent / Chat */
    .mode-toggle {
      display: flex;
      flex-shrink: 0;
      height: 28px;
      border: 1px solid var(--border);
      border-radius: 10px;
      overflow: hidden;
      position: relative;
    }
    .mode-toggle::before {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      width: 50%;
      height: 100%;
      background: color-mix(in srgb, var(--fg) 10%, transparent);
      border-radius: 9px;
      transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
      z-index: 0;
    }
    .mode-toggle.mode-chat::before {
      transform: translateX(100%);
    }
    #mode-agent-btn {
      border-radius: 10px 0 0 10px;
    }
    #mode-chat-btn {
      border-radius: 0 10px 10px 0;
    }
    .mode-toggle-btn {
      background: none;
      border: none;
      color: color-mix(in srgb, var(--fg) 40%, transparent);
      cursor: pointer;
      padding: 0 10px;
      font-size: 11px;
      font-weight: 500;
      font-family: inherit;
      transition: color 0.2s;
      white-space: nowrap;
      height: 100%;
      position: relative;
      z-index: 1;
    }
    .mode-toggle-btn:not(.active):hover {
      color: color-mix(in srgb, var(--fg) 60%, transparent);
    }
    .mode-toggle-btn:hover {
      background: none !important;
      border-color: transparent !important;
    }
    .mode-toggle-btn.active,
    .mode-toggle-btn.active:hover,
    .mode-toggle-btn.active:focus {
      color: var(--fg) !important;
      background: none !important;
      border-color: transparent !important;
      cursor: default;
    }
    .mode-toggle-btn + .mode-toggle-btn {
      border-left: none;
    }
    /* Message count badge in the chat-meta header (next to the title).
       Auto-hides when empty so a brand-new chat doesn't show "0 msgs". */
    .chat-meta-count {
      font-size: inherit;
      font-weight: 400;
      color: color-mix(in srgb, var(--fg) 35%, transparent);
      white-space: nowrap;
      user-select: none;
      margin-left: 6px;
    }
    .chat-meta-count:empty { display: none; }
    /* Session cost indicator (next to chevron in header) */
    .session-cost-display {
      font-size: inherit;
      font-weight: 400;
      color: color-mix(in srgb, var(--fg) 35%, transparent);
      white-space: nowrap;
      user-select: none;
      margin-right: 2px;
    }
    /* Model picker — input bar drop-up */
    .model-picker-wrap {
      position: relative;
      flex-shrink: 0;
    }
    .model-picker-btn {
      display: inline-flex;
      align-items: center;
      gap: 4px;
      height: 21px;
      padding: 0 6px;
      font-size: 11px;
      font-weight: 500;
      font-family: inherit;
      background: none;
      border: 1px solid transparent;
      border-radius: 4px;
      /* 65% mix lifts the model label above the WCAG AA 4.5:1 threshold
         against the dark chat-bar (40% only reached ~2.9:1). */
      color: color-mix(in srgb, var(--fg) 65%, transparent);
      cursor: pointer;
      white-space: nowrap;
      transition: background 0.15s, color 0.15s, border-color 0.15s;
    }
    .model-picker-btn:hover {
      border-color: var(--border);
      background: color-mix(in srgb, var(--fg) 8%, transparent);
      color: var(--fg);
    }
    .model-picker-btn #model-picker-label {
      max-width: 120px;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .model-picker-btn svg {
      flex-shrink: 0;
      opacity: 0.5;
    }
    .model-picker-logo {
      display: inline-flex;
      align-items: center;
      vertical-align: -2px;
    }
    .model-picker-logo svg {
      width: 12px;
      height: 12px;
      opacity: 0.7;
    }
    .model-picker-menu {
      position: absolute;
      bottom: calc(100% + 16px);
      right: 0;
      z-index: 300;
      min-width: 260px;
      max-width: 360px;
      background: var(--panel);
      border: 1px solid var(--border);
      border-radius: 8px;
      box-shadow: 0 -4px 20px rgba(0,0,0,0.4);
      padding: 6px;
      animation: picker-roll-up 0.2s ease-out;
      transform-origin: bottom right;
    }
    .model-picker-menu.closing {
      animation: picker-roll-down 0.15s ease-in forwards;
    }
    .model-picker-menu.hidden { display: none; }
    .model-picker-search-row {
      display: grid;
      grid-template-columns: minmax(0, 1fr) 30px;
      gap: 4px;
      align-items: center;
      margin-bottom: 4px;
      transition: grid-template-columns 0.18s ease, gap 0.18s ease;
    }
    .model-picker-menu.no-models .model-picker-search-row {
      margin-bottom: 0;
    }
    .model-picker-search-row.searching {
      grid-template-columns: minmax(0, 1fr) 0px;
      gap: 0;
    }
    .model-picker-search-row.searching .model-picker-action-btn {
      opacity: 0;
      transform: translateX(10px) scale(0.88);
      pointer-events: none;
    }
    .model-picker-menu input[type="text"] {
      width: 100%;
      box-sizing: border-box;
      padding: 6px 8px;
      font-size: 0.82em;
      border: 1px solid var(--border);
      border-radius: 4px;
      background: var(--bg);
      color: var(--fg);
      font-family: inherit;
      outline: none;
      min-width: 0;
      transition: border-color 0.15s, padding 0.18s ease, background 0.18s ease;
    }
    .model-picker-menu input[type="text"]:focus {
      border-color: var(--red);
    }
    .model-picker-menu input[type="text"]::placeholder {
      color: color-mix(in srgb, var(--fg) 30%, transparent);
    }
    .model-picker-action-btn {
      appearance: none;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 30px;
      height: 30px;
      border: 1px solid var(--border);
      border-radius: 4px;
      background: color-mix(in srgb, var(--fg) 4%, transparent);
      color: color-mix(in srgb, var(--fg) 66%, transparent);
      cursor: pointer;
      padding: 0;
      overflow: hidden;
      transform: translateX(0) scale(1);
      transition: opacity 0.16s ease, transform 0.18s ease, border-color 0.15s, color 0.15s, background 0.15s;
    }
    .model-picker-action-btn:hover,
    .model-picker-action-btn:focus-visible {
      border-color: var(--red);
      color: var(--fg);
      background: color-mix(in srgb, var(--red) 10%, var(--panel));
      outline: none;
    }
    .model-picker-action-btn.primary {
      color: var(--red);
      background: color-mix(in srgb, var(--red) 8%, transparent);
    }
    .model-picker-action-btn svg {
      width: 14px;
      height: 14px;
      transition: transform 0.28s ease;
    }
    .model-picker-action-btn:hover svg,
    .model-picker-action-btn:focus-visible svg {
      transform: rotate(90deg);
    }
    .model-picker-list {
      max-height: min(280px, 50dvh);
      overflow-y: auto;
    }
    .model-picker-list.is-empty {
      max-height: 0;
      overflow: hidden;
    }
    .model-picker-list .model-switch-item {
      display: flex;
      align-items: center;
      padding: 5px 8px;
      border-radius: 4px;
      cursor: pointer;
      font-size: 0.82em;
      color: var(--fg);
      gap: 6px;
    }
    .model-picker-list .model-switch-item:hover {
      background: color-mix(in srgb, var(--red) 8%, transparent);
    }
    .model-picker-list .model-switch-empty {
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 5px 8px;
      color: color-mix(in srgb, var(--fg) 50%, transparent);
      font-size: 0.82em;
    }
    .model-picker-list .mp-section-label {
      font-size: 0.72em;
      text-transform: uppercase;
      letter-spacing: 0.05em;
      opacity: 0.4;
      padding: 6px 8px 2px;
    }
    .model-picker-list .mp-section-label:first-child {
      padding-top: 2px;
    }
    /* Model name takes the slack so the endpoint label + favorite dot sit on the right. */
    .model-picker-list .model-switch-item .mp-model-name {
      flex: 1 1 auto;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .model-picker-list .model-switch-item .model-switch-ep {
      flex: 0 1 auto;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      font-size: 0.9em;
      opacity: 0.45;
    }
    /* Keyboard navigation highlight (Arrow keys in the search box). */
    .model-picker-list .model-switch-item.kb-active {
      background: color-mix(in srgb, var(--red) 14%, transparent);
    }
    /* Inline favorite dot — always visible (works on touch), active when on. */
    .model-picker-list .mp-fav-dot {
      flex: 0 0 auto;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 30px;
      height: 24px;
      margin: -5px -8px -5px 8px;
      padding: 0;
      border: none;
      background: transparent;
      cursor: pointer;
      color: color-mix(in srgb, var(--fg) 22%, transparent);
      font-family: inherit;
      font-size: 13px;
      line-height: 1;
      transition: color 0.15s ease, opacity 0.15s ease, transform 0.12s ease;
      -webkit-tap-highlight-color: transparent;
    }
    .model-picker-list .mp-fav-dot:hover {
      color: color-mix(in srgb, var(--fg) 68%, transparent);
    }
    .model-picker-list .mp-fav-dot:focus-visible {
      outline: none;
      color: color-mix(in srgb, var(--fg) 68%, transparent);
    }
    .model-picker-list .mp-fav-dot.active {
      color: var(--accent, var(--red));
      opacity: 1;
    }
    .model-picker-list .mp-fav-dot.active:hover {
      color: var(--accent, var(--red));
      opacity: 0.72;
    }
    .model-picker-list .mp-fav-dot.pulse {
      animation: mpFavPulse 0.32s ease-out;
    }
    @keyframes mpFavPulse {
      0% { text-shadow: 0 0 0 color-mix(in srgb, var(--accent, var(--red)) 0%, transparent); }
      45% { text-shadow: 0 0 10px color-mix(in srgb, var(--accent, var(--red)) 60%, transparent); }
      100% { text-shadow: 0 0 0 color-mix(in srgb, var(--accent, var(--red)) 0%, transparent); }
    }
    /* First-run hint when a large catalog has no Recent/Favorites yet. */
    .model-picker-list .mp-empty-hint {
      flex-direction: column;
      gap: 2px;
      padding: 14px 8px;
      text-align: center;
    }
    .model-picker-list .mp-empty-hint .mp-empty-title {
      font-size: 1.05em;
      color: color-mix(in srgb, var(--fg) 70%, transparent);
    }
    .model-picker-list .mp-empty-hint .mp-empty-sub {
      font-size: 0.92em;
      opacity: 0.7;
    }
    /* Provider group headers */
    .model-picker-list .mp-provider-header {
      display: flex;
      align-items: center;
      gap: 6px;
      padding: 5px 8px;
      cursor: pointer;
      font-size: 0.78em;
      font-weight: 500;
      color: var(--fg);
      border-radius: 4px;
      user-select: none;
    }
    .model-picker-list .mp-provider-header:hover {
      background: color-mix(in srgb, var(--fg) 6%, transparent);
    }
    .model-picker-list .mp-provider-chevron {
      display: inline-flex;
      opacity: 0.4;
      transition: transform 0.2s, opacity 0.15s;
      flex-shrink: 0;
    }
    .model-picker-list .mp-provider-header:hover .mp-provider-chevron {
      opacity: 0.7;
    }
    .model-picker-list .mp-provider-chevron.collapsed {
      transform: rotate(-90deg);
    }
    .model-picker-list .mp-provider-name { flex: 1; }
    .model-picker-list .mp-provider-count { font-size: 0.85em; opacity: 0.4; }
    /* Domino expand (15% faster than sidebar) */
    .mp-provider-group.mp-just-expanded .model-switch-item {
      animation: mp-domino-in 0.31s cubic-bezier(0.22, 1.61, 0.36, 1) backwards;
    }
    .mp-provider-group.mp-just-expanded .model-switch-item:nth-child(1)  { animation-delay: 0.035s; }
    .mp-provider-group.mp-just-expanded .model-switch-item:nth-child(2)  { animation-delay: 0.07s; }
    .mp-provider-group.mp-just-expanded .model-switch-item:nth-child(3)  { animation-delay: 0.105s; }
    .mp-provider-group.mp-just-expanded .model-switch-item:nth-child(4)  { animation-delay: 0.14s; }
    .mp-provider-group.mp-just-expanded .model-switch-item:nth-child(5)  { animation-delay: 0.175s; }
    .mp-provider-group.mp-just-expanded .model-switch-item:nth-child(6)  { animation-delay: 0.21s; }
    .mp-provider-group.mp-just-expanded .model-switch-item:nth-child(7)  { animation-delay: 0.245s; }
    .mp-provider-group.mp-just-expanded .model-switch-item:nth-child(8)  { animation-delay: 0.28s; }
    .mp-provider-group.mp-just-expanded .model-switch-item:nth-child(9)  { animation-delay: 0.315s; }
    .mp-provider-group.mp-just-expanded .model-switch-item:nth-child(10) { animation-delay: 0.35s; }
    @keyframes mp-domino-in {
      0%   { opacity: 0; transform: translateY(6px) scale(0.94); }
      60%  { opacity: 1; }
      100% { opacity: 1; transform: translateY(0) scale(1); }
    }
    /* Comfortable touch targets on phones / narrow screens. */
    @media (hover: none) and (pointer: coarse), (max-width: 768px) {
      .model-picker-list .model-switch-item {
        padding-top: 8px;
        padding-bottom: 8px;
      }
      .model-picker-list .mp-fav-dot {
        width: 30px;
        height: 30px;
        margin: -7px -8px -7px 8px;
      }
    }
    /* Overflow "+" menu */
    .overflow-wrapper {
      position: relative;
      display: flex;
      align-items: center;
    }
    .plus-active-dot {
      position: absolute;
      top: 2px;
      right: 2px;
      width: 6px;
      height: 6px;
      background: var(--fg);
      border-radius: 50%;
      display: none;
    }
    .overflow-plus-btn.has-active .plus-active-dot {
      display: block;
    }
    .overflow-menu {
      position: fixed;
      background: var(--panel);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 4px;
      min-width: 170px;
      box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
      z-index: 1000;
      /* Container spring-in: the rounded panel scales/grows out of the
         chevron's position before the menu items domino in on top. */
      transform-origin: bottom left;
      animation: overflow-menu-pop 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
    }
    @keyframes overflow-menu-pop {
      0%   { transform: scale(0.6) translateY(8px); opacity: 0; }
      100% { transform: scale(1)   translateY(0);   opacity: 1; }
    }
    .overflow-menu.hidden {
      display: none;
    }
    /* Closing state: JS adds `.closing` to play the fold-in animation,
       waits for it to finish, then flips to `.hidden`. Container
       scales/translates back into the chevron while items peel off from
       the top down so the menu collapses into its anchor. */
    .overflow-menu.closing {
      animation: overflow-menu-pop-out 0.22s cubic-bezier(0.5, 0, 0.75, 0) forwards;
      animation-delay: 0.16s;
    }
    @keyframes overflow-menu-pop-out {
      0%   { transform: scale(1)   translateY(0);   opacity: 1; }
      100% { transform: scale(0.6) translateY(8px); opacity: 0; }
    }
    .overflow-menu-item {
      display: flex;
      align-items: center;
      gap: 8px;
      width: 100%;
      padding: 8px 12px;
      background: none;
      border: none;
      color: var(--fg);
      opacity: 0.7;
      cursor: pointer;
      border-radius: 6px;
      font-size: 13px;
      font-family: inherit;
      transition: background 0.15s, opacity 0.15s, color 0.15s;
      /* Domino-style cascade: each item slides up + fades in with a tiny
         delay after the previous one (set via nth-last-child below so the
         BOTTOM item appears first and the cascade rolls upward — visually
         feels like the menu is "stacking up" from the chevron). The
         container itself springs in via .overflow-menu's keyframe. */
      animation: overflow-item-in 0.32s cubic-bezier(0.22, 1.61, 0.36, 1) backwards;
    }
    .overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(1)  { animation-delay: 0.06s; }
    .overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(2)  { animation-delay: 0.10s; }
    .overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(3)  { animation-delay: 0.14s; }
    .overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(4)  { animation-delay: 0.18s; }
    .overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(5)  { animation-delay: 0.22s; }
    .overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(6)  { animation-delay: 0.26s; }
    .overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(7)  { animation-delay: 0.30s; }
    .overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(8)  { animation-delay: 0.34s; }
    .overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(9)  { animation-delay: 0.38s; }
    .overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(10) { animation-delay: 0.42s; }
    @keyframes overflow-item-in {
      0%   { opacity: 0; transform: translateY(10px) translateX(-6px) scale(0.9); }
      60%  { opacity: 1; }
      100% { opacity: 1; transform: translateY(0)    translateX(0)    scale(1); }
    }
    /* Fold-in: items peel off top-down (mirror of the open's bottom-up
       cascade) so the menu visibly empties before the container scales
       back into the chevron. */
    .overflow-menu.closing .overflow-menu-item {
      animation: overflow-item-out 0.20s ease-in forwards;
    }
    .overflow-menu.closing .overflow-menu-item:nth-child(1)  { animation-delay: 0.00s; }
    .overflow-menu.closing .overflow-menu-item:nth-child(2)  { animation-delay: 0.02s; }
    .overflow-menu.closing .overflow-menu-item:nth-child(3)  { animation-delay: 0.04s; }
    .overflow-menu.closing .overflow-menu-item:nth-child(4)  { animation-delay: 0.06s; }
    .overflow-menu.closing .overflow-menu-item:nth-child(5)  { animation-delay: 0.08s; }
    .overflow-menu.closing .overflow-menu-item:nth-child(6)  { animation-delay: 0.10s; }
    .overflow-menu.closing .overflow-menu-item:nth-child(7)  { animation-delay: 0.12s; }
    .overflow-menu.closing .overflow-menu-item:nth-child(8)  { animation-delay: 0.14s; }
    .overflow-menu.closing .overflow-menu-item:nth-child(9)  { animation-delay: 0.16s; }
    .overflow-menu.closing .overflow-menu-item:nth-child(10) { animation-delay: 0.18s; }
    @keyframes overflow-item-out {
      0%   { opacity: 1; transform: translateY(0)   translateX(0)    scale(1); }
      100% { opacity: 0; transform: translateY(6px) translateX(-3px) scale(0.92); }
    }
    .overflow-menu-item:hover {
      opacity: 1;
      background: color-mix(in srgb, var(--red) 10%, transparent);
    }
    #overflow-attach-btn {
      position: relative;
      font-weight: 600;
    }
    #overflow-attach-btn svg {
      transition: transform 0.16s cubic-bezier(0.34, 1.56, 0.64, 1);
    }
    .overflow-menu-item.active {
      opacity: 1;
      color: var(--fg);
    }
    .overflow-active-dot {
      width: 6px;
      height: 6px;
      background: var(--fg);
      border-radius: 50%;
      margin-left: auto;
      display: none;
      flex-shrink: 0;
    }
    .overflow-menu-item.active .overflow-active-dot {
      display: block;
    }
    .attach-strip {
      display: flex;
      gap: 6px;
      flex-wrap: wrap;
      margin: 0 0 8px;
      min-height: 0;
    }
    .attach-strip:empty {
      display: none;
    }
    .attach-strip { 
      display: flex; 
      gap: 6px; 
      flex-wrap: wrap; 
      margin: 6px 0 0; 
      min-height: 32px;
      padding: 2px;
    }
    .attachment-placeholder {
      display: flex;
      align-items: center;
      color: var(--fg);
      opacity: 0.7;
      font-style: italic;
      padding: 4px 8px;
      border-radius: 4px;
      background: color-mix(in srgb, var(--fg) 6%, transparent);
    }
    .attach-btn { width:32px; display:grid; place-items:center; }
    .hidden { display:none; }
    .toggle { position:relative; display:inline-block; width:30px; height:16px; vertical-align:middle; }
    .toggle input { opacity:0; width:0; height:0; }
    .toggle .slider {
      position:absolute; cursor:pointer; top:0; left:0; right:0; bottom:0;
      background:color-mix(in srgb, var(--fg) 15%, transparent); transition:background .08s; border-radius:8px;
    }
    .toggle .slider:before {
      position:absolute; content:""; height:12px; width:12px; left:2px; top:2px;
      background:var(--panel); border-radius:50%; transform:translateX(0); transition:transform .08s;
      box-shadow:0 1px 2px rgba(0,0,0,0.25);
    }
    .toggle input:checked + .slider { background:var(--red); }
    .toggle input:checked + .slider:before { transform:translateX(14px); }
    .copy-btn {
      position:absolute; top:6px; right:6px;
      background:var(--bg); color:var(--fg);
      border:1px solid var(--border); border-radius:6px;
      font-size:12px; height:24px; padding:0 8px; cursor:pointer;
      opacity:0; transition:.15s;
    }
    .msg:hover .copy-btn { opacity:1; }
    .role-timestamp {
      font-size:0.7rem; color:var(--color-muted-alt); font-weight:normal; margin-left:6px;
    }
    .msg-footer {
      display:flex; align-items:center; gap:6px;
      flex-wrap: wrap;
      margin-top:6px;
      color:var(--color-muted-alt); font-size:0.75rem;
      position: relative;
    }
    .msg-footer .timestamp {
      font-size:0.75rem; color:var(--color-muted-alt);
      margin:0; opacity:1;
    }
    .msg-footer .response-metrics {
      font-size:0.75rem; color:var(--color-muted-alt);
      transition: color .15s;
    }
    .msg-footer .response-metrics:hover { color:var(--fg); }
    /* Context usage ring — right side of footer */
    .ctx-ring {
      display: inline-flex;
      align-items: center;
      gap: 3px;
      margin-left: auto;
      line-height: 0;
      opacity: 0.6;
      cursor: default;
      transition: opacity 0.15s;
      --ctx-stroke: var(--color-muted, #888);
    }
    .ctx-ring .ctx-ring-pct {
      color: var(--color-muted, #888);
      transition: color 0.1s ease;
    }
    .ctx-ring svg circle {
      transition: stroke 0.1s ease;
    }
    .ctx-ring:hover {
      opacity: 1;
      --ctx-stroke: var(--ctx-color);
    }
    .ctx-ring:hover .ctx-ring-pct {
      color: var(--ctx-color);
    }
    .ctx-ring-pct {
      font-size: 0.7rem;
      font-weight: 600;
      line-height: 1;
    }
    /* Context detail popup */
    .ctx-detail-popup {
      position: fixed;
      z-index: 200;
      background: var(--panel, var(--bg));
      border: 1px solid var(--border);
      border-radius: 8px;
      padding: 12px 14px;
      box-shadow: 0 4px 20px rgba(0,0,0,0.4);
      min-width: 220px;
      max-width: 280px;
      font-size: 0.85rem;
      color: var(--fg);
    }
    .ctx-bar-wrap {
      width: 100%;
      height: 6px;
      background: var(--border);
      border-radius: 4px;
      overflow: hidden;
    }
    .ctx-bar-fill {
      height: 100%;
      border-radius: 4px;
      transition: width 0.3s;
    }
    .ctx-compact-btn {
      display: block;
      width: 100%;
      margin-top: 10px;
      padding: 6px 0;
      background: none;
      border: 1px solid var(--border);
      border-radius: 6px;
      color: var(--fg);
      font-size: 0.8rem;
      cursor: pointer;
      transition: border-color 0.15s, color 0.15s;
    }
    .ctx-compact-btn:hover {
      border-color: var(--accent, var(--red));
      color: var(--accent, var(--red));
    }
    .ctx-compact-btn:disabled {
      opacity: 0.5;
      cursor: default;
    }
    .compact-wave {
      display: inline-block;
      color: var(--accent, var(--red));
      letter-spacing: 1px;
      font-size: 0.9em;
    }
    /* Memory-used indicator pill */
    .memory-used-pill {
      display: inline-flex;
      align-items: center;
      background: var(--panel);
      border: 1px solid var(--border);
      color: var(--fg);
      font-size: 0.7rem;
      padding: 2px 8px;
      border-radius: 10px;
      cursor: pointer;
      opacity: 0.6;
      transition: opacity 0.15s, background 0.15s;
      white-space: nowrap;
      position: relative;
      z-index: 1;
    }
    .memory-used-pill:hover { opacity: 1; background: var(--border); }
    /* Nudge label text 1px down so it visually centers with the icon. */
    .memory-used-pill-text { position: relative; top: 1px; display: inline-block; }
    .memory-used-detail {
      position: fixed;
      background: var(--panel);
      border: 1px solid var(--border);
      border-radius: 8px;
      padding: 6px 8px;
      min-width: 200px;
      max-width: min(360px, calc(100vw - 16px));
      max-height: 50vh;
      overflow-y: auto;
      z-index: 300;
      box-shadow: 0 4px 12px rgba(0,0,0,0.3);
      font-size: 0.75rem;
    }
    .memory-used-row {
      display: flex;
      align-items: flex-start;
      gap: 6px;
      padding: 3px 0;
      line-height: 1.3;
    }
    .memory-used-row + .memory-used-row {
      border-top: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
    }
    .memory-used-badge {
      flex-shrink: 0;
      font-size: 0.7rem;
      width: 16px;
      text-align: center;
    }
    .memory-used-badge.pinned { color: var(--red); }
    .memory-used-badge.recalled { color: var(--fg); opacity: 0.5; }
    .memory-used-text {
      color: var(--fg);
      opacity: 0.85;
    }

    .msg-actions {
      display:inline-flex; align-items:center; gap:4px;
    }
    .footer-copy-btn {
      background:none; border:none;
      color:var(--color-muted-alt); cursor:pointer;
      padding:2px 6px; border-radius:4px;
      transition: color .15s;
      line-height:1;
      display: inline-flex; align-items: center; justify-content: center;
    }
    .footer-copy-btn:hover { color:var(--accent); }
    /* Delete action — same chrome as copy/download/edit but hover reveals
       the destructive red tint so the user can tell it's not a benign op. */
    .footer-delete-btn:hover { color: var(--red); }
    .regen-btn {
      background:none; border:none;
      color:var(--color-muted-alt); font-size:1.1rem; cursor:pointer;
      padding:2px 6px; border-radius:4px;
      transition: color .15s;
      line-height:1;
    }
    .regen-btn:hover { color:var(--accent); }
    .fork-btn {
      background:none; border:none;
      color:var(--color-muted-alt); font-size:1.1rem; cursor:pointer;
      padding:2px 6px; border-radius:4px;
      transition: color .15s;
      line-height:1;
    }
    .fork-btn:hover { color:var(--accent); }
    .msg-action-btn {
      background:none; border:none;
      color:var(--muted, var(--color-muted-alt)); font-size:1.1rem; cursor:pointer;
      padding:2px 6px; border-radius:4px;
      transition: color .15s;
      line-height:1;
    }
    .msg-action-btn:hover { color:var(--accent); }
    .msg-action-btn[data-action="shorten"] { position:relative; top:1px; font-size:1.25rem; }
    .msg-delete-btn { position:relative; top:1px; }
    .msg-delete-btn:hover { color:var(--red); }
    .msg-more-btn {
      font-size:0.7rem; letter-spacing:0.5px;
    }
    .msg-overflow-menu {
      position:fixed;
      background:var(--panel);
      border:1px solid var(--border);
      border-radius:6px;
      padding:4px;
      box-shadow:0 4px 16px rgba(0,0,0,0.4);
      z-index:100;
      min-width:150px;
    }
    .msg-overflow-item {
      display:flex;
      align-items:center;
      gap:6px;
      width:100%;
      background:none;
      border:none;
      border-bottom:1px solid color-mix(in srgb, var(--border) 40%, transparent);
      color:var(--fg);
      font-size:0.8rem;
      padding:5px 8px;
      border-radius:4px;
      cursor:pointer;
      text-align:left;
      font-family:inherit;
      transition:background .1s;
    }
    .msg-overflow-item:last-child { border-bottom:none; }
    .msg-overflow-item:hover {
      background:color-mix(in srgb, var(--red) 8%, transparent);
    }
    .overflow-icon {
      width:16px;
      text-align:center;
      flex-shrink:0;
      font-size:1rem;
    }
    .variant-nav {
      display:inline-flex;
      align-items:center;
      gap:0px;
      margin-left:auto;
      font-size:0.85em;
      font-family:inherit;
      opacity:0.6;
    }
    .variant-divider {
      opacity:0.25;
      margin:0 4px 0 2px;
    }
    .variant-tag {
      font-size:1.1em;
      opacity:0.8;
      margin-right:2px;
      position:relative;
      top:-1px;
    }
    .variant-tag-scissors {
      top:-1px;
    }
    /* The ✂ "Rewrite shorter" action button glyph sits a touch low against the
       other footer icons — nudge it up 2px. */
    .msg-action-btn[data-action="shorten"] {
      position: relative;
      top: -2px;
    }
    /* The "?" Explain-simpler glyph also sits slightly low — nudge it up 1px. */
    .msg-action-btn[data-action="explain"] {
      position: relative;
      top: -1px;
    }
    .variant-btn {
      background:none;
      border:none;
      color:var(--fg);
      cursor:pointer;
      padding:4px 6px;
      font-size:1em;
      font-family:inherit;
      opacity:0.7;
      line-height:1;
    }
    .variant-btn:hover { opacity:1; color:var(--fg); }
    .variant-btn:disabled { opacity:0.3; cursor:default; }
    .variant-num {
      background:none;
      border:none;
      color:var(--fg);
      cursor:pointer;
      padding:4px 4px;
      font-size:1em;
      font-family:inherit;
      opacity:0.7;
      line-height:1;
    }
    .variant-num:hover { opacity:1; color:var(--fg); }
    .variant-num:disabled { opacity:0.5; cursor:default; }
    .variant-slash {
      opacity:0.4;
      font-size:1em;
    }
    .continue-separator {
      display:inline;
      opacity:0.35;
      font-size:0.85em;
      font-style:italic;
    }
    .stopped-indicator {
      color:var(--red);
      margin-top:8px;
      font-size:0.85em;
      opacity:0.8;
      display:flex;
      align-items:center;
      gap:8px;
      flex-wrap:wrap;
    }

    /* Message edit UI */
    .msg-edit-textarea {
      background:var(--bg);
      color:var(--fg);
      border:1px solid var(--border);
      border-radius:6px;
      padding:10px;
      font-family:inherit;
      font-size:inherit;
      line-height:1.6;
      resize:vertical;
      box-sizing:border-box;
    }
    .msg-edit-textarea:focus { outline:1px solid var(--hl-function, #61afef); border-color:var(--hl-function, #61afef); }
    .msg-edit-bar {
      display:flex;
      gap:8px;
      margin-top:6px;
    }
    .msg-edit-save, .msg-edit-cancel {
      background:var(--bg);
      color:var(--fg);
      border:1px solid var(--border);
      border-radius:6px;
      padding:4px 14px;
      cursor:pointer;
      font-size:0.85em;
    }
    .msg-edit-save:hover { border-color:var(--color-save-green, #4caf50); color:var(--color-save-green, #4caf50); }
    .msg-edit-cancel:hover { border-color:var(--red); color:var(--red); }

    /* Edited indicator — similar to stopped-indicator */
    .edited-indicator {
      color:var(--fg);
      margin-top:8px;
      font-size:0.85em;
      opacity:0.4;
      font-style:italic;
    }
    .continue-btn {
      background:none;
      border:none;
      color:var(--fg);
      opacity:0.5;
      cursor:pointer;
      font-size:2.6em;
      padding:2px 2px 0;
      line-height:1;
    }
    .continue-btn:hover {
      opacity:0.8;
    }
    .ctx-indicator {
      display:inline-flex; align-items:center; gap:1px;
      font-size:0.75rem;
    }
    .ctx-popup {
      position:fixed;
      z-index:250;
      background:var(--panel);
      border:1px solid var(--border);
      border-radius:8px;
      padding:10px 14px;
      font-size:0.8rem;
      color:var(--fg);
      box-shadow:0 8px 24px rgba(0,0,0,0.4);
      min-width:180px;
      line-height:1.7;
    }
    .ctx-label {
      display:inline-block;
      width:60px;
      color:var(--color-muted-alt);
      font-size:0.75rem;
    }
    .edit-btn {
      background:none; border:none;
      color:var(--color-muted-alt); font-size:1.1rem; cursor:pointer;
      padding:2px 6px; border-radius:4px;
      transition: color .15s;
      line-height:1;
    }
    .edit-btn:hover { color:var(--accent); }
    .edit-textarea {
      width:100%; background:var(--bg); color:var(--fg);
      border:1px solid var(--border); border-radius:6px;
      padding:8px; font-family:inherit; font-size:0.95rem;
      resize:vertical; outline:none;
      min-height:80px;
    }
    .edit-textarea:focus { border-color:var(--red); }
    .edit-save-btn, .edit-cancel-btn {
      background:var(--bg); color:var(--fg);
      border:1px solid var(--border); border-radius:6px;
      padding:4px 12px; cursor:pointer; font-size:0.8rem;
    }
    .edit-save-btn:hover { background:var(--panel); }
    .edit-cancel-btn:hover { background:var(--panel); }
    pre .copy-code {
      position:absolute; right:6px;
      background:var(--bg); color:var(--fg);
      border:1px solid var(--border); border-radius:6px;
      width:28px; height:28px; padding:0; cursor:pointer;
      opacity:0; transition: opacity .15s, color .15s, border-color .15s;
      display:flex; align-items:center; justify-content:center;
    }
    pre .copy-code { top:6px; }
    pre .copy-code.bottom { top:auto; bottom:6px; }
    pre:hover .copy-code { opacity:0.7; }
    pre .copy-code:hover { opacity:1; }
    pre .copy-code.copied {
      opacity: 1;
      color: var(--color-save-green, #4caf50);
      border-color: var(--color-save-green, #4caf50);
      background: color-mix(in srgb, var(--color-save-green, #4caf50) 18%, var(--bg));
      animation: code-copy-pulse 0.36s cubic-bezier(0.34, 1.56, 0.64, 1);
    }
    @keyframes code-copy-pulse {
      0%   { transform: scale(1); }
      50%  { transform: scale(1.15); }
      100% { transform: scale(1); }
    }
    /* Slim text-button variant: swap "Copy" → "✓ Copied" via the
       ::before content while still inheriting the green flash + pulse. */
    pre.pre-compact .copy-code.copied::before { content: '✓ Copied'; }

    /* Edit code button — positioned left of copy button */
    pre .edit-code {
      position:absolute; right:42px; top:6px;
      background:var(--bg); color:var(--fg);
      border:1px solid var(--border); border-radius:6px;
      width:28px; height:28px; padding:0; cursor:pointer;
      opacity:0; transition: opacity .15s, color .15s, border-color .15s;
      display:flex; align-items:center; justify-content:center;
    }
    pre .edit-code.bottom { top:auto; bottom:6px; }
    pre:hover .edit-code { opacity:0.7; }
    pre .edit-code:hover { opacity:1; }
    /* When the edit-code button is in "save" mode (checkmark), use the
       theme accent so it matches the EDITING outline + label that are
       also accent-coloured — clearer that this is the confirm action. */
    pre .edit-code.active {
      opacity: 1;
      color: var(--accent-primary, var(--red));
      border-color: var(--accent-primary, var(--red));
      background: color-mix(in srgb, var(--accent-primary, var(--red)) 12%, var(--bg));
    }
    pre .use-code {
      position:absolute; right:42px; top:6px;
      background:var(--bg); color:var(--fg);
      border:1px solid var(--border); border-radius:6px;
      width:28px; height:28px; padding:0; cursor:pointer;
      opacity:0; transition: opacity .15s, color .15s, border-color .15s;
      display:flex; align-items:center; justify-content:center;
    }
    pre .use-code.bottom { top:auto; bottom:6px; }
    pre:hover .use-code { opacity:0.7; }
    pre .use-code:hover { opacity:1; }
    pre .use-code.used {
      opacity: 1;
      color: var(--color-save-green, #4caf50);
      border-color: var(--color-save-green, #4caf50);
      background: color-mix(in srgb, var(--color-save-green, #4caf50) 18%, var(--bg));
      animation: code-copy-pulse 0.36s cubic-bezier(0.34, 1.56, 0.64, 1);
    }
    .setup-trigger-link, .setup-clickable-provider, .setup-clickable-code {
      transition: color 0.15s ease, opacity 0.15s ease;
    }
    .setup-trigger-link:hover,
    .setup-clickable-provider:hover,
    .setup-clickable-code:hover {
      color: var(--accent, var(--red)) !important;
      opacity: 0.9;
    }

    /* Tapping the code body (not a button) toggles the overlay buttons off so
       they stop covering the text on touch screens. Tap again to bring back. */
    pre.buttons-hidden .copy-code,
    pre.buttons-hidden .edit-code,
    pre.buttons-hidden .run-code { opacity:0 !important; pointer-events:none !important; }

    /* Editing state — subtle border on the code block */
    /* Editing state: was a 1px subtle outline that was almost invisible on
       mobile, so users couldn't tell their tap-to-edit had actually engaged.
       Use the accent colour + a tinted background so it reads at a glance. */
    pre.editing {
      outline: 2px solid var(--accent-primary, var(--red));
      outline-offset: -2px;
      background: color-mix(in srgb, var(--accent-primary, var(--red)) 6%, var(--bg)) !important;
    }
    pre.editing code.editing { outline:none; cursor:text; }
    pre.editing::before {
      content: 'EDITING';
      position: absolute; top: 0; left: 0;
      padding: 2px 8px;
      font-size: 9px; font-weight: 700; letter-spacing: 0.5px;
      background: var(--accent-primary, var(--red));
      color: #fff;
      border-radius: 0 0 4px 0;
      z-index: 2;
      pointer-events: none;
    }

    /* Run code button — positioned left of edit button */
    pre .run-code {
      position:absolute; right:78px; top:6px;
      background:var(--bg); color:var(--fg);
      border:1px solid var(--border); border-radius:6px;
      width:28px; height:28px; padding:0; cursor:pointer;
      opacity:0; transition: opacity .15s, color .15s, border-color .15s;
      display:flex; align-items:center; justify-content:center;
    }
    pre .run-code.bottom { top:auto; bottom:6px; }
    pre:hover .run-code { opacity:0.7; }
    pre .run-code:hover { opacity:1; color:var(--hl-function, #61afef); border-color:var(--hl-function, #61afef); }

    /* Compact (single-line) code blocks: slim buttons so the row doesn't
       double the height of a 1-line bash. Copy is text ("Copy" → "✓ Copied"),
       Run and Edit keep their icons but at smaller sizes. Edit swaps to
       a "Save" text label when its .active state is on. */
    pre.pre-compact { padding-right: 200px; min-height: 0; }
    pre.pre-compact .copy-code,
    pre.pre-compact .edit-code,
    pre.pre-compact .run-code {
      height: 20px;
      padding: 0;
      font-size: 10px;
      font-weight: 500;
      line-height: 20px;
      top: 3px;
    }
    /* Copy: text-only, hide SVG */
    pre.pre-compact .copy-code {
      width: auto;
      padding: 0 8px;
      right: 6px;
      gap: 0;
    }
    pre.pre-compact .copy-code svg { display: none; }
    pre.pre-compact .copy-code::before { content: 'Copy'; }
    pre.pre-compact .copy-code.copied::before { content: '✓ Copied'; }
    /* Edit: icon + "Edit" label, swap to "Save" when editing */
    pre.pre-compact .edit-code {
      width: auto;
      padding: 0 8px 0 6px;
      gap: 3px;
      right: 64px;
    }
    pre.pre-compact .edit-code svg { width: 12px; height: 12px; }
    pre.pre-compact .edit-code::after { content: 'Edit'; }
    pre.pre-compact .edit-code.active::after { content: 'Save'; }
    /* Run: icon + "Run" label */
    pre.pre-compact .run-code {
      width: auto;
      padding: 0 8px 0 6px;
      gap: 3px;
      right: 126px;
    }
    pre.pre-compact .run-code svg { width: 12px; height: 12px; }
    pre.pre-compact .run-code::after { content: 'Run'; }
    /* Bottom-positioned slim buttons (when pre is near the top of the
       viewport, the existing JS toggles .bottom to flip them down). */
    pre.pre-compact .copy-code.bottom,
    pre.pre-compact .edit-code.bottom,
    pre.pre-compact .run-code.bottom { top: auto; bottom: 3px; }

    /* Touch devices: no hover, so always show copy/run/edit buttons */
    @media (hover: none) {
      pre .copy-code { opacity:0.7; }
      pre .edit-code { opacity:0.7; }
      pre .run-code { opacity:0.7; }
    }

    /* Code runner output panel */
    .code-runner-output {
      position:relative;
      border:1px solid var(--border); border-top:2px solid var(--hl-function, #61afef);
      border-radius:0 0 4px 4px;
      background:var(--bg);
      margin:-4px 0 8px 0;
      padding:8px 12px;
      max-height:400px;
      overflow:auto;
    }
    .code-runner-pre {
      margin:0; padding:0;
      font-family:'Fira Code', 'Courier New', monospace;
      font-size:0.9em; line-height:1.5;
      white-space:pre-wrap; word-break:break-word;
      color:var(--fg);
      background:none !important;
      border:none !important;
    }
    .code-runner-error { color:var(--red); }
    .code-runner-loading { font-style:italic; color:var(--red); padding:4px 0; }
    .code-runner-close {
      position:absolute; top:4px; right:4px;
      background:none; border:none; color:var(--fg);
      cursor:pointer; opacity:0.5; font-size:14px; padding:2px 6px;
    }
    .code-runner-close:hover { opacity:1; }
    /* Labeled copy pill — pinned top-right INSIDE the run-output panel, not
       in a separate footer. Panel is position:relative so absolute works. */
    .code-runner-copy-inline {
      position: absolute; top: 6px; right: 32px;  /* sits LEFT of the X close (top:4 right:4 ~24px wide) */
      z-index: 2;
      background: var(--panel); color: var(--fg);
      border: 1px solid var(--border); border-radius: 6px;
      padding: 3px 10px; font-size: 11px; cursor: pointer;
      display: inline-flex; align-items: center;
      transition: border-color 0.15s, color 0.15s, background 0.15s;
    }
    .code-runner-copy-inline:hover {
      border-color: var(--accent-primary, var(--red));
      color: var(--accent-primary, var(--red));
    }
    /* Reserve room on the right so the output text doesn't slide under
       either the Copy pill or the Close X. Applies to both the chat run
       panel AND the document panel (doc-run-output reuses the same children). */
    .code-runner-output,
    .doc-run-output { padding-right: 110px; }

    .toast {
      position:fixed;
      top: 16px; right: 16px;
      left: auto; bottom: auto;
      background:var(--panel); color:var(--fg);
      border:1px solid color-mix(in srgb, var(--accent) 30%, transparent);
      border-left: 3px solid var(--accent);
      padding:8px 12px; border-radius:6px; font-size:12px; opacity:0;
      /* Off-screen to the right by default; .show slides to 0;
         removing .show transitions to -120% (off-screen left). */
      transform: translateX(120%);
      transition: opacity .35s cubic-bezier(0.22, 1, 0.36, 1),
                  transform .45s cubic-bezier(0.22, 1, 0.36, 1);
      z-index: 9999; pointer-events: none;
      box-shadow: 0 4px 12px rgba(0,0,0,0.2);
      backdrop-filter: blur(12px);
      max-width: min(360px, calc(100vw - 32px));
      min-width: min(220px, calc(100vw - 32px));
      min-height: 34px;
      display: inline-flex;
      align-items: center;
      box-sizing: border-box;
    }
    .toast.show { opacity:1; transform: translateX(0); }
    .toast .toast-checkmark {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 16px;
      height: 16px;
      margin-right: 7px;
      color: var(--green, #50fa7b);
      vertical-align: -3px;
      transform: scale(0.65);
      opacity: 0;
      animation: toastCheckPop 360ms cubic-bezier(0.2, 0.9, 0.25, 1.25) forwards;
    }
    .toast .toast-checkmark svg polyline {
      stroke-dasharray: 24;
      stroke-dashoffset: 24;
      animation: toastCheckDraw 420ms ease-out 120ms forwards;
    }
    @keyframes toastCheckPop {
      0% { opacity: 0; transform: scale(0.65); }
      65% { opacity: 1; transform: scale(1.16); }
      100% { opacity: 1; transform: scale(1); }
    }
    @keyframes toastCheckDraw {
      to { stroke-dashoffset: 0; }
    }
    .toast.exiting {
      opacity: 0;
      transform: translateX(-120%);
    }
    .toast.error {
      border-color: color-mix(in srgb, var(--color-error) 40%, transparent);
      border-left-color: var(--color-error);
      color: var(--color-error);
    }
    /* When the notes panel is docked to the right, the default top-right toast
       sits directly over the Archive / View-toggle buttons in the panel header.
       Flip it to the top-left so you can still reach the header after an
       archive action. Mirror the slide direction too (enter from left, exit
       to right) so the motion still reads naturally. */
    body:has(.notes-pane.modal-right-docked) .toast {
      right: auto;
      left: 16px;
      transform: translateX(-120%);
    }
    body:has(.notes-pane.modal-right-docked) .toast.show { transform: translateX(0); }
    body:has(.notes-pane.modal-right-docked) .toast.exiting { transform: translateX(120%); }
    @media (max-width: 768px) {
      .toast {
        top: 12px;
        right: 12px;
        max-width: calc(100vw - 24px);
        /* Receive touches so the swipe-to-dismiss gesture works (desktop keeps
           pointer-events:none so the toast never blocks clicks). Horizontal pan
           only, so it doesn't fight page scroll. */
        pointer-events: auto;
        touch-action: pan-x;
      }
    }
    .stop-btn {
      position:absolute; top:2px; right:2px;
      background:var(--panel);
      color:var(--fg);
      border:1px solid var(--fg);
      font-family:inherit;
      font-size:1em; line-height:1; padding:2px 5px;
      cursor:pointer;
    }
    .small-note { font-size:12px; color:color-mix(in srgb, var(--fg) 60%, transparent); margin-top:4px; }
    .row-end { justify-content:flex-end; }
    .model-chat-btn {
      height:32px; padding:0 10px; margin-left:auto;
    }
    /* Nudge the "+ Chat" label down 1px to sit centered in the button. */
    .model-chat-btn-label {
      position: relative;
      top: 1px;
    }
    .openai-row {
      display:flex; align-items:center; gap:6px;
    }
    .models-row {
      display:flex; align-items:center; gap:6px; border:1px solid var(--border); padding:4px; margin:4px 0; border-radius: 4px;
    }
    .models-row .grow,
    .models-row select { 
      flex:1; 
      display:flex; 
      align-items:center; 
      font-size: 9.75px;
    }
    .model-fav-btn {
      width: 8px; height: 8px;
      border-radius: 50%;
      border: 1.5px solid color-mix(in srgb, var(--fg) 22%, transparent);
      flex-shrink: 0;
      cursor: pointer;
      transition: all 0.15s;
      position: relative;
      margin-left: 4px;
    }
    .model-fav-btn::before {
      content: '';
      position: absolute;
      top: -10px; left: -10px; right: -10px; bottom: -10px;
    }
    .model-fav-btn:hover {
      border-color: var(--fg);
      background: color-mix(in srgb, var(--fg) 27%, transparent);
      transform: scale(1.3);
    }
    .model-fav-btn.active {
      background: var(--fg);
      border-color: var(--fg);
    }
    .model-fav-btn.active:hover {
      opacity: 0.6;
    }
    .model-search-input {
      width: 100%;
      padding: 6px 10px;
      margin-bottom: 4px;
      border: 1px solid var(--border);
      border-radius: 6px;
      background: var(--bg);
      color: var(--fg);
      font-family: inherit;
      font-size: 0.8rem;
      outline: none;
      box-sizing: border-box;
      transition: border-color 0.15s;
    }
    .model-search-input:focus {
      border-color: var(--red);
    }
    .model-search-input::placeholder {
      color: color-mix(in srgb, var(--fg) 30%, transparent);
    }
    .models-row button {
      font-size: 9px;
      height: 24px;
      padding: 0 8px;
    }
    @media (max-width:768px){
      .box { max-height:none; }
      .chat-container { padding:10px; flex:1; margin-top:0; padding-top:42px; min-height:0; }
      .scroll-nav-btn { width:44px; height:44px; font-size:14px; margin-bottom:0; }
      .send-btn { width:48px; height:48px !important; border-radius:12px; }
      .send-btn svg { width:22px; height:22px; }
      #compare-toggle-btn { display:none !important; }
      .section[draggable] { -webkit-user-drag:none; }
      .drag-handle, .item-drag-handle, .folder-drag-handle { display:none !important; }
      /* Sidebar overlays chat on mobile */
      .sidebar {
        position: fixed !important;
        top: 0; bottom: 0; left: 0;
        z-index: 200;
        width: 80% !important;
        max-width: 340px;
        box-shadow: 4px 0 20px rgba(0,0,0,0.5);
        transition: transform 0.25s ease, opacity 0.25s ease !important;
        opacity: 1 !important;
        overflow: visible !important;
      }
      .sidebar.hidden {
        width: 80% !important;
        transform: translateX(-100%);
        pointer-events: none;
        overflow: hidden !important;
      }
      .sidebar.right-side.hidden {
        transform: translateX(100%);
      }
      .sidebar.right-side {
        left: auto; right: 0;
        box-shadow: -4px 0 20px rgba(0,0,0,0.5);
      }
      /* Backdrop behind sidebar */
      #sidebar-backdrop {
        position: fixed;
        inset: 0;
        z-index: 199;
        background: rgba(0,0,0,0.4);
        opacity: 0;
        pointer-events: none;
        transition: opacity 0.35s ease;
      }
      #sidebar-backdrop.visible { opacity: 1; pointer-events: auto; }
      /* Elastic overscroll on sidebar */
      .sidebar-inner {
        -webkit-overflow-scrolling: touch;
        overscroll-behavior-y: auto;
        overflow-y: scroll !important;
        padding-bottom: 40px;
      }
      .sidebar:not(.hidden) {
        overscroll-behavior: auto;
      }
      /* Sidebar header — room for hamburger */
      .sidebar-header {
        padding: 18px 12px 8px 12px;
        min-height: 44px;
      }
      .sidebar-brand-title {
        font-size: 1.2rem;
        left: 0 !important;
      }
      /* Sidebar inner — bigger spacing */
      .sidebar-inner {
        padding: 12px 10px 12px !important;
        gap: 4px !important;
      }
      /* Section headers — match list item sizing */
      .section-header-flex {
        padding: 12px 10px !important;
        height: 48px !important;
        border-radius: 8px;
        box-sizing: border-box;
      }
      .section-header-flex h4,
      .section-header-flex .section-title {
        font-size: 14px !important;
        gap: 11px !important;
      }
      .section-icon,
      .sidebar-action-icon {
        width: 16px !important;
        height: 16px !important;
        left: 0 !important;
      }
      /* List items — bigger touch targets, bigger text */
      .list-item {
        padding: 12px 10px !important;
        min-height: 48px;
        font-size: 14px;
        border-radius: 8px;
        gap: 10px !important;
      }
      .list-item .grow {
        font-size: 14px !important;
      }
      /* Search, New Chat & Assistant */
      #sidebar-search-btn,
      #sidebar-new-chat-btn,
      .sidebar-assistant-entry {
        padding: 12px 10px !important;
        min-height: 48px !important;
      }
      #sidebar-search-btn .grow,
      #sidebar-new-chat-btn .grow,
      .sidebar-assistant-entry .grow {
        font-size: 14px !important;
        left: 0 !important;
      }
      /* Section separator — more breathing room */
      .section {
        margin-top: 4px;
        border-radius: 8px;
      }
      /* Wave accent — slightly bigger on mobile */
      .section-header-flex h4::before {
        height: 18px;
      }
      /* Compact top bar on mobile — align with sidebar header */
      .chat-top-bar {
        padding: 1px 8px;
        min-height: 14px;
        margin-top: -31px;
        padding-top: 31px;
      }
      .chat-top-bar .chat-new-btn { display: none; }
      .chat-meta-overlay { font-size: 0.65em; max-width: 55%; overflow: visible; left: 50%; top: 50%; transform: translate(-50%, -50%); position: absolute; }
      .chat-meta-overlay .export-dl-btn { display: inline-flex; }
      /* Incognito — smaller on mobile */
      .incognito-btn {
        padding: 4px 10px;
        font-size: 10px;
      }
      /* Incognito indicator — right side next to hamburger on mobile */
      .incognito-indicator {
        position: fixed;
        top: 12px;
        left: auto !important;
        right: 48px !important;
        transform: none;
        width: 32px;
        height: 32px;
        z-index: 210;
        opacity: 0.8;
      }
      /* Icon rail on mobile — hidden by default, shown in mini-sidebar state */
      .icon-rail { display: none !important; }
      .icon-rail.mobile-mini {
        display: flex !important;
        position: fixed;
        top: 0; bottom: 0; left: 0;
        z-index: 200;
        width: 48px;
        box-shadow: 2px 0 12px rgba(0,0,0,0.4);
      }
      .icon-rail.mobile-mini.right-side {
        left: auto; right: 0;
        box-shadow: -2px 0 12px rgba(0,0,0,0.4);
      }
      /* Chat bubbles — AI stretches full width, user stays compact */
      .msg { font-size: 0.85em; }
      .msg .body { font-size: 0.9em; }
      .msg-user { max-width: 90% !important; margin-left: auto; margin-right: 4px; }
      .msg-ai, .agent-thread { width: 100% !important; max-width: 100% !important; margin: 8px 0; }
      /* Prevent inner scrollable elements from trapping vertical scroll */
      .msg pre,
      .agent-tool-output pre,
      .agent-thread-cmd,
      .msg details {
        overflow-y: hidden !important;
        max-height: none !important;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        touch-action: pan-y pan-x;
      }
      /* Models row — match list items */
      .models-row {
        padding: 12px 10px;
        min-height: 48px;
      }
      /* Input icon buttons — bigger touch targets */
      .input-icon-btn {
        padding: 10px;
        min-width: 44px;
        min-height: 44px;
      }
      /* Tool indicators — icon only on mobile (hide text, keep x) */
      .tool-indicator > span {
        display: none !important;
      }
      .tool-indicator .tool-indicator-x {
        display: inline-block !important;
        opacity: 0.6;
      }
      /* Hamburger — always right on mobile */
      .hamburger-btn {
        width: 44px;
        height: 44px;
        top: 6px;
        left: auto !important;
        right: 4px !important;
        -webkit-tap-highlight-color: transparent;
      }
      .hamburger-btn:hover,
      .hamburger-btn:active {
        background: none !important;
        border: none !important;
        box-shadow: none !important;
      }
      /* New chat — always left on mobile */
      .mobile-new-chat-btn {
        display: flex;
        position: fixed;
        top: 12px;
        left: 8px;
        z-index: 210;
        width: 32px;
        height: 32px;
        align-items: center;
        justify-content: center;
        background: none;
        border: none;
        color: var(--fg);
        cursor: pointer;
        opacity: 0.5;
        padding: 0;
      }
      /* Modal close button — bigger on mobile */
      .close-btn,
      .modal-close {
        min-width: 44px;
        min-height: 44px;
        width: 44px;
        height: 44px;
        font-size: 14px;
      }
      /* Code block buttons — slightly bigger touch targets */
      pre .copy-code,
      pre .edit-code,
      pre .run-code {
        width: 44px;
        height: 44px;
      }
      /* Touch-friendly targets for small buttons */
      .export-dl-btn {
        min-width: 44px;
        min-height: 44px;
      }
      .section-header-btn {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
      }
      /* 44×44 touch buttons starting from right:6 ends at 50. ~8px gap is
         enough to be distinguishable without spreading them too far apart. */
      pre .edit-code {
        right: 58px;
      }
      pre .run-code {
        right: 110px;
      }
      /* Dropdowns — bigger touch targets on mobile */
      .dropdown-item-compact {
        padding: 12px 12px !important;
        font-size: 14px !important;
        min-height: 44px;
        gap: 10px !important;
      }
      .dropdown-item-compact .dropdown-icon {
        width: 18px !important;
        height: 18px !important;
      }
      .dropdown-item-compact .dropdown-icon svg {
        width: 16px !important;
        height: 16px !important;
      }
      .dropdown,
      .session-dropdown {
        padding: 6px !important;
        border-radius: 12px !important;
      }
      /* Safe area padding for notched devices */
      .chat-input-bar {
        padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
      }
      /* Mode toggle — larger touch targets */
      .mode-toggle {
        height: 34px;
        border-radius: 12px;
      }
      .mode-toggle::before {
        border-radius: 11px;
      }
      .mode-toggle-btn {
        padding: 0 14px;
        font-size: 12px;
        min-height: 34px;
      }
      #mode-agent-btn { border-radius: 12px 0 0 12px; }
      #mode-chat-btn { border-radius: 0 12px 12px 0; }
      /* Diff mode — stack toolbar, bigger buttons */
      .diff-toolbar {
        padding: 8px 12px;
        gap: 6px;
        flex-wrap: wrap;
      }
      .diff-toolbar-btn {
        padding: 6px 12px;
        font-size: 12px;
        min-height: 36px;
      }
      .diff-chunk-btn {
        width: 28px;
        height: 28px;
        font-size: 14px;
      }
      /* Document/email/gallery/research library — full-height bottom-sheet
         on mobile. Keep the rounded top corners + border-top so they match
         the cookbook/calendar/compare look instead of looking like raw
         full-bleed panels. */
      .doclib-modal-content,
      .gallery-modal-content {
        width: 100vw !important;
        max-width: 100vw !important;
        /* vh fallback, dvh override so the modal adapts to mobile
           URL-bar show/hide. Order matters: later same-specificity rule
           wins, so dvh must come after vh. */
        max-height: 100vh !important;
        max-height: 100dvh !important;
        height: 100vh;
        height: 100dvh;
        border-radius: 14px 14px 0 0 !important;
        border: none !important;
        border-top: 1px solid var(--border) !important;
        box-shadow: none !important;
        padding: 6px !important;
        padding-bottom: env(safe-area-inset-bottom, 6px) !important;
        margin: 0 !important;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
      }
      #email-lib-modal .doclib-grid {
        max-height: none;
        flex: 1;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
      }
      /* Library modal layout: pin the header + tab strip, give the active
         panel (admin-card) the remaining height, and let the grid scroll
         internally. The load-more button sits below the grid as a sibling
         inside admin-card so it stays visible at the bottom of the panel
         without competing with the grid for scroll. */
      #doclib-modal .doclib-modal-content {
        display: flex !important;
        flex-direction: column !important;
        overflow: hidden !important;
      }
      #doclib-modal .modal-header,
      #doclib-modal .lib-tabs {
        flex: 0 0 auto !important;
      }
      #doclib-modal .modal-body {
        flex: 1 1 0 !important;
        min-height: 0 !important;
        overflow: hidden !important;
      }
      #doclib-modal .admin-card {
        flex: 1 1 0 !important;
        min-height: 0 !important;
      }
      /* :not(:has(.doclib-card-expanded)) scopes these to the normal list
         state — when a document card is expanded, the existing expand-state
         rules take over (grid claims flex:1 with overflow:hidden so the
         expanded card can scroll itself). */
      #doclib-modal .doclib-grid:not(:has(.doclib-card-expanded)) {
        flex: 1 1 0 !important;
        min-height: 0 !important;
        max-height: none !important;
        overflow-y: auto !important;
        -webkit-overflow-scrolling: touch;
      }
      #doclib-modal .doclib-load-more {
        margin: 8px auto 12px !important;
      }
      /* Squeeze a little more height for the preview content by tightening
         the spacing inside an expanded doc card on mobile. */
      #doclib-modal .doclib-card.doclib-card-expanded,
      #email-lib-modal .doclib-card.doclib-card-expanded,
      #memory-modal .doclib-card.doclib-card-expanded {
        gap: 2px !important;
        padding: 4px 6px !important;
        height: 100% !important;
      }
      /* Skills preview kept stopping at partial height because the flex
         chain (modal → tabs → panel → admin-card → grid → card) wouldn't
         reliably hand the card a full-height parent. But the skills LIST
         already scrolls correctly inside #skills-list, so that grid box
         already has the right bounded height (the area below the tabs).
         Anchor the expanded card to THAT box with position:absolute so it
         fills the list area exactly — header + tabs stay visible.

         NOTE: position:relative here is UNCONDITIONAL (not gated behind
         :has(.doclib-card-expanded)). Firefox mobile builds without :has()
         support never applied the gated rule, so the absolute card lost its
         anchor and only filled ~50% — while Chromium/desktop-narrow worked.
         Relative-when-collapsed is harmless (no abs children then). */
      #memory-modal #skills-list.doclib-grid {
        position: relative !important;
      }
      #memory-modal .doclib-card.skill-card.doclib-card-expanded {
        position: absolute !important;
        inset: 0 !important;
        /* Height is set in JS (skills.js _fillSkillCardHeight) as an explicit
           px value — Firefox does NOT treat inset:0 stretch OR height:100%
           (against the flex-sized grid) as a definite height, so grid/flex
           children never filled. An explicit px height is unambiguous. */
        margin: 0 !important;
        padding: 8px 10px calc(8px + env(safe-area-inset-bottom, 0px)) !important;
        background: var(--bg) !important;
        /* Flex column. JS (_fillSkillCardHeight) sets EXPLICIT px heights on
           the preview + <pre>; in a flex column an explicit-height item
           (flex:0 0 auto + height) is honoured. (Grid was worse here — the
           collapsed 1fr track overrode the preview's explicit height.) */
        display: flex !important;
        flex-direction: column !important;
        overflow: hidden !important;
        border: none !important;
        border-radius: 0 !important;
        box-sizing: border-box !important;
      }
      /* Preview is the 1fr grid track — give it a definite-height flex column
         so the <pre> (flex:1) fills and the footer pins at the bottom. */
      #memory-modal .doclib-card.skill-card.doclib-card-expanded > .doclib-card-preview {
        display: flex !important;
        flex-direction: column !important;
        min-height: 0 !important;
        overflow: hidden !important;
      }
      /* The card now fills the screen, but a base rule (.skill-card...
         .doclib-card-preview { flex: 0 1 auto }) sizes the preview to its
         CONTENT — so a medium SKILL.md left the preview at ~half the card
         (the "only 50%" the debug confirmed: card was full, content wasn't).
         Force the preview AND the <pre> to FILL the card. Extra .skill-card
         in the selector out-specifies both the base and the generic mobile
         rule so this wins without ambiguity. */
      #memory-modal .doclib-card.skill-card.doclib-card-expanded > .doclib-card-preview {
        flex: 1 1 auto !important;
        min-height: 0 !important;
      }
      #memory-modal .doclib-card.skill-card.doclib-card-expanded .skill-md-pre {
        flex: 1 1 auto !important;
        min-height: 0 !important;
      }
      /* Flatten the expanded doc/email card on mobile — drop the inner
         border + background so it doesn't read as a "subwindow" inside the
         modal (the chat preview doesn't have that nested-card look). The
         body prefix beats the desktop email rule later in the file that
         paints a 2px accent border + box-shadow with !important. */
      body #doclib-modal .doclib-card.doclib-card-expanded,
      body #email-lib-modal .doclib-card.doclib-card-expanded,
      body #memory-modal .doclib-card.doclib-card-expanded {
        background: transparent !important;
        border: none !important;
        border-radius: 0 !important;
        box-shadow: none !important;
      }
      /* Same layout pattern the chat preview uses: preview itself clips,
         the <pre> (or PDF iframe) inside owns the scroll, and the action
         bar pins to the bottom with extra bottom padding so it floats
         above the iOS safe-area / home-indicator strip. */
      #doclib-modal .doclib-card.doclib-card-expanded .doclib-card-preview,
      #email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview,
      #memory-modal .doclib-card.doclib-card-expanded .doclib-card-preview {
        padding: 4px 6px 20px !important;
        flex: 1 1 auto !important;
        min-height: 82dvh !important;
        overflow: hidden !important;
        display: flex !important;
        flex-direction: column !important;
        border-top: none !important;
      }
      /* The "Load more" button is flex-shrink:0 and sits in the panel AFTER the
         grid, so even when a card is expanded it reserves ~40px at the panel
         bottom — shrinking the visible grid and clipping the action bar by that
         much (the "black bar"). The global collapse rule's max-height:0 can't
         remove a flex-shrink:0 item, so hide it outright on expand. */
      #doclib-modal [data-doclib-panel="documents"]:has(.doclib-card-expanded) .doclib-load-more,
      #doclib-modal [data-doclib-panel="documents"]:has(.doclib-card-expanded) .doclib-inline-load-more {
        display: none !important;
      }
      /* Small bottom padding now that the load-more no longer steals space —
         the action bar sits low, just clearing the home-indicator safe area. */
      #doclib-modal [data-doclib-panel="documents"] .doclib-card.doclib-card-expanded .doclib-card-preview {
        padding-bottom: calc(18px + env(safe-area-inset-bottom, 0px)) !important;
      }
      #doclib-modal .doclib-card.doclib-card-expanded .doclib-card-preview pre,
      #doclib-modal .doclib-card.doclib-card-expanded .doclib-card-preview .doclib-card-pdf-frame,
      #email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview pre,
      #email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview .email-reader-body,
      #memory-modal .doclib-card.doclib-card-expanded .doclib-card-preview pre,
      #memory-modal .doclib-card.doclib-card-expanded .doclib-card-preview .skill-md-editor {
        flex: 1 1 auto !important;
        min-height: 0 !important;
        overflow-y: auto !important;
        -webkit-overflow-scrolling: touch;
        /* Strip the code-box visual treatment so the <pre> / reader body
           doesn't read as a "sub-window" inside the modal. */
        background: transparent !important;
        border: none !important;
        box-shadow: none !important;
        padding: 0 !important;
        margin: 0 !important;
        border-radius: 0 !important;
      }
      /* The skill editor textarea keeps a light frame on mobile (it's an
         input, not read-only text) — override the strip-down above. */
      #memory-modal .doclib-card.doclib-card-expanded .doclib-card-preview .skill-md-editor {
        background: var(--bg) !important;
        border: 1px solid var(--border) !important;
        padding: 8px !important;
      }
      #doclib-modal .doclib-card.doclib-card-expanded .doclib-card-preview pre code,
      #doclib-modal .doclib-card.doclib-card-expanded .doclib-card-preview code.hljs,
      #email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview pre code,
      #email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview code.hljs,
      #memory-modal .doclib-card.doclib-card-expanded .doclib-card-preview pre code {
        background: transparent !important;
        border: none !important;
        padding: 0 !important;
        box-shadow: none !important;
      }
      #doclib-modal .doclib-card.doclib-card-expanded .doclib-card-expanded-actions,
      #email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-expanded-actions,
      #memory-modal .doclib-card.doclib-card-expanded .doclib-card-expanded-actions {
        padding: 6px 4px 0 !important;
        margin-top: 4px !important;
        flex-shrink: 0 !important;
      }
      /* Email reader on mobile inherits the library modal's horizontal
         padding (the .doclib-modal-content default — 6px). No extra
         overrides for the modal-content / modal-body / admin-card / grid
         chain; we just clean up the inner email reader header/body
         padding so the From / To lines align with the email subject. */
      #email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview .email-reader-header,
      #email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview .email-reader-atts,
      #email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview .email-reader-body {
        padding-left: 6px !important;
        padding-right: 6px !important;
      }
      /* Mobile email-reader header: meta on the left, actions on the right
         (two stacked rows). The two .email-reader-actions-row siblings
         render as their own flex-row strips so primary (reply/reply-all/
         forward) sits above secondary (AI/summary/more), instead of
         flattening into one line that collided with the recipient chips. */
      #email-lib-modal .email-reader-header,
      .email-reader-tab-modal .email-reader-header,
      .email-window-modal .email-reader-header {
        padding: 8px 8px !important;
        gap: 6px !important;
        flex-direction: row !important;
        align-items: flex-start !important;
      }
      #email-lib-modal .email-reader-actions,
      .email-reader-tab-modal .email-reader-actions,
      .email-window-modal .email-reader-actions {
        display: flex !important;
        flex-direction: column !important;
        align-items: flex-end !important;
        gap: 4px !important;
        margin-left: auto !important;
        flex-shrink: 0 !important;
        position: relative !important;
        top: -3px !important; /* lift the reply/forward/etc. action buttons up on mobile */
      }
      #email-lib-modal .email-reader-actions-row,
      .email-reader-tab-modal .email-reader-actions-row,
      .email-window-modal .email-reader-actions-row {
        display: flex !important;
        flex-direction: row !important;
        flex-wrap: nowrap !important;
        align-items: center !important;
        justify-content: flex-end !important;
        gap: 4px !important;
      }
      #email-lib-modal .email-reader-actions .memory-toolbar-btn.reader-icon-btn,
      .email-reader-tab-modal .email-reader-actions .memory-toolbar-btn.reader-icon-btn,
      .email-window-modal .email-reader-actions .memory-toolbar-btn.reader-icon-btn {
        width: 44px !important;
        height: 44px !important;
        flex: 0 0 auto !important;
        display: inline-flex !important;
        flex-direction: column !important;
        align-items: center !important;
        justify-content: center !important;
        gap: 3px !important;
        padding: 4px 2px !important;
      }
      #email-lib-modal .email-reader-actions .memory-toolbar-btn.reader-icon-btn svg,
      .email-reader-tab-modal .email-reader-actions .memory-toolbar-btn.reader-icon-btn svg,
      .email-window-modal .email-reader-actions .memory-toolbar-btn.reader-icon-btn svg {
        width: 16px !important;
        height: 16px !important;
      }
      /* Gallery-style label under each button — only on mobile. The
         global rule below the @media block hides them on desktop. */
      #email-lib-modal .reader-btn-label,
      .email-reader-tab-modal .reader-btn-label,
      .email-window-modal .reader-btn-label {
        display: inline-block !important;
        font-size: 8.5px !important;
        font-weight: 500 !important;
        line-height: 1 !important;
        letter-spacing: 0.02em !important;
        opacity: 0.75 !important;
        white-space: nowrap !important;
      }
      /* List-view cards keep the framed look from their own .doclib-card /
         .memory-item base styles — no extra grid padding here, so the
         spacing matches the documents/library tab exactly. */
      /* Tighten the top of the email sheet — modal header + description +
         account row + toolbar were stacking with full desktop spacing and
         pushed the email subjects way down. */
      #email-lib-modal .modal-header {
        padding: 4px 8px !important;
        min-height: 0 !important;
      }
      #email-lib-modal .modal-header h4 {
        /* Match the other tool headers (base .modal-header h4 = 1rem); was
           13px, which read noticeably smaller than Calendar/Tasks/etc. */
        font-size: 1rem !important;
        line-height: 1.2 !important;
      }
      #email-lib-modal .modal-body {
        gap: 4px !important;
      }
      #email-lib-modal .admin-card {
        gap: 4px !important;
      }
      #email-lib-modal .admin-card > .doclib-desc {
        display: none !important;
      }
      /* When an email is expanded the list-mode toolbar siblings are
         already hidden by the existing :has rule. Hide the modal-header
         entirely AND zero out the modal-content top padding so the email
         reader claims the full sheet height. The swipe-down gesture (and
         the dock chip) still dismiss the modal. */
      #email-lib-modal:has(.doclib-card-expanded) .modal-header,
      #email-lib-modal.email-reading .modal-header {
        display: none !important;
      }
      #email-lib-modal:has(.doclib-card-expanded) .doclib-modal-content,
      #email-lib-modal.email-reading .doclib-modal-content {
        padding-top: 0 !important;
      }
      #email-lib-modal:has(.doclib-card-expanded) .modal-body,
      #email-lib-modal.email-reading .modal-body {
        gap: 0 !important;
      }
      /* Flatten the From / To bar so it doesn't read as a cut-off framed
         strip with a hard background change. Remove the background, drop
         the border-bottom to a faint divider, and let it blend with the
         sheet. */
      #email-lib-modal .email-reader-header {
        background: transparent !important;
        border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent) !important;
      }
      #email-lib-modal .email-card-nav-btn {
        padding: 6px 10px !important;
        min-width: 40px !important;
        height: 38px !important;
      }
      #email-lib-modal .email-card-nav-btn svg {
        width: 18px !important;
        height: 18px !important;
      }
      /* Nudge the prev/next arrow cluster ~4px to the right so it sits
         comfortably out at the edge instead of crowding the subject text. */
      #email-lib-modal .email-card-nav-arrows {
        transform: translateX(4px);
      }
      /* Done check — extend the actual TAP area via padding + negative
         margin (a transparent ::before doesn't change the parent's hit
         box). Layout stays the same height as library cards because the
         margin cancels the padding visually, but the clickable region is
         ~37px square. */
      #email-lib-modal .email-card-done {
        position: relative !important;
        z-index: 5 !important;
        pointer-events: auto !important;
        touch-action: manipulation !important;
        padding: 12px !important;
        margin: -12px !important;
      }
      #email-lib-modal .email-card-done svg {
        width: 13px !important;
        height: 13px !important;
      }
      /* Make sure no overlay or pseudo intercepts the tap. */
      #email-lib-modal .email-card-done * { pointer-events: none; }
      /* Tighten the From / To meta bar and the email body — they had
         desktop padding (10–14px) that wasted real estate on phones. */
      #email-lib-modal .email-reader-header {
        padding: 6px 4px !important;
        gap: 4px !important;
      }
      #email-lib-modal .email-reader-meta {
        font-size: 11px !important;
      }
      #email-lib-modal .email-reader-meta-row strong {
        min-width: 28px !important;
      }
      #email-lib-modal .email-reader-atts {
        padding: 6px 4px !important;
      }
      #email-lib-modal .email-reader-body {
        padding: 8px 4px !important;
      }
      /* Make sure the grid claims the full admin-card height when a doc
         is expanded — the existing rule sets flex:1, but on mobile we also
         need a hard height fallback because the parent uses dvh which
         desktop-tuned selectors don't always trickle through. */
      #doclib-modal .admin-card:has(.doclib-card-expanded) > .doclib-grid,
      #memory-modal .admin-card:has(.doclib-card-expanded) > .doclib-grid {
        height: 100% !important;
      }
      /* Skills modal: keep the header + tab strip visible; the expanded
         card fills the skills-list area below them via position:absolute
         (see the #skills-list rule above). Just make sure the tab-panel +
         its card give the list its full height. */
      #memory-modal .memory-tab-panel[data-memory-panel="skills"] > .admin-card:has(.doclib-card-expanded) {
        flex: 1 1 auto !important;
        min-height: 0 !important;
      }
      .doclib-card-header {
        padding: 10px 8px;
        gap: 4px;
      }
      .doclib-card-session,
      .doclib-card-time {
        display: none;
      }
      .doclib-card-expanded-actions {
        flex-wrap: wrap;
      }
      /* Keep the footer buttons identical to the chat/research footers on
         mobile too — those have no mobile enlargement, so neither should
         these (otherwise the doc footer reads in a larger/different font). */
      .doclib-card-action-btn {
        font-size: 10px;
        padding: 3px 8px;
      }
      /* Chat top bar — adjusted for reduced height */
      /* Suggestion nav — bigger touch targets */
      .doc-suggestion-nav-btn {
        padding: 6px 8px;
        font-size: 18px;
      }
      .doc-suggestion-close {
        padding: 10px 12px;
        margin: -10px -12px;
      }
    }
    #mobile-backdrop, #mobile-menu-btn { display:none !important; }
    #sidebar-backdrop { display:none !important; }
    /* ----- Loading spinner ----- */
    @keyframes spin {
      to { transform: rotate(360deg); }
    }
    .spinner {
      width: 24px;
      height: 24px;
      margin: 8px auto;
      border: 3px solid var(--border);
      border-top-color: var(--red);
      border-radius: 50%;
      animation: spin 0.9s linear infinite;
    }
    /* Inline spinner for buttons */
    .btn-spinner {
      display: inline-block;
      width: 12px;
      height: 12px;
      border: 2px solid transparent;
      border-top-color: currentColor;
      border-radius: 50%;
      animation: spin 1s linear infinite;
      margin-right: 6px;
    }
    .search-status {
      font-size: 0.85em;
      color: var(--red);
      margin-top: 4px;
      padding: 4px;
      border-left: 2px solid var(--red);
      background: color-mix(in srgb, var(--red) 5%, transparent);
    } 
    /* Loading indicator for messages */
    .loading-indicator {
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 10px;
    }
    .loading-dots {
      display: flex;
      gap: 4px;
    }
    .loading-dot {
      width: 6px;
      height: 6px;
      background-color: var(--red);
      border-radius: 50%;
    }
    .loading-dot:nth-child(1) {
      animation: loading-bounce 1.4s infinite ease-in-out both;
    }
    .loading-dot:nth-child(2) {
      animation: loading-bounce 1.4s infinite ease-in-out both;
      animation-delay: -0.32s;
    }
    .loading-dot:nth-child(3) {
      animation: loading-bounce 1.4s infinite ease-in-out both;
      animation-delay: -0.64s;
    }
    @keyframes loading-bounce {
      0%, 80%, 100% {
        transform: scale(0);
      }
      40% {
        transform: scale(1);
      }
    }
    /* Modal styling */
    .modal {
      position:fixed;
      top:0; left:0; width:100%; height:100%;
      background:none;
      display:flex; align-items:center; justify-content:center;
      z-index:250;
      backdrop-filter:none;
      pointer-events:none;
    }
    /* Cookbook always sits above Gallery so the "Serve a model in
       Cookbook…" flow (and any other "open Cookbook from inside another
       modal") is visible no matter which modal was opened first. */
    #cookbook-modal { z-index: 260; }
    .modal.hidden { display:none; }

    /* Tool windows open centered in the CHAT AREA (the space right of the
       sidebar + icon rail), rather than the full viewport.
       We narrow the overlay to the chat area so its flex-centering lands the
       window there; fullscreen / docked states use position:fixed so they
       escape this narrowed overlay and still fill the screen. The
       --sidebar-w / --icon-rail-w vars track collapse state live, so when a
       window (e.g. Cookbook) hides the sidebar this naturally re-centers.
       Desktop only — on mobile these are full-screen sheets. */
    @media (min-width: 769px) {
      #calendar-modal,
      #gallery-modal,
      #tasks-modal,
      #memory-modal,
      #doclib-modal,
      #compare-model-overlay,
      #research-overlay,
      #theme-modal,
      #settings-modal,
      #email-lib-modal {
        left: calc(var(--icon-rail-w, 48px) + var(--sidebar-w, 0px));
        width: calc(100% - (var(--icon-rail-w, 48px) + var(--sidebar-w, 0px)));
        box-sizing: border-box;
        /* Slide in sync with the sidebar's 0.25s collapse/expand so the
           centered window glides instead of jumping when the nav toggles. */
        transition: left 0.25s ease, width 0.25s ease;
      }
    }
    .modal-content {
      background:var(--panel);
      border:1px solid var(--border);
      width:min(520px, 92vw); max-height:85vh; padding:10px;
      box-sizing:border-box; font-size:14px;
      font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      letter-spacing: -0.015em;
      display:flex; flex-direction:column;
      position:relative;
      overflow-y:auto;
      border-radius:10px;
      box-shadow:0 8px 32px rgba(0,0,0,0.45);
      pointer-events:auto;
      animation: modal-enter 0.25s ease-out both;
    }
    .modal-header {
      display:flex; justify-content:space-between; align-items:center; margin-bottom:6px;
      cursor:grab; user-select:none;
      /* Pin the header (with its close button) to the top of the
         scrollable modal-content so users can always dismiss the modal
         even after scrolling far down — especially important on mobile
         where the modal can be taller than the viewport. */
      position: sticky;
      top: 0;
      z-index: 5;
      /* Inherit the modal-content's background so the header matches the
         panel body. Many tool modals set their content to var(--bg) inline
         while this header was hard-coded to var(--panel) — making the title
         strip a different shade in every theme. `inherit` tracks whatever
         the content uses (var(--bg) or the default var(--panel)) and stays
         opaque, so the sticky header still masks scrolled content. */
      background-color: inherit;
    }
    .modal-header:active { cursor:grabbing; }
    /* Edge/corner window resize (windowResize.js). While a resize is in
       progress, suppress text selection and force the active resize cursor
       across the whole document so it does not flicker as the pointer passes
       over child elements mid-drag. */
    body.window-resizing-active { user-select:none !important; }
    body.window-resizing-active * { cursor:inherit !important; }
    /* Suppress only TRANSITIONS while resizing so the edge tracks the cursor
       crisply. We deliberately do NOT toggle `animation` here: toggling
       animation off→on re-triggers the modal open-animation (a scale-in) on
       mouseup, which both mis-measures the final size and visibly "pops" the
       window. windowResize.js instead kills the one-shot open animation inline
       once, in begin(). */
    .window-resizing {
      transition:none !important;
    }
    /* Cookbook's modal-content is var(--bg) (inline) instead of the default
       var(--panel), so its sticky header — which defaults to var(--panel) —
       read as a different-coloured band. Match the header to the cookbook
       body so the panel is one uniform colour. */
    #cookbook-modal .modal-header {
      background: var(--bg);
      /* Cookbook opts out of the sticky header on mobile; the
         title bar scrolls away with the content instead of following. */
      position: static;
      top: auto;
    }
    .modal-header h4 {
      margin:0;
      /* Push every header control (opacity slider, minimize, close) into one
         group on the right — works whether or not optional controls like the
         opacity slider are present, so the minimize button never floats to
         the centre when a sibling is hidden. */
      margin-right:auto;
      font-size:1rem;
      font-weight:600;
      letter-spacing:-0.03em;
      color:var(--red);
    }
    .close-btn,
    .modal-close {
      background:var(--bg);
      color:var(--fg);
      border:1px solid var(--fg);
      font-size:12px;
      width:24px;
      height:24px;
      padding:0;
      display:inline-flex;
      align-items:center;
      justify-content:center;
      line-height:1;
      text-indent:0;
      cursor:pointer;
      border-radius:4px;
      line-height:1;
      flex-shrink:0;
    }
    .close-btn:hover,
    .modal-close:hover {
      background:var(--fg);
      color:var(--bg);
    }
    /* Minimize button — sits beside the close button on every modal */
    .minimize-btn {
      background:var(--bg);
      color:var(--fg);
      border:1px solid var(--fg);
      font-size:14px;
      font-weight:700;
      width:24px;
      height:24px;
      padding:0 0 6px 0; /* nudge the underscore visually toward the middle */
      display:inline-flex;
      align-items:center;
      justify-content:center;
      line-height:1;
      cursor:pointer;
      border-radius:4px;
      flex-shrink:0;
      margin-left:auto; /* push to the right so it docks next to .close-btn */
      margin-right:4px;
    }
    .minimize-btn:hover {
      background:var(--fg);
      color:var(--bg);
    }
    @media (max-width: 768px) {
      .minimize-btn { display: none !important; }
      #modal-dock { display: none !important; }
    }
    /* Minimized modals are hidden but stay in the DOM with their state intact */
    .modal.minimized { display:none !important; }
    /* Bottom dock for minimized modals */
    #modal-dock {
      position:fixed;
      bottom:var(--composer-clearance, 0px);
      left:0;
      right:0;
      display:flex;
      flex-wrap:wrap;
      gap:4px;
      padding:4px 8px;
      z-index:240;
      pointer-events:none;
      justify-content:flex-start;
    }
    #modal-dock:empty { display:none; }
    .modal-dock-item {
      background:var(--panel);
      border:1px solid var(--border);
      border-bottom:none;
      border-radius:6px 6px 0 0;
      padding:4px 4px 4px 10px;
      display:inline-flex;
      align-items:center;
      gap:6px;
      cursor:pointer;
      pointer-events:auto;
      font-size:12px;
      color:var(--fg);
      max-width:220px;
      box-shadow:0 -2px 8px rgba(0,0,0,0.25);
      transition:background 0.15s;
    }
    .modal-dock-item:hover { background:var(--bg); }
    .modal-dock-label {
      white-space:nowrap;
      overflow:hidden;
      text-overflow:ellipsis;
      max-width:180px;
    }
    .modal-dock-close {
      background:transparent;
      border:none;
      color:var(--fg);
      cursor:pointer;
      font-size:14px;
      padding:0 4px;
      line-height:1;
      opacity:0.6;
    }
    .modal-dock-close:hover { color:var(--red); opacity:1; }
    .modal-body { flex:1; overflow-y:auto; }
    .modal-body button { margin-top:6px; }
    /* Styled confirm dialog — keeps backdrop */
    #styled-confirm-overlay {
      background:rgba(0,0,0,0.5);
      backdrop-filter:blur(4px);
      pointer-events:auto !important;
      z-index: 99999 !important;
      position: fixed !important;
      top: 0 !important;
      left: 0 !important;
      width: 100% !important;
      height: 100% !important;
      top: 0; left: 0; width: 100%; height: 100%;
    }
    #styled-confirm-overlay .modal-content {
      position: relative;
      z-index: 10001;
    }
    .styled-confirm-box {
      width:360px; max-width:90vw;
      max-height:none; /* override modal-content's 85vh */
      padding:14px 18px;
    }
    .styled-confirm-box .modal-header { margin-bottom:4px; }
    .styled-confirm-box .modal-body p {
      margin:8px 0 12px; color:var(--fg); font-size:0.92rem; line-height:1.45;
      white-space:pre-line;
    }
    .styled-confirm-box .modal-footer {
      display:flex; justify-content:flex-end; gap:8px; padding-top:6px;
      border-top:1px solid var(--border); margin-top:4px;
    }
    @media (max-width:768px) {
      .styled-confirm-box {
        width: 85vw;
        padding: 12px 16px;
        font-size: 0.88rem;
        border-radius: 12px;
      }
      .styled-confirm-box .modal-header h4 { font-size: 0.9rem; }
      .styled-confirm-box .modal-body p { font-size: 0.85rem; margin: 6px 0 10px; }
      .styled-confirm-box .modal-footer { gap: 10px; }
      .styled-confirm-box .confirm-btn {
        flex: 1;
        /* More bottom than top padding nudges the label up ~2px so it isn't
           sitting low in the taller mobile buttons. */
        padding: 8px 12px 12px;
        font-size: 0.85rem;
        border-radius: 8px;
        text-align: center;
      }
    }
    .confirm-btn {
      /* Asymmetric padding nudges the label UP ~2px from where it was (more
         bottom than top padding), so the confirm-dialog text isn't sitting low. */
      padding:3px 16px 5px; border-radius:4px; font-size:0.85rem;
      cursor:pointer; border:1px solid var(--border);
      font-family:inherit;
    }
    @media (max-width: 820px) {
      /* Mobile: flip the asymmetry to shift the text 1 px UP from
         centre instead (the bigger touch targets on mobile read better
         with the label sitting slightly high). */
      .confirm-btn { padding:2px 16px 4px; }
    }
    .confirm-btn-secondary { background:var(--bg); color:var(--fg); }
    .confirm-btn-secondary:hover { background:var(--border); }
    .confirm-btn-primary { background:var(--accent-primary, var(--red, #4a9eff)); color:#fff; border-color:transparent; }
    .confirm-btn-primary:hover { filter:brightness(1.15); }
    .confirm-btn-danger { background:var(--color-danger); color:#fff; border-color:transparent; }
    .confirm-btn-danger:hover { background:var(--color-error); }
    /* Styled prompt — text-input dialog (used in place of window.prompt) */
    #styled-prompt-overlay {
      background:rgba(0,0,0,0.5);
      backdrop-filter:blur(4px);
      pointer-events:auto !important;
      z-index: 99999 !important;
      position: fixed !important;
      top: 0 !important;
      left: 0 !important;
      width: 100% !important;
      height: 100% !important;
    }
    #styled-prompt-overlay .modal-content {
      position: relative;
      z-index: 10001;
    }
    .styled-prompt-box { width: min(400px, 92vw); max-width: 100%; box-sizing: border-box; }
    .styled-prompt-box .modal-body { padding-top: 4px; }
    .styled-prompt-input {
      width:100%;
      box-sizing:border-box;
      margin-top:8px;
      padding:9px 12px;
      border:1px solid var(--border);
      border-radius:6px;
      background:var(--bg);
      color:var(--fg);
      font:inherit;
      font-size:0.95rem;
      outline:none;
      transition: border-color 0.15s, box-shadow 0.15s;
    }
    .styled-prompt-input:focus {
      border-color: var(--accent-primary, var(--red, #4a9eff));
      box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-primary, var(--red, #4a9eff)) 25%, transparent);
    }
    @media (max-width:768px) {
      .styled-prompt-box { width: 85vw; }
      .styled-prompt-input { font-size: 0.9rem; padding: 10px 12px; }
    }
    /* Scroll navigation buttons */
    .scroll-nav-btn {
      position:fixed;
      background:var(--panel);
      color: var(--accent);
      border:none;
      border-radius:10px;
      width:38px; height:38px;
      padding:0;
      display:flex; align-items:center; justify-content:center;
      font-size:14px;
      line-height:1;
      font-family:inherit;
      cursor:pointer;
      opacity:0;
      pointer-events:none;
      transition: opacity .2s, transform .3s cubic-bezier(0.25, 1, 0.5, 1), background .15s;
      z-index:100;
      transform: translateY(0);
    }
    .scroll-nav-btn::before {
      content: '';
      position: absolute;
      inset: 0;
      border-radius: 10px;
      padding: 1px;
      background: linear-gradient(to bottom, var(--border), color-mix(in srgb, var(--border) 30%, transparent));
      -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
      mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
      -webkit-mask-composite: xor;
      mask-composite: exclude;
      pointer-events: none;
    }
    #scroll-bottom-btn.show { opacity:1; pointer-events:auto; }
    @media (hover: hover) and (pointer: fine) {
      #scroll-bottom-btn.show:hover {
        border-color: color-mix(in srgb, var(--fg) 25%, transparent);
      }
    }
    #scroll-bottom-btn.slide-out {
      transform: translateY(20px);
      opacity: 0 !important;
      pointer-events: none;
    }
    /* Focus outline for accessibility */
    :focus-visible {
      outline: 2px solid var(--red);
      outline-offset: 2px;
    }
    /* Hamburger menu button */
    .hamburger {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 32px;
      height: 32px;
      background: none;
      border: 1px solid transparent;
      border-radius: 6px;
      padding: 0;
      cursor: pointer;
      transition: background 0.15s, border-color 0.15s;
    }
    .hamburger:hover {
      background: color-mix(in srgb, var(--fg) 7%, transparent);
      border-color: var(--border);
    }
    .hamburger span {
      display: block;
      width: 16px;
      height: 2px;
      background: var(--fg);
      border-radius: 1px;
      transition: transform 0.2s, opacity 0.2s;
    }
    .hamburger span + span { margin-top: 3px; }
    /* Agent indicator */
    #agent-indicator {
      position: fixed;
      top: 20px;
      right: 20px;
      background: var(--bg);
      color: var(--fg);
      border: 1px solid var(--border);
      padding: 6px 12px;
      border-radius: 6px;
      font-size: 12px;
      display: none;
      z-index: 100;
      cursor: pointer;
      transition: all 0.2s ease;
    }
    #agent-indicator.active {
      display: block;
      border-color: var(--color-agent-active);
      box-shadow: 0 0 10px rgba(0, 255, 0, 0.3);
    }
    #agent-indicator:hover {
      border-color: var(--color-agent-active);
      background: var(--panel);
    }
    #research-toggle-btn:disabled {
      opacity: 0.6;
      cursor: not-allowed;
    }
    
/* ── Drag & Drop ── */

/* ---------- Color palette (dark / light) ---------- */

    /* Drag and drop styling */
    .section[dnd-active="true"] {
      background: color-mix(in srgb, var(--red) 10%, transparent) !important;
      border-color: var(--red) !important;
    }
    
    .section[dnd-over="true"] {
      background: color-mix(in srgb, var(--red) 20%, transparent) !important;
      border-color: var(--red) !important;
      transform: scale(1.02);
    }
    
    .drag-handle {
      cursor: grab;
      opacity: 0.5;
      padding: 0 6px;
      user-select: none;
    }
    
    .drag-handle:hover {
      opacity: 0.8;
    }
    
    .drag-handle:active {
      cursor: grabbing;
    }
    
/* ── UI Controls (Radio, Presets, Toolbar, Settings) ── */


    /* Custom radio button styling */
    .radio-option {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 8px;
      border: 1px solid var(--border);
      border-radius: 6px;
      background: color-mix(in srgb, var(--fg) 6%, transparent);
      cursor: pointer;
      transition: all 0.2s ease;
    }
    
    .radio-option:hover {
      background: color-mix(in srgb, var(--fg) 9%, transparent);
      border-color: var(--fg);
    }
    
    .radio-option input[type="radio"] {
      appearance: none;
      -webkit-appearance: none;
      width: 18px;
      height: 18px;
      border: 2px solid var(--border);
      border-radius: 50%;
      outline: none;
      margin: 0;
      background: var(--bg);
      transition: all 0.2s ease;
      position: relative;
      flex-shrink: 0;
    }
    
    .radio-option input[type="radio"]:checked {
      border-color: var(--red);
      background: var(--red);
    }
    
    .radio-option input[type="radio"]:focus {
      box-shadow: 0 0 0 2px color-mix(in srgb, var(--red) 30%, transparent);
    }
    
    .radio-label {
      color: var(--fg);
      font-size: 14px;
      user-select: none;
    }
    
    /* Preset buttons */
    .preset-btn {
      height: 27.2px; /* 15% smaller than 32px */
      padding: 0 8.5px; /* 15% smaller than 10px */
      margin-left: 4px;
      border: 1px solid var(--border);
      border-radius: 4px;
      background: var(--bg);
      color: var(--fg);
      font-family: 'Fira Code', monospace;
      font-size: 10.2px; /* 15% smaller than 12px */
      cursor: pointer;
      transition: all 0.2s ease;
    }
    
    .preset-btn:hover {
      background: var(--panel);
      border-color: var(--fg);
    }
    
    .preset-btn.active {
      background: var(--panel);
      border-color: var(--fg);
      box-shadow: 0 0 0 1px var(--fg), 0 0 8px color-mix(in srgb, var(--fg) 16%, transparent);
      font-weight: 600;
    }
    
    /* All preset buttons use the same blue color */
    .preset-btn {
      border-color: var(--red); /* Blue color */
    }
    
    .preset-btn.active {
      border-color: var(--red); /* Blue color when active */
      box-shadow: 0 0 0 1px var(--red), 0 0 8px color-mix(in srgb, var(--red) 30%, transparent);
    }
    
    /* Custom preset modal: the base .preset-modal-content sets overflow:hidden
       (for desktop rounded-corner clipping), which on the mobile sheet clipped
       the footer (Start/Cancel) off the bottom with no way to reach it. Let the
       whole sheet scroll — same as every other mobile modal (.modal-content is
       overflow-y:auto on mobile) — so the footer is always reachable. No flex
       changes, so the body can't collapse. */
    #custom-preset-modal .preset-modal-content {
      overflow-y: auto !important;
    }
    #custom-preset-modal .modal-body label {
      font-size: 13px;
      font-weight: 500;
      color: var(--fg);
      margin-top: 8px;
      margin-bottom: 4px;
      display: block;
    }

    #custom-preset-modal .modal-body input,
    #custom-preset-modal .modal-body textarea {
      width: 100%;
      margin-bottom: 8px;
      box-sizing: border-box;
    }
    #custom-preset-modal .modal-body textarea,
    #custom-preset-modal .modal-body input[type="text"] {
      background: var(--panel);
      border: 1px solid var(--border);
      border-radius: 6px;
      color: var(--fg);
      padding: 8px 10px;
      font-size: 13px;
      font-family: inherit;
      transition: border-color 0.15s;
    }
    #custom-preset-modal .modal-body textarea {
      resize: vertical;
    }
    #custom-preset-modal .modal-body textarea:focus,
    #custom-preset-modal .modal-body input[type="text"]:focus {
      outline: none;
      border-color: var(--red);
    }

    #custom-preset-modal .modal-footer {
      display: flex;
      justify-content: flex-end;
      gap: 8px;
      margin-top: 12px;
      padding-top: 10px;
      border-top: 1px solid var(--border);
    }

    #custom-preset-modal .modal-footer button {
      padding: 7px 14px;
      border-radius: 6px;
      font-size: 12px;
      font-weight: 500;
      border: 1px solid var(--border);
      background: none;
      color: var(--fg);
      cursor: pointer;
      transition: all 0.15s;
    }
    #custom-preset-modal .modal-footer button:hover {
      background: color-mix(in srgb, var(--fg) 8%, transparent);
    }

    #custom-preset-modal .modal-footer button#save-custom-preset {
      /* The theme's accent is stored in --red (theme.js sets --red = accentHex)
         and --accent is undefined, so the canonical accent expression is
         var(--accent, var(--red)). The old bare var(--accent) resolved to nothing
         which, with color:var(--bg), made the button invisible. */
      background: var(--accent, var(--red));
      color: var(--bg);
      border-color: var(--accent, var(--red));
    }
    #custom-preset-modal .modal-footer button#save-custom-preset:hover {
      opacity: 0.85;
    }
    /* Toolbar visibility tab */
    .toolbar-hint {
      font-size: 12px;
      color: color-mix(in srgb, var(--fg) 55%, transparent);
      margin-bottom: 12px;
    }
    /* ── Appearance visibility toggles ── */
    .vis-toggles {
      display: flex;
      flex-direction: column;
    }
    .vis-row {
      display: flex;
      align-items: center;
      gap: 8px;
      cursor: pointer;
      padding: 5px 6px;
      border-radius: 4px;
      transition: background 0.12s;
    }
    .vis-row:hover {
      background: color-mix(in srgb, var(--fg) 5%, transparent);
    }
    .vis-row input[type="checkbox"] {
      display: none;
    }
    .vis-icon {
      width: 20px;
      height: 20px;
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
      color: color-mix(in srgb, var(--fg) 40%, transparent);
    }
    .vis-icon-text {
      font-size: 10px;
      font-weight: 700;
      font-family: inherit;
      letter-spacing: -0.5px;
    }
    .vis-label {
      flex: 1;
      font-size: 12px;
      color: var(--fg);
      user-select: none;
    }
    .vis-switch {
      position: relative;
      width: 30px;
      height: 16px;
      background: color-mix(in srgb, var(--fg) 15%, transparent);
      border-radius: 8px;
      transition: background 0.2s;
      flex-shrink: 0;
    }
    .vis-switch::after {
      content: '';
      position: absolute;
      top: 2px;
      left: 2px;
      width: 12px;
      height: 12px;
      background: var(--panel);
      border-radius: 50%;
      transition: transform 0.2s;
      box-shadow: 0 1px 2px rgba(0,0,0,0.25);
    }
    .vis-row input:checked + .vis-switch {
      background: var(--red);
    }
    .vis-row input:checked + .vis-switch::after {
      transform: translateX(14px);
    }
    .vis-row input:checked ~ .vis-icon,
    .vis-row:has(input:checked) .vis-icon {
      color: var(--fg);
    }
    /* Compare model selector — match the calendar modal's clean header
       (no border underline). */
    #compare-model-overlay .modal-header h4 {
      pointer-events: none;
    }
    /* Compare model selector: keep manually-resized/tiny windows contained.
       Picker dropdowns are appended to document.body, so the card itself can
       clip and scroll without cropping the dropdown list. */
    #compare-model-overlay .modal-content {
      display: flex;
      flex-direction: column;
      max-height: min(720px, calc(100dvh - 48px));
      overflow: hidden;
      min-height: 180px;
    }
    #compare-model-overlay .modal-body {
      overflow: auto;
      flex: 1 1 auto;
      min-height: 0;
    }
    .vis-hint {
      font-size: 10px;
      color: color-mix(in srgb, var(--fg) 30%, transparent);
      font-weight: 400;
      margin-left: 2px;
    }
    /* Settings toggle — admin-only lock indicator */
    .ui-vis-lock {
      display: none;
    }
    /* (legacy toolbar-toggle styles removed — now using .vis-* classes) */

    /* Demo highlight pulse */
    .odysseus-highlight {
      outline: 2px solid var(--accent, var(--red)) !important;
      outline-offset: 1px;
      box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
      animation: ody-pulse 1.5s ease-in-out infinite;
      z-index: 100;
      position: relative;
    }
    @keyframes ody-pulse {
      0%, 100% { outline-color: var(--accent, var(--red)); box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent, var(--red)) 18%, transparent); }
      50%       { outline-color: color-mix(in srgb, var(--accent, var(--red)) 40%, transparent); box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent, var(--red)) 8%, transparent); }
    }
    /* Floating breathing halo. Rendered as a body-level div positioned over
       the target, so we don't fight the target's own outline / box-shadow /
       overflow chain. JS keeps it in sync with the target's bounding rect. */
    .tour-halo {
      position: fixed;
      pointer-events: none;
      border: 3px solid var(--accent, var(--red));
      border-radius: 10px;
      z-index: 10000;
      animation: ody-breathe 1.4s ease-in-out infinite;
      opacity: 0;
      transform: scale(0.94);
      transition: opacity 0.35s ease-out, transform 0.35s ease-out;
    }
    .tour-halo.tour-fade-in {
      opacity: 1;
      transform: scale(1);
    }
    @keyframes ody-breathe {
      0%, 100% {
        box-shadow: 0 0 0 3px  color-mix(in srgb, var(--accent, var(--red)) 55%, transparent),
                    0 0 22px 4px color-mix(in srgb, var(--accent, var(--red)) 40%, transparent);
        border-color: var(--accent, var(--red));
      }
      50% {
        box-shadow: 0 0 0 6px  color-mix(in srgb, var(--accent, var(--red)) 30%, transparent),
                    0 0 40px 14px color-mix(in srgb, var(--accent, var(--red)) 70%, transparent);
        border-color: color-mix(in srgb, var(--accent, var(--red)) 80%, transparent);
      }
    }
    /* While the tour is active, lift overflow:hidden on common clipping
       ancestors so the halo around a highlighted child isn't cropped. */
    body.tour-active .sidebar,
    body.tour-active .sidebar-inner,
    body.tour-active .chat-input-bar,
    body.tour-active .chat-input-top,
    body.tour-active .chat-input-wrap,
    body.tour-active .chat-input-right,
    body.tour-active .mode-toggle,
    body.tour-active .model-picker-wrap {
      overflow: visible !important;
    }

    /* ── Secret tour hint (drag-to-snap on first modal open) ── */
    .tour-hint {
      position: fixed;
      z-index: 10002;
      background: var(--bg);
      color: var(--fg);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 12px 14px 10px;
      width: 240px;
      font-size: 0.78rem;
      line-height: 1.5;
      box-shadow: 0 6px 20px rgba(0, 0, 0, 0.32);
      opacity: 0;
      transform: translateY(-4px);
      transition: opacity 0.28s ease-out, transform 0.28s ease-out;
      pointer-events: auto;
    }
    .tour-hint.tour-hint-in  { opacity: 1; transform: translateY(0); }
    .tour-hint.tour-hint-out { opacity: 0; transform: translateY(-4px); }
    .tour-hint-visual {
      display: flex;
      justify-content: center;
      margin-bottom: 8px;
      color: var(--accent, var(--red));
    }
    .tour-hint-visual svg { display: block; }
    .tour-hint-text { margin-bottom: 10px; opacity: 0.92; }
    .tour-hint-text b { color: var(--accent, var(--red)); font-weight: 600; }
    .tour-hint-dismiss {
      display: block;
      margin: 0 0 0 auto;
      background: none;
      border: 1px solid var(--border);
      color: var(--fg);
      border-radius: 6px;
      padding: 3px 12px;
      cursor: pointer;
      font-family: inherit;
      font-size: 0.72rem;
      opacity: 0.85;
      transition: opacity 0.15s, background 0.15s;
    }
    .tour-hint-dismiss:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 8%, transparent); }

    /* SVG dance: cursor approaches title bar, drags right, modal snaps to a
       right-half zone, holds, returns. 3.2s loop. */
    .th-cursor { animation: th-cursor 3.2s ease-in-out infinite; transform-origin: 0 0; }
    .th-modal-group { animation: th-modal-slide 3.2s ease-in-out infinite; transform-origin: 39px 31px; }
    .th-zone { animation: th-zone-flash 3.2s ease-in-out infinite; }
    @keyframes th-cursor {
      0%, 5%    { transform: translate(35px, 32px); }
      18%       { transform: translate(35px, 22px); }
      48%       { transform: translate(80px, 22px); }
      72%       { transform: translate(80px, 22px); }
      90%, 100% { transform: translate(35px, 32px); }
    }
    @keyframes th-modal-slide {
      0%, 18%   { transform: translate(0, 0) scale(1, 1); }
      48%       { transform: translate(45px, 0) scale(1, 1); }
      58%       { transform: translate(30px, -19px) scale(1.4, 2.4); }
      72%       { transform: translate(30px, -19px) scale(1.4, 2.4); }
      90%, 100% { transform: translate(0, 0) scale(1, 1); }
    }
    @keyframes th-zone-flash {
      0%, 38%   { opacity: 0; }
      48%       { opacity: 0.22; }
      58%, 72%  { opacity: 0; }
      100%      { opacity: 0; }
    }
    @media (prefers-reduced-motion: reduce) {
      .th-cursor, .th-modal-group, .th-zone { animation: none !important; }
    }
    .odysseus-hl-label {
      position: absolute;
      top: -22px;
      left: 8px;
      background: var(--red);
      color: var(--bg);
      font-size: 0.7rem;
      padding: 2px 8px;
      border-radius: 4px;
      white-space: nowrap;
      z-index: 101;
      pointer-events: none;
    }

    /* Generated images inside chat bubbles */
    .msg.generated-image-wrap .body { text-align: center; }
    .generated-image {
      max-width: 100%;
      max-height: 512px;
      border-radius: 8px;
      cursor: pointer;
      transition: transform 0.2s;
      display: inline-block;
      margin: 0 auto;
    }
    .generated-image:hover { transform: scale(1.02); }
    .generated-image-caption {
      font-size: 0.8rem;
      opacity: 0.5;
      margin-top: 6px;
      font-style: italic;
      text-align: center;
    }

    /* Setup wizard */
    .setup-wizard { padding: 16px; max-width: 500px; }
    .setup-wizard .setup-title { font-size: 1.1rem; font-weight: 600; margin-bottom: 12px; color: var(--fg); }
    .setup-wizard .setup-label { font-size: 0.85rem; color: var(--fg); opacity: 0.7; margin-bottom: 8px; }
    .setup-wizard .setup-presets { display: flex; flex-wrap: wrap; gap: 8px; }
    .setup-wizard .setup-preset-btn {
      padding: 8px 14px; border: 1px solid var(--border); border-radius: 6px;
      background: var(--panel); color: var(--fg); cursor: pointer;
      font-size: 0.85rem; transition: all 0.2s;
    }
    .setup-wizard .setup-preset-btn:hover { border-color: var(--red); background: color-mix(in srgb, var(--red) 11%, transparent); }
    .setup-wizard .setup-input {
      display: block; width: 100%; padding: 8px 12px; margin-bottom: 8px;
      background: var(--panel); border: 1px solid var(--border);
      border-radius: 6px; color: var(--fg); font-size: 0.85rem; box-sizing: border-box;
    }
    .setup-wizard .setup-input:focus { border-color: var(--red); outline: none; }
    .setup-wizard .setup-connect-btn {
      padding: 8px 20px; background: var(--red); color: var(--bg);
      border: none; border-radius: 6px; cursor: pointer; font-weight: 600; margin-top: 4px;
    }
    .setup-wizard .setup-connect-btn:hover { opacity: 0.9; }
    .setup-wizard .setup-status { font-size: 0.8rem; margin-top: 8px; color: var(--fg); opacity: 0.7; }
    .setup-wizard .setup-model-list { display: flex; flex-wrap: wrap; gap: 8px; }
    .setup-wizard .setup-model-btn {
      padding: 8px 14px; border: 1px solid var(--border); border-radius: 6px;
      background: var(--panel); color: var(--fg); cursor: pointer;
      font-size: 0.85rem; transition: all 0.2s;
    }
    .setup-wizard .setup-model-btn:hover { border-color: var(--red); background: color-mix(in srgb, var(--red) 11%, transparent); }
    .setup-wizard .setup-step.hidden { display: none; }

    /* Dropdown menu styles */
    .dropdown {
      position: absolute;
      top: calc(100% + 6px);
      right: 0;
      background: var(--panel);
      border: 1px solid var(--border);
      border-radius: 10px;
      box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3), 0 0 0 1px color-mix(in srgb, var(--fg) 5%, transparent);
      z-index: 1000;
      display: none;
      min-width: 220px;
      padding: 6px;
      backdrop-filter: blur(12px);
    }

    .dropdown.show {
      display: block;
      animation: dropdown-in 0.15s ease-out;
    }
    @keyframes dropdown-in {
      from { opacity:0; transform:translateY(-6px) scale(0.97); }
      to { opacity:1; transform:translateY(0) scale(1); }
    }

    .dropdown-item {
      padding: 8px 10px;
      border-radius: 6px;
      cursor: pointer;
      transition: background 0.12s ease;
      display: flex;
      align-items: center;
      gap: 10px;
      border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent);
    }

    .dropdown-item:last-child {
      border-bottom: none;
    }

    .dropdown-item .menu-icon {
      width: 28px;
      height: 28px;
      border-radius: 6px;
      background: color-mix(in srgb, var(--fg) 5%, transparent);
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 14px;
      flex-shrink: 0;
    }

    .dropdown-item .menu-text h4 {
      margin: 0;
      font-size: 12.5px;
      font-weight: 500;
      color: var(--fg);
      line-height: 1.3;
    }

    .dropdown-item .menu-text p {
      margin: 0;
      font-size: 10.5px;
      color: var(--color-subheader);
      line-height: 1.3;
    }

    .dropdown-item:hover {
      background: color-mix(in srgb, var(--red) 10%, transparent);
    }
    .dropdown-item:hover .menu-icon {
      background: color-mix(in srgb, var(--red) 15%, transparent);
    }

    /* Compact dropdown items (session/model context menus) */
    .dropdown-item-compact {
      cursor: pointer;
      padding: 6px 8px;
      font-size: 11px;
      border-radius: 8px;
      display: flex;
      align-items: center;
      gap: 10px;
      color: var(--fg);
      transition: background 0.1s;
    }
    .dropdown-item-compact:hover {
      background: color-mix(in srgb, var(--accent) 10%, transparent);
    }
    .dropdown-item-compact .dropdown-icon {
      width: 14px;
      height: 14px;
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
      opacity: 0.5;
    }
    .dropdown-item-compact .dropdown-icon svg {
      width: 14px;
      height: 14px;
    }
    .dropdown-item-compact .dropdown-shortcut {
      margin-left: auto;
      font-size: 9.5px;
      opacity: 0.35;
      font-weight: 400;
    }
    /* Keyboard shortcut hints (⌘+Alt+D etc.) are meaningless on touch —
       hide them in the per-chat actions menu on mobile. */
    @media (max-width: 768px) {
      .session-dropdown .dropdown-shortcut { display: none; }
    }
    .dropdown-item-danger {
      color: var(--red) !important;
    }
    .dropdown-item-danger .dropdown-icon {
      opacity: 0.7;
    }

    /* Inline rename input for sessions */
    .session-rename-input {
      width: 100%;
      background: var(--bg);
      color: var(--fg);
      border: 1px solid var(--accent, var(--accent-primary));
      border-radius: 4px;
      padding: 2px 6px;
      font-family: inherit;
      font-size: 12px;
      line-height: 1.3;
      outline: none;
    }

    /* Session dropdown container */
    .session-dropdown-menu {
      position: fixed;
      z-index: 1000;
      display: none;
      min-width: auto;
      width: max-content;
      padding: 4px;
      animation: dropdown-in 0.15s ease-out;
    }

    /* Folder move submenu */
    .session-folder-submenu {
      position: fixed;
      z-index: 1001;
      display: none;
      min-width: auto;
      width: max-content;
      padding: 4px;
    }

    .dropdown-divider {
      height: 1px;
      background: var(--border);
      margin: 4px 8px;
    }
    
    /* Search toggle styles */
    .search-toggle {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 6px 0;
      border-bottom: 1px solid var(--border);
      margin-bottom: 8px;
    }
    
    .search-provider-label {
      font-size: 14px;
      color: var(--fg);
    }

/* ── Voice, Search, Themes, Comparison, Censor, Print ── */

    /* Voice recording styles */
    #mic-btn {
      color: var(--fg);
      border: 1px solid var(--border);
      border-radius: 4px;
      transition: all 0.2s ease;
    }
    
    #mic-btn:hover {
      background: color-mix(in srgb, var(--fg) 6%, transparent);
      border-color: var(--fg);
    }
    
    #mic-btn.recording {
      background: var(--color-recording);
      border-color: var(--color-recording);
      animation: pulse 1.5s infinite;
    }
    
    @keyframes pulse {
      0% { opacity: 1; }
      50% { opacity: 0.7; }
      100% { opacity: 1; }
    }
    
    #recording-indicator {
      position: fixed;
      top: 10px;
      left: 0;
      right: 0;
      background: rgba(0, 0, 0, 0.8);
      backdrop-filter: blur(10px);
      border: 1px solid var(--border);
      border-radius: 8px;
      padding: 12px;
      margin: 10px;
      z-index: 1000;
      display: flex;
      align-items: center;
      justify-content: space-between;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    }
    
    #recording-indicator.hidden {
      display: none !important;
    }
    
    .recording-content {
      display: flex;
      align-items: center;
      gap: 12px;
      color: white;
    }
    
    .recording-icon {
      color: var(--color-recording);
      font-size: 20px;
      animation: pulse 1.5s infinite;
    }
    
    .recording-text {
      font-size: 16px;
      font-weight: 500;
    }
    
    #stop-recording {
      background: var(--color-recording);
      color: white;
      border: none;
      border-radius: 6px;
      padding: 6px 12px;
      font-size: 14px;
      font-weight: 500;
      cursor: pointer;
      transition: background 0.2s ease;
    }
    
    #stop-recording:hover {
      background: var(--color-recording-hover);
    }
    
    /* Error state for recording */
    #recording-indicator.error {
      background: rgba(173, 26, 26, 0.9);
    }
    
    .recording-error {
      color: var(--color-recording);
      font-size: 14px;
      margin-top: 4px;
    }
    
    @media (max-width: 768px) {
      #recording-indicator {
        margin: 8px;
        padding: 10px;
      }
      
      .recording-text {
        font-size: 14px;
      }
      
      #stop-recording {
        padding: 6px 10px;
        font-size: 12px;
      }
    }
/* SYNTAX HIGHLIGHTING — uses theme vars from style.css, no hardcoded overrides */
.hljs { color: var(--hl-fg, #9cdef2); background: none !important; }
pre { background: var(--code-bg, var(--hl-bg, #282c34)) !important; }

    /* ---------- Search overlay (Ctrl+K command palette) ---------- */
    .search-overlay {
      position: fixed;
      top: 0; left: 0; width: 100%; height: 100%;
      background: rgba(0, 0, 0, 0.6);
      display: flex;
      align-items: flex-start;
      justify-content: center;
      padding-top: 15vh;
      z-index: 300;
      backdrop-filter: blur(6px);
    }
    .search-overlay.hidden { display: none; }
    .search-popup {
      width: 520px;
      max-width: 90vw;
      background: var(--bg);
      border: 1px solid var(--border);
      border-radius: 12px;
      box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5), 0 0 0 1px color-mix(in srgb, var(--fg) 6%, transparent);
      overflow: hidden;
      display: flex;
      flex-direction: column;
      max-height: 60vh;
    }
    .search-popup input#search-input {
      width: 100%;
      background: transparent;
      border: none;
      border-bottom: 1px solid var(--border);
      outline: none;
      color: var(--fg);
      font-size: 16px;
      font-family: 'Fira Code', monospace;
      padding: 14px 16px;
      box-sizing: border-box;
    }
    .search-popup input#search-input::placeholder {
      color: color-mix(in srgb, var(--fg) 30%, transparent);
    }
    .search-results {
      overflow-y: auto;
      flex: 1;
      padding: 4px;
    }
    .search-results:empty {
      display: none;
    }
    .search-group-header {
      font-size: 10px;
      font-weight: 600;
      color: var(--color-subheader);
      text-transform: uppercase;
      letter-spacing: 0.5px;
      padding: 10px 12px 4px;
    }
    .search-result-item {
      display: flex;
      align-items: baseline;
      gap: 8px;
      padding: 8px 12px;
      border-radius: 6px;
      cursor: pointer;
      transition: background 0.1s;
    }
    .search-result-item:hover,
    .search-result-item.selected {
      background: color-mix(in srgb, var(--red) 10%, transparent);
    }
    .search-result-role {
      font-size: 10px;
      font-weight: 600;
      color: var(--color-subheader);
      flex-shrink: 0;
      min-width: 24px;
    }
    .search-result-snippet {
      flex: 1;
      font-size: 12px;
      color: var(--fg);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .search-result-time {
      font-size: 10px;
      color: color-mix(in srgb, var(--fg) 35%, transparent);
      flex-shrink: 0;
      white-space: nowrap;
    }
    mark.search-highlight {
      /* Was orange-on-orange (0.35 alpha bg + orange text) — too low-contrast
         to read. Solid accent fill with bg-colored text reads clearly. */
      background: var(--accent, #e8a830);
      color: var(--bg, #1a1a1a);
      border-radius: 2px;
      padding: 0 2px;
      font-weight: 600;
    }
    /* Document library search-term highlight — clear, high-contrast. */
    mark.doclib-search-hl {
      background: var(--accent, #e8a830);
      color: var(--bg, #1a1a1a);
      border-radius: 2px;
      padding: 0 2px;
      font-weight: 600;
    }
    .search-empty {
      text-align: center;
      color: color-mix(in srgb, var(--fg) 35%, transparent);
      padding: 24px;
      font-size: 13px;
    }

    /* ---------- Theme popup (uses .modal > .modal-content frame) ---------- */
    #theme-modal { z-index: 260; }
    #theme-popup .theme-popup-sub { color: color-mix(in srgb, var(--fg) 50%, transparent); font-size: 11px; margin-bottom: 10px; }
    /* `max-height` instead of a fixed height so the popup shrinks to fit its
       content (avoids the leftover whitespace after the time-based switching
       card was removed). The inner .theme-tab-panel keeps overflow:auto, so
       any taller content still scrolls inside. */
    #theme-popup { overflow-y: hidden; max-height: min(85vh, 600px); }
    #theme-popup .modal-header { flex-shrink: 0; }
    #theme-popup .admin-tabs { margin: 0 -10px 8px; padding: 0 10px; flex-shrink: 0; }
    .theme-tab-panel { overflow-y: auto; min-height: 0; flex: 1; padding-bottom: 10px; }

    .theme-grid {
      display: grid; grid-template-columns: repeat(auto-fill, minmax(66px, 1fr));
      gap: 6px; margin-bottom: 12px;
    }
    .theme-swatch {
      border: 2px solid var(--border); border-radius: 8px; cursor: pointer;
      padding: 5px; text-align: center; font-size: 0.65rem; color: var(--fg);
      transition: border-color 0.15s, transform 0.15s;
    }
    .theme-swatch:hover { transform: scale(1.06); }
    .theme-swatch.active { border-color: var(--red); box-shadow: 0 0 0 2px color-mix(in srgb, var(--red) 33%, transparent); }
    .theme-swatch-colors {
      display: flex; justify-content: center; margin-bottom: 3px;
    }
    .theme-swatch-colors span {
      width: 15px; height: 15px; border-radius: 50%;
      margin-left: -5px;
      border: 1.5px solid color-mix(in srgb, var(--fg) 12%, transparent);
    }
    .theme-swatch-colors span:first-child { margin-left: 0; }
    .theme-custom-label { font-size: 11px; font-weight: 600; color: color-mix(in srgb, var(--fg) 50%, transparent); text-transform: uppercase; letter-spacing: 0.06em; margin: 10px 0 6px; }
    .theme-custom { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 12px; }
    .color-row { display: flex; align-items: center; gap: 4px; }
    .color-row label { font-size: 13px; font-weight: 500; color: var(--fg); opacity: 0.7; flex: 1; }
    .color-row input[type="color"],
    .color-row input.cp-swatch-input {
      width: 24px; height: 24px; border: 1px solid var(--border); border-radius: 50%;
      background: none; cursor: pointer; padding: 0; flex-shrink: 0;
      overflow: hidden;
      -webkit-appearance: none;
      appearance: none;
    }
    .color-row input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
    .color-row input[type="color"]::-webkit-color-swatch { border: none; border-radius: 50%; }
    .color-row input[type="color"]::-moz-color-swatch { border: none; border-radius: 50%; }
    .color-row input.cp-swatch-input {
      color: transparent;
      text-shadow: none;
      caret-color: transparent;
      font-size: 0;
      user-select: none;
    }
    .color-row input.cp-swatch-input::selection { background: transparent; }
    .color-row input.cp-swatch-input:focus { outline: 1px solid var(--red); outline-offset: 1px; }
    .color-reset-btn {
      width: 24px; height: 24px; border: none; background: none; cursor: pointer;
      color: var(--fg); opacity: 0; font-size: 1.15rem; padding: 0; line-height: 1;
      transition: opacity 0.15s, color 0.15s; flex-shrink: 0; pointer-events: none;
    }
    .color-reset-btn.changed { opacity: 0.4; pointer-events: auto; }
    .color-reset-btn.changed:hover { opacity: 1; color: var(--red); }
    .theme-custom-divider {
      grid-column: 1 / -1; font-size: 11px; color: var(--fg); opacity: 0.5;
      text-transform: uppercase; letter-spacing: 0.04em; margin: 6px 0 2px;
      border-top: 1px solid var(--border); padding-top: 6px;
    }
    .theme-swatch[data-custom] { position: relative; overflow: visible; }
    /* Accent-coloured circular X — always visible (mobile-friendly), sits
       INSIDE the swatch's top-right corner so it never crops into a
       neighbouring swatch. */
    .theme-delete-btn {
      position: absolute;
      top: -2px;
      right: -2px;
      width: 20px;
      height: 20px;
      padding: 0;
      border: none;
      border-radius: 50%;
      background: var(--accent, var(--red, #d92534));
      color: #fff;
      cursor: pointer;
      z-index: 2;
      display: flex;
      align-items: center;
      justify-content: center;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
      transition: transform 0.12s, background 0.12s;
    }
    .theme-delete-btn:hover {
      background: color-mix(in srgb, var(--accent, var(--red)) 80%, white);
      transform: scale(1.12);
    }
    .theme-delete-btn:active { transform: scale(0.95); }
    .theme-delete-btn svg {
      display: block;
      /* Optical nudge — the X reads off-center inside the circle without it. */
      position: relative;
      left: 1px;
      top: -1px;
    }
    .theme-save-row {
      display: flex; gap: 6px; margin-top: 8px;
    }
    .theme-save-row input {
      flex: 1; padding: 5px 8px; border: 1px solid var(--border); border-radius: 6px;
      background: var(--bg); color: var(--fg); font-size: 12px; font-family: inherit;
    }
    .theme-save-row input:focus { outline: none; border-color: var(--red); }
    .theme-save-row input::placeholder { color: color-mix(in srgb, var(--fg) 35%, transparent); }
    .theme-save-row button {
      padding: 6px 12px; border: 1px solid var(--red); border-radius: 6px;
      background: transparent; color: var(--red); cursor: pointer;
      font-size: 12px; font-family: inherit; white-space: nowrap; transition: all 0.15s;
    }
    .theme-save-row button:hover { background: color-mix(in srgb, var(--red) 11%, transparent); }
    .theme-save-error {
      font-size: 11px; color: var(--red); margin-top: 2px; display: none;
    }
    /* Import/Export */
    .theme-io-row { display: flex; gap: 6px; margin-top: 6px; }
    .theme-io-btn {
      flex: 1; padding: 5px 10px; border: 1px solid var(--border); border-radius: 6px;
      background: transparent; color: var(--fg); cursor: pointer;
      font-size: 12px; opacity: 0.7; transition: all 0.15s; font-family: inherit;
    }
    .theme-io-btn:hover { opacity: 1; border-color: var(--fg); background: color-mix(in srgb, var(--fg) 5%, transparent); }
    .theme-import-area {
      width: 100%; margin-top: 6px; padding: 6px 8px;
      border: 1px solid var(--border); border-radius: 6px;
      background: var(--bg); color: var(--fg); font-size: 0.7rem;
      font-family: inherit; resize: vertical; min-height: 48px;
    }
    .theme-import-area:focus { outline: none; border-color: var(--red); }
    .theme-import-area.hidden { display: none; }
    .theme-import-actions { display: flex; gap: 6px; margin-top: 4px; }
    .theme-import-actions.hidden { display: none; }
    /* Font & Density */
    .theme-fd-row { display: flex; gap: 8px; margin-bottom: 8px; }
    .theme-fd-group { flex: 1; display: flex; flex-direction: column; gap: 3px; }
    .theme-fd-label { font-size: 12px; font-weight: 500; color: var(--fg); opacity: 0.6; }
    .theme-fd-select {
      padding: 5px 8px; border: 1px solid var(--border); border-radius: 6px;
      background: var(--bg); color: var(--fg); font-size: 12px;
      font-family: inherit; cursor: pointer;
    }
    .theme-fd-select:focus { outline: none; border-color: var(--red); }
    .theme-fd-range {
      width: 100%;
      max-width: 100%;
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      height: 24px;
      background: transparent;
      cursor: pointer;
      -webkit-appearance: none;
      appearance: none;
      accent-color: var(--red);
    }
    .theme-fd-range::-webkit-slider-runnable-track {
      height: 4px; background: var(--border); border-radius: 2px;
    }
    .theme-fd-range::-moz-range-track {
      height: 4px; background: var(--border); border-radius: 2px;
    }
    .theme-fd-range::-webkit-slider-thumb {
      -webkit-appearance: none; appearance: none;
      width: 14px; height: 14px; border-radius: 50%;
      background: var(--red); border: none; margin-top: -5px; cursor: pointer;
    }
    .theme-fd-range::-moz-range-thumb {
      width: 14px; height: 14px; border-radius: 50%;
      background: var(--red); border: none; cursor: pointer;
    }
    .theme-fd-range:focus { outline: none; }
    /* Color Harmony Generator */
    .theme-harmony-row { display: flex; gap: 8px; align-items: flex-end; }
    .harmony-generate-btn {
      padding: 5px 14px; border: 1px solid var(--red); border-radius: 6px;
      background: transparent; color: var(--red); cursor: pointer;
      font-size: 12px; white-space: nowrap; transition: all 0.15s;
      font-family: inherit; width: 100%;
    }
    .harmony-generate-btn:hover { background: color-mix(in srgb, var(--red) 11%, transparent); }
    .harmony-preview {
      display: flex; height: 20px; border-radius: 6px; overflow: hidden;
      margin-top: 8px; border: 1px solid var(--border);
    }
    .harmony-preview:empty { display: none; border: none; }
    .harmony-preview span { flex: 1; }
    #theme-reset-btn {
      margin-top: 6px; width: 100%; padding: 6px; border: 1px solid var(--border);
      border-radius: 6px; background: var(--bg); color: var(--fg); cursor: pointer;
      font-size: 12px; font-family: inherit; opacity: 0.7; transition: opacity 0.15s;
    }
    #theme-reset-btn:hover { opacity: 1; }
    .theme-adv-toggle {
      margin-top: 10px; margin-bottom: 10px;
      padding: 6px 8px; cursor: pointer;
      font-size: 11px; color: var(--red); opacity: 0.8;
      border: 1px solid var(--border); border-radius: 6px;
      transition: opacity 0.15s, background 0.15s;
      user-select: none;
    }
    .theme-adv-toggle:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 4%, transparent); }
    .theme-adv-toggle .theme-adv-arrow {
      display: inline-block; transition: transform 0.2s; font-size: 10px;
      margin-right: 4px;
    }
    .theme-adv-toggle.open .theme-adv-arrow { transform: rotate(90deg); }
    .theme-adv-section { margin-top: 8px; margin-bottom: 10px; }
    .theme-adv-section.hidden { display: none; }
    .theme-adv-group { margin-bottom: 8px; }
    .theme-adv-group-label {
      font-size: 10px; color: var(--fg); opacity: 0.5;
      text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 4px;
    }
    .theme-adv-clear-btn {
      margin-top: 6px; width: 100%; padding: 5px; border: 1px solid var(--border);
      border-radius: 6px; background: transparent; color: var(--fg); opacity: 0.5;
      cursor: pointer; font-size: 12px; font-family: inherit; transition: opacity 0.15s;
    }
    .theme-adv-clear-btn:hover { opacity: 1; }


    /* Mobile: bottom sheet modals — slide up from bottom */
    @media (max-width: 768px) {
      .modal {
        align-items: flex-end;
        background: rgba(0,0,0,0.4);
        pointer-events: auto;
        /* Anchor the bottom sheet to the DYNAMIC viewport. The base overlay is
           position:fixed; height:100% = the layout viewport, whose bottom sits
           UNDER Firefox Android's bottom URL bar — so flex-end parked the sheet
           (and its footer / Start button) beneath the bar. dvh excludes the bar
           so the footer lands above it. Extra safe-area pad for iOS home bar. */
        height: 100dvh;
        padding-bottom: env(safe-area-inset-bottom, 0px);
      }
      /* Confirm dialog stays centered, not a bottom sheet */
      #styled-confirm-overlay {
        align-items: center;
      }
      .styled-confirm-box {
        border-radius: 12px !important;
        border: 1px solid var(--border) !important;
        animation: modal-enter 0.25s ease-out both !important;
        max-height: none !important;
      }
      .styled-confirm-box.modal-closing {
        animation: modal-exit 0.18s ease-in both !important;
      }
      .styled-confirm-box::before {
        display: none !important;
      }
      .styled-confirm-box .close-btn {
        display: none !important;
      }
      #theme-popup {
        width: 100% !important;
        height: 65vh !important;
        max-height: 65vh !important;
        top: auto !important; left: 0 !important; right: 0 !important; bottom: 0 !important;
        position: fixed !important;
        border-radius: 14px 14px 0 0;
        border: none;
        border-top: 1px solid var(--border);
        padding: 6px 12px 12px;
        animation: sheet-enter 0.2s ease-out forwards;
        overflow-y: hidden !important;
      }
      #theme-popup .theme-tab-panel {
        touch-action: pan-y;
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
      }
      #theme-popup.sheet-ready {
        animation: none;
      }
      #theme-popup.modal-closing {
        animation: sheet-exit 0.15s ease-in both !important;
      }
      .modal-content,
      .memory-modal-content,
      .settings-modal-content,
      #compare-model-overlay .modal-content {
        width: 100% !important;
        /* 85dvh leaves comfortable headroom above the sheet so the user can
           still see the chat behind it and the drag handle has breathing
           room. Was 65vh (too short, content clipped) → 90vh (too tall, top
           hugged the status bar). */
        max-height: 85dvh !important;
        max-height: 85vh !important; /* fallback for browsers without dvh */
        height: auto !important;
        border-radius: 14px 14px 0 0;
        border: none;
        border-top: 1px solid var(--border);
        padding-top: 6px !important;
        /* Clip children to the rounded top corners — otherwise the sticky
           modal-header's var(--panel) background paints a darker
           rectangle past the radius and the corners look square again. */
        overflow: hidden;
        animation: sheet-enter 0.2s ease-out forwards;
      }
      /* Tool modals fill the full mobile viewport — top edge to bottom —
         instead of stopping at the 85dvh sheet height and leaving a gap.
         Email's content carries both `modal-content` + `doclib-modal-content`,
         so the generic 85dvh rule above (later in source, equal specificity)
         was capping it; the ID-scoped selectors here win on specificity and
         bring every tool (cookbook / tasks / memory / settings / email /
         library) to the same top edge. */
      #cookbook-modal .modal-content,
      #tasks-modal .modal-content,
      #calendar-modal .modal-content,
      #memory-modal .memory-modal-content,
      #settings-modal .settings-modal-content,
      #email-lib-modal .modal-content,
      #doclib-modal .doclib-modal-content {
        max-height: 100vh !important;
        max-height: 100dvh !important;
        height: 100vh !important;
        height: 100dvh !important;
        /* Reserve the iOS home-indicator / bottom-bar strip so an expanded
           skill card's footer (Delete / Edit / Run) isn't hidden under it.
           dvh already excludes the URL bar; this handles the safe area. */
        padding-bottom: env(safe-area-inset-bottom, 0px) !important;
      }
      /* Grab handle pill on the non-standard content classes too. Memory's
         desktop rule defines a radial-glow ::before later in the file —
         these body-prefixed selectors win the specificity battle on mobile. */
      body .memory-modal-content::before,
      body .settings-modal-content::before {
        content: '';
        display: block;
        position: static;
        inset: auto;
        width: 36px; height: 4px;
        background: var(--fg);
        opacity: 0.25;
        border-radius: 2px;
        margin: 0 auto 4px;
        flex-shrink: 0;
        padding: 0;
        border-top: 10px solid transparent;
        border-bottom: 6px solid transparent;
        background-clip: padding-box;
        animation: none;
      }
      .memory-modal-content .close-btn,
      .memory-modal-content .modal-close,
      .settings-modal-content .close-btn,
      .settings-modal-content .modal-close {
        display: none !important;
      }
      .memory-modal-content,
      .settings-modal-content {
        touch-action: pan-y;
        overscroll-behavior: contain;
      }
      /* Library modals — go full-bleed on mobile so content extends to the
         very bottom (no wasted vh strip below). The parent modal centers
         them; padding-top reset is in the per-modal rules below. */
      #cookbook-modal .modal-content,
      #calendar-modal .modal-content,
      #email-lib-modal .modal-content,
      #doclib-modal .modal-content,
      #gallery-modal .modal-content,
      #tasks-modal .modal-content {
        /* vh-first as fallback for very old browsers, then dvh wins so
           the modal shrinks/grows with the mobile URL bar. Wrong order
           previously made the expanded chat preview extend below the
           visible viewport on Chrome/Safari mobile (URL bar covered the
           action buttons row). */
        max-height: 100vh !important;
        max-height: 100dvh !important;
        height: 100vh !important;
        height: 100dvh !important;
      }
      /* Anchor those modals to the top so the full height is usable */
      #cookbook-modal,
      #calendar-modal,
      #email-lib-modal,
      #doclib-modal,
      #gallery-modal,
      #tasks-modal {
        padding-top: 0 !important;
        align-items: stretch !important;
      }
      /* Deep Research already gets the swipe grab-handle pill from the
         shared `.modal-content::before` rule (the pane carries that class).
         The previous header-level pill was redundant and rendered as a
         SECOND pill stacked above the real one — removed. */
      /* The inner body must flex to fill the new full height, and the
         tasks list inside it must scroll independently — otherwise the
         list crops at whatever pre-mobile height the desktop layout had. */
      #tasks-modal .modal-content {
        display: flex !important;
        flex-direction: column !important;
      }
      #tasks-modal .modal-body {
        flex: 1 1 auto !important;
        min-height: 0 !important;
      }
      /* Memory/Skills: the body lacks flex:1, so on the fixed-height mobile
         sheet (overflow:hidden) it grew past the viewport and clipped the
         bottom of the skills list + its row action buttons with no way to
         scroll there. Bound the body so the inner list scrolls internally. */
      #memory-modal .memory-modal-content {
        display: flex !important;
        flex-direction: column !important;
      }
      /* flex-basis MUST be 0 (not auto) — matches the working #doclib-modal
         .modal-body. With basis:auto, Firefox (incl. mobile) sizes the body
         to its content (~half height) instead of filling, while Chromium
         fills it regardless — which is exactly why the skill expand worked
         on desktop/Chromium but stuck at ~50% on Firefox mobile. */
      #memory-modal .memory-modal-body {
        flex: 1 1 0 !important;
        min-height: 0 !important;
        overflow: hidden !important;
      }
      /* Same basis:0 fix down the rest of the skills chain so every layer
         fills instead of sizing to content under Firefox. */
      #memory-modal .memory-tab-panel[data-memory-panel="skills"] {
        flex: 1 1 0 !important;
        min-height: 0 !important;
      }
      #memory-modal .memory-tab-panel[data-memory-panel="skills"] > .admin-card {
        flex: 1 1 0 !important;
        min-height: 0 !important;
      }
      /* The skills modal carries an extra toolbar row (search/sort/select +
         bulk bar) the doc/email libraries don't, so the shared 82dvh preview
         min-height overflowed here and pushed the expanded footer (Delete /
         Edit / Run) off the bottom of the screen. Let flexbox size it from
         the resolved card height instead, so the footer always pins inside
         the visible area. */
      #memory-modal .doclib-card.doclib-card-expanded .doclib-card-preview {
        min-height: 0 !important;
      }
      /* Once enter animation finishes, clear it so inline transform works for dragging */
      .modal-content.sheet-ready {
        animation: none;
      }
      .modal-content.modal-closing {
        animation: sheet-exit 0.15s ease-in both !important;
      }
      /* Grab handle — large touch target, visible pill */
      #theme-popup::before,
      .modal-content::before {
        content: '';
        display: block;
        width: 36px; height: 4px;
        background: var(--fg); opacity: 0.25;
        border-radius: 2px;
        margin: 0 auto 4px;
        flex-shrink: 0;
        padding: 0;
        /* Expand touch target without changing visual size */
        border-top: 10px solid transparent;
        border-bottom: 6px solid transparent;
        background-clip: padding-box;
      }
      /* Hide close X on mobile — swipe down to dismiss */
      .modal-content .close-btn,
      .modal-content .modal-close,
      #theme-popup .close-btn {
        display: none !important;
      }
      /* Hide the auto-injected minimize (_) button on mobile — the dock
         chip already represents the minimized state, and the swipe-down
         gesture is the canonical minimize action. */
      .modal-minimize-btn,
      .minimize-btn,
      [data-minimize] {
        display: none !important;
      }
      /* Lock modals to vertical touch only, prevent horizontal dragging */
      .modal-content {
        touch-action: pan-y;
        overscroll-behavior: contain;
      }
      .modal-content .cookbook-body,
      .modal-content .modal-body {
        touch-action: pan-y;
        overscroll-behavior: contain;
      }
      .modal-header {
        cursor: default;
        touch-action: none;
      }
      @keyframes sheet-enter {
        from { transform: translateY(100%); }
        to   { transform: translateY(0); }
      }
      @keyframes sheet-exit {
        from { transform: translateY(0); }
        to   { transform: translateY(100%); }
      }
    }

    /* ── Model A/B Comparison ── */

    /* -- Extracted inline-style classes -- */
    .cmp-header-action-btn {
      background: none; border: 1px solid var(--border); color: var(--fg);
      cursor: pointer; padding: 3px 10px; font-size: 11px; font-weight: 600;
      opacity: 0.7; transition: all 0.15s; line-height: 1; border-radius: 4px;
      display: inline-flex; align-items: center; font-family: inherit;
    }
    .cmp-form-control {
      padding: 8px; background: var(--bg); color: var(--fg);
      border: 1px solid var(--border); border-radius: 6px; font-size: 0.85em;
    }
    .cmp-btn-secondary {
      background: transparent; color: var(--fg); border: 1px solid var(--border);
      border-radius: 6px; cursor: pointer;
    }
    .cmp-btn-primary {
      background: var(--fg); color: var(--bg); border: none;
      border-radius: 6px; cursor: pointer; font-weight: 600;
      transition: filter 0.12s, background 0.12s, color 0.12s;
    }
    /* Override global button:hover (which switches bg to var(--panel) =
       very dark) — keep the bright primary look and just brighten slightly. */
    .cmp-btn-primary:hover:not(:disabled) {
      background: var(--fg); color: var(--bg);
      filter: brightness(1.1);
    }
    .cmp-btn-primary:active:not(:disabled) { filter: brightness(0.95); }
    .cmp-btn-secondary:hover:not(:disabled) {
      background: color-mix(in srgb, var(--fg) 8%, transparent);
      border-color: var(--fg); color: var(--fg);
    }
    .cmp-model-row {
      display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
      transition: margin-left 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
    }
    .cmp-row-label {
      color: var(--fg); font-size: 0.82em; font-weight: 600; min-width: 20px;
      opacity: 0.4; text-align: center; flex-shrink: 0;
    }
    .cmp-rm-btn {
      background: none; border: none; color: var(--fg); cursor: pointer;
      min-width: 20px; font-size: 16px; font-weight: 600; opacity: 0.3;
      transition: all 0.15s; padding: 0; line-height: 1; text-align: center;
      position: relative; top: -1px;
    }
    .cmp-prov-select {
      flex: 0 0 auto; width: 120px; font-size: 0.8em;
    }
    /* Eval-prompts picker — only shown during compare; absolute top-right
       inside .chat-input-top, matching .model-picker-wrap's slot. */
    .chat-input-top > .cmp-eval-wrap {
      position: absolute;
      top: 0; right: 0;
      z-index: 2;
    }
    .cmp-eval-btn {
      display: inline-flex; align-items: center; gap: 4px;
      padding: 4px 8px;
      background: transparent;
      border: 1px solid var(--border);
      border-radius: 6px;
      color: var(--fg);
      font-size: 11px; font-weight: 500;
      font-family: inherit;
      cursor: pointer; opacity: 0.75;
      transition: opacity 0.15s, border-color 0.15s;
    }
    .cmp-eval-btn:hover { opacity: 1; border-color: var(--fg); }
    .cmp-eval-caret { opacity: 0.7; transform: rotate(180deg); }
    .cmp-eval-menu {
      position: absolute; bottom: calc(100% + 4px); right: 0;
      min-width: 220px; max-width: 280px;
      max-height: 360px; overflow-y: auto;
      background: var(--panel);
      border: 1px solid var(--border);
      border-radius: 6px;
      box-shadow: 0 -4px 16px rgba(0,0,0,0.3);
      padding: 4px;
      z-index: 1000;
    }
    .cmp-eval-menu.hidden { display: none; }
    .cmp-eval-group-label {
      font-size: 9px; text-transform: uppercase; letter-spacing: 0.5px;
      opacity: 0.45; font-weight: 600;
      padding: 6px 8px 2px;
    }
    .cmp-eval-item {
      display: block; width: 100%;
      text-align: left;
      padding: 5px 8px;
      background: none; border: none;
      color: var(--fg); font-size: 11px;
      font-family: inherit;
      border-radius: 4px;
      cursor: pointer;
    }
    .cmp-eval-item:hover {
      background: color-mix(in srgb, var(--fg) 8%, transparent);
    }
    .cmp-eval-empty {
      padding: 10px; text-align: center;
      font-size: 11px; opacity: 0.5;
    }
    /* Tick on items that ship a known expected answer */
    .cmp-eval-item-tick {
      float: right;
      margin-left: 6px;
      font-size: 10px;
      color: var(--color-success, #4caf50);
      opacity: 0.8;
    }
    /* Expected-answer panel — floats as its own little window above the
       chat-input-bar. Distinct surface, padded, drop-shadow, slightly
       lifted so it reads as a separate UI element, not part of the input. */
    .chat-input-bar:has(.cmp-eval-expected) { position: relative; }
    .cmp-eval-expected {
      position: absolute;
      bottom: calc(100% + 8px);
      right: 0;
      display: inline-flex; align-items: center; gap: 8px;
      padding: 8px 12px;
      font-size: 11px;
      background: var(--panel);
      border: 1px solid color-mix(in srgb, var(--color-success, #4caf50) 50%, transparent);
      border-radius: 8px;
      box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.2);
      color: var(--fg);
      width: fit-content;
      z-index: 5;
      pointer-events: auto;
    }
    .cmp-eval-expected.hidden { display: none; }
    .cmp-eval-expected-label {
      opacity: 0.6;
      text-transform: uppercase;
      font-size: 9px;
      letter-spacing: 0.5px;
      font-weight: 600;
    }
    .cmp-eval-expected-value {
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
      font-size: 11px;
    }
    .cmp-eval-expected-close {
      background: none; border: none; color: var(--fg);
      font-size: 14px; line-height: 1; padding: 0 0 0 4px;
      opacity: 0.5; cursor: pointer; font-family: inherit;
    }
    .cmp-eval-expected-close:hover { opacity: 1; }
    /* Auto-grade badge — stamped on a pane after stream completes when an
       eval prompt with a known expected answer was used. */
    .pane-grade-badge {
      display: inline-flex; align-items: center; justify-content: center;
      width: 18px; height: 18px;
      margin: 0 4px;
      font-size: 12px; font-weight: 700;
      border-radius: 50%;
      border: 1px solid currentColor;
      flex-shrink: 0;
    }
    .pane-grade-badge.pass {
      color: var(--color-success, #4caf50);
      background: color-mix(in srgb, var(--color-success, #4caf50) 12%, transparent);
    }
    .pane-grade-badge.fail {
      color: var(--color-error, #e55);
      background: color-mix(in srgb, var(--color-error, #e55) 12%, transparent);
    }

    /* Compare probe overlay */
    .compare-probe-overlay {
      position: fixed; inset: 0; z-index: 300;
      background: rgba(0,0,0,0.5); display: flex;
      align-items: center; justify-content: center;
    }
    .compare-probe-card {
      background: var(--panel); border-radius: 12px; padding: 20px 24px;
      display: flex; flex-direction: column; align-items: center; gap: 12px;
      min-width: 280px; max-width: 90vw; box-shadow: 0 8px 32px rgba(0,0,0,0.3);
      overflow: hidden;
    }
    .compare-probe-title {
      font-size: 13px; font-weight: 600; opacity: 0.7;
    }
    .compare-probe-list {
      display: grid; grid-template-columns: 1fr 1fr; gap: 6px; min-width: 320px; width: 100%;
    }
    .compare-probe-row {
      display: flex; align-items: center; gap: 6px; padding: 6px 10px;
      border-radius: 6px; background: color-mix(in srgb, var(--fg) 4%, transparent);
      font-size: 12px; transition: background 0.2s; overflow: hidden;
    }
    .compare-probe-row.fail {
      background: color-mix(in srgb, var(--color-error, #f44) 8%, transparent);
    }
    .compare-probe-spinner {
      width: 24px; text-align: center; font-size: 10px; flex-shrink: 0;
      letter-spacing: -1px; opacity: 0.6;
    }
    .compare-probe-spinner.ok { color: var(--color-success); animation: none; }
    .compare-probe-spinner.fail { color: var(--color-error, #f44); animation: none; }
    .compare-probe-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .compare-probe-status { font-size: 11px; opacity: 0.5; flex-shrink: 0; max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .compare-probe-status.ok { color: var(--color-success); opacity: 1; }
    .compare-probe-status.fail { color: var(--color-error, #f44); opacity: 1; }
    .compare-probe-action-btn {
      padding: 2px 8px; background: transparent; color: var(--fg); border: 1px solid var(--border);
      border-radius: 4px; cursor: pointer; font-size: 10px; font-family: inherit;
      opacity: 0.7; transition: opacity 0.15s, border-color 0.15s; white-space: nowrap; flex-shrink: 0;
    }
    .compare-probe-action-btn:hover { opacity: 1; border-color: var(--accent); }
    @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
    @keyframes pane-shake {
      0%, 100% { transform: translateX(0); }
      15% { transform: translateX(-3px) rotate(-0.5deg); }
      30% { transform: translateX(3px) rotate(0.5deg); }
      45% { transform: translateX(-2px); }
      60% { transform: translateX(2px); }
      75% { transform: translateX(-1px); }
    }

    .chat-container.compare-active {
      display: flex;
      flex-direction: column;
      padding: 0;
      overflow: hidden;
      animation: compare-enter 0.3s ease-out;
    }
    @keyframes compare-enter {
      from { opacity: 0; transform: translateY(8px); }
      to { opacity: 1; transform: translateY(0); }
    }
    .chat-container.compare-active .chat-input-bar {
      margin-bottom: 0;
    }
    .chat-container.compare-active #model-picker-wrap {
      display: none !important;
    }
    .compare-grid {
      display: grid;
      gap: 4px;
      flex: 1 1 0;
      min-height: 0;
      overflow: hidden;
    }
    .compare-grid[data-cols="2"] { grid-template-columns: 1fr 1fr; }
    .compare-grid[data-cols="3"] { grid-template-columns: 1fr 1fr 1fr; }
    .compare-grid[data-cols="4"] { grid-template-columns: repeat(4, 1fr); }
    .compare-grid[data-cols="5"] { grid-template-columns: repeat(4, 1fr); }
    .compare-grid[data-cols="6"] { grid-template-columns: repeat(4, 1fr); }
    .compare-grid[data-cols="7"] { grid-template-columns: repeat(4, 1fr); }
    .compare-grid[data-cols="8"] { grid-template-columns: repeat(4, 1fr); }
    .compare-grid { grid-auto-rows: 1fr; }
    /* Sequential waterfall layout — stacked rows, staggered left, flush right */
    .compare-grid.sequential-layout {
      display: flex !important;
      flex-direction: column !important;
      grid-template-columns: none !important;
      gap: 4px;
      overflow-y: auto;
    }
    .compare-grid.sequential-layout .compare-pane {
      flex-shrink: 0;
      min-height: 200px;
      transition: margin-left 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease;
    }
    /* Herringbone diagonal cut on left side of sequential pane headers */
    .compare-grid.sequential-layout .compare-pane .pane-header {
      clip-path: polygon(20px 0, 100% 0, 100% 100%, 0 100%);
      padding-left: 26px;
    }
    .compare-pane {
      display: flex;
      flex-direction: column;
      border: 1px solid var(--border);
      border-radius: 6px;
      overflow: hidden;
      min-height: 0;
      min-width: 0;
    }
    .compare-pane .pane-header {
      display: flex;
      align-items: center;
      gap: 4px;
      padding: 4px 10px;
      background: color-mix(in srgb, var(--fg) 4%, transparent);
      border-bottom: 1px solid var(--border);
      font-size: 0.82em;
      font-weight: 600;
      color: var(--fg);
      transition: background 0.4s;
      flex-shrink: 0;
      overflow: hidden;
      min-width: 0;
      flex-wrap: wrap;
    }
    .pane-actions {
      display: flex; gap: 4px; align-items: center; margin-left: auto; flex-shrink: 0;
    }
    .compare-pane-footer {
      font-size: 0.72em; opacity: 0.4; padding: 4px 10px;
      border-top: 1px solid color-mix(in srgb, var(--border) 40%, transparent);
      text-align: center; flex-shrink: 0;
    }
    /* Per-pane vote button. Sits at the bottom of each compare pane so
       the action lives right under the response it judges. */
    .pane-vote-footer {
      padding: 6px 8px;
      border-top: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
      background: color-mix(in srgb, var(--fg) 3%, transparent);
      flex-shrink: 0;
    }
    .pane-vote-btn {
      width: 100%;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 4px;
      background: var(--bg);
      color: var(--fg);
      border: 1px solid var(--border);
      border-radius: 6px;
      padding: 6px 10px;
      font-family: inherit;
      font-size: 12px;
      font-weight: 500;
      cursor: pointer;
      transition: background 0.15s, border-color 0.15s, opacity 0.15s;
    }
    .pane-vote-btn:hover:not(:disabled) {
      background: color-mix(in srgb, var(--accent, var(--fg)) 12%, var(--bg));
      border-color: var(--accent, var(--fg));
    }
    .pane-vote-btn:disabled { cursor: not-allowed; }
    .pane-vote-label {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      min-width: 0;
    }
    .pane-action-btn {
      background: none; border: none; color: var(--fg); cursor: pointer;
      opacity: 0.3; padding: 2px; border-radius: 4px;
      display: flex; align-items: center; transition: all 0.15s;
    }
    .pane-action-btn:hover { opacity: 0.8; background: color-mix(in srgb, var(--fg) 6%, transparent); }
    /* Pane title as clickable model-swap button */
    .pane-title-btn {
      background: none; border: none; cursor: pointer;
      font-size: 10px; font-weight: 400; font-family: inherit;
      color: var(--fg); padding: 0;
      text-align: left; display: flex; align-items: center; gap: 4px;
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
      transition: opacity 0.15s;
      min-width: 0; flex: 1 1 0;
    }
    .pane-title-btn:hover { opacity: 0.7; }
    .pane-title-caret { font-size: 0.6em; opacity: 0.35; flex-shrink: 0; position: relative; top: 2px; }
    .pane-title-btn:hover .pane-title-caret { opacity: 0.7; }
    .compare-pane .pane-close-btn { opacity: 0.3; }
    .compare-pane .pane-close-btn:hover { opacity: 1; color: var(--color-error); }
    /* Model swap dropdown under pane title */
    .pane-model-dropdown {
      position: absolute;
      z-index: 1000;
      min-width: 220px;
      max-height: 300px;
      overflow-y: auto;
      background: var(--bg);
      border: 1px solid var(--border);
      border-radius: 8px;
      box-shadow: 0 4px 16px rgba(0,0,0,0.3);
      padding: 4px;
    }
    .pane-model-item {
      display: block; width: 100%;
      padding: 6px 10px; font-size: 0.7em;
      text-align: left; background: none; border: none; border-radius: 4px;
      color: var(--fg); cursor: pointer; transition: background 0.1s;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .pane-model-item:hover { background: color-mix(in srgb, var(--fg) 10%, transparent); }
    .pane-model-item.current { color: var(--red); font-weight: 600; }
    .pane-timer {
      font-size: 10px; font-weight: 400; opacity: 0.45; font-variant-numeric: tabular-nums;
      white-space: nowrap; padding-right: 4px;
    }
    /* When 4+ panes, timer wraps to its own row */
    .compare-grid[data-cols="4"] .pane-timer,
    .compare-grid[data-cols="5"] .pane-timer,
    .compare-grid[data-cols="6"] .pane-timer {
      width: 100%; order: 99; margin-top: -4px; padding-bottom: 2px; padding-left: 2px;
    }
    .pane-finish-badge {
      font-weight: 600; color: var(--red);
    }
    .compare-pane.winner .pane-header {
      background: color-mix(in srgb, var(--red) 12%, transparent);
      border-bottom-color: color-mix(in srgb, var(--red) 30%, var(--border));
    }
    .compare-pane.winner .pane-title {
      color: var(--red);
    }
    .compare-pane.loser .pane-header {
      opacity: 0.5;
    }
    .confetti-piece {
      position: fixed;
      pointer-events: none;
      z-index: 1000000;
    }
    .compare-pane.expanded { grid-column: 1 / -1; }
    .compare-pane .chat-history {
      flex: 1 1 0;
      min-height: 0;
      overflow-y: auto !important;
      overflow-x: hidden;
      padding: 8px;
      display: flex;
      flex-direction: column;
    }
    .compare-pane .chat-history .msg {
      flex-shrink: 0;
    }
    .compare-gen-image {
      max-width: 100%;
      max-height: 100%;
      object-fit: contain;
      border-radius: 4px;
      cursor: pointer;
    }
    .compare-section {
      margin-bottom: 14px;
    }
    .compare-section:last-child {
      margin-bottom: 0;
    }
    .compare-section-label {
      font-size: 9px;
      text-transform: uppercase;
      letter-spacing: 0.6px;
      opacity: 0.5;
      margin-bottom: 5px;
      font-weight: 600;
    }
    /* Active-type/mode readout next to "Type:"/"Mode:" — only needed on mobile
       (where the tab/toggle text labels are hidden); hidden on desktop. */
    .compare-type-current,
    .compare-mode-current { display: none; }
    /* Contextual one-liner under the mode toggles describing what you just
       changed — empty until the first toggle, then a subtle hint. */
    .compare-mode-hint {
      font-size: 11px;
      opacity: 0.55;
      margin-top: 6px;
      min-height: 0;
      line-height: 1.3;
    }
    .compare-mode-hint:empty { display: none; }
    .compare-mode-tabs {
      display: flex;
      gap: 4px;
      flex-wrap: wrap;
      min-width: 0;
    }
    /* Type tabs match Mode toggles 1:1 (same flex column layout, same metrics) */
    .compare-mode-tab {
      display: flex; flex-direction: column; align-items: center; justify-content: center;
      width: 56px; height: auto; flex: 1 1 0;
      padding: 5px 4px 4px; border: 1px solid var(--border); border-radius: 6px;
      cursor: pointer; user-select: none; transition: all 0.15s;
      background: none; color: var(--fg); opacity: 0.35;
      flex-shrink: 0; gap: 0;
      font-family: inherit;
    }
    .compare-mode-tab:hover {
      opacity: 0.7; background: color-mix(in srgb, var(--fg) 6%, transparent);
    }
    .compare-mode-tab.active {
      opacity: 1; border-color: var(--fg);
      background: color-mix(in srgb, var(--fg) 10%, transparent);
    }
    .compare-sources-box {
      display: flex; align-items: center; gap: 6px;
      padding: 6px 10px; margin-bottom: 8px;
      border-radius: 6px; font-size: 0.78em;
      background: color-mix(in srgb, var(--red) 8%, transparent);
      border: 1px solid color-mix(in srgb, var(--red) 20%, transparent);
      color: color-mix(in srgb, var(--fg) 70%, transparent);
      cursor: default;
    }
    .compare-sources-box .sources-label { font-weight: 600; }
    /* Compact tool blocks inside compare panes */
    .compare-pane .agent-thread-node {
      margin: 4px 0; font-size: 0.85em;
    }
    .compare-pane .agent-thread-cmd {
      max-height: 80px; overflow-y: auto;
    }
    .compare-pane .agent-tool-output pre {
      max-height: 120px; overflow-y: auto;
    }
    .compare-vote-bar {
      display: flex; justify-content: center; gap: 8px;
      padding: 8px; border-top: 1px solid var(--border);
      flex-shrink: 0; flex-wrap: wrap;
    }
    .compare-vote-bar.hidden { display: none; }
    .compare-vote-btn {
      padding: 6px 13px; border: 1px solid var(--border); border-radius: 6px;
      background: var(--panel); color: var(--fg); cursor: pointer; font-size: 0.8em;
      transition: all 0.15s; white-space: nowrap;
    }
    .compare-vote-btn:hover { border-color: var(--red); background: color-mix(in srgb, var(--red) 11%, transparent); }
    .compare-vote-tie { opacity: 0.7; }
    .compare-rematch-btn { display: flex; align-items: center; gap: 6px; margin-left: 8px; border-color: color-mix(in srgb, var(--fg) 20%, transparent); opacity: 0.6; }
    .compare-rematch-btn:hover { opacity: 1; }
    /* Preview button accent in pane header */
    .pane-preview-btn.active { color: var(--red); }
    /* Full-pane iframe for HTML preview */
    .compare-pane-iframe {
      flex: 1;
      width: 100%;
      border: none;
      border-radius: 0 0 8px 8px;
      background: #fff;
    }
    /* ---- Add-pane "+" button in pane header (last pane only) ---- */
    .pane-add-btn { display: none !important; font-size: 16px; font-weight: 600; }
    .compare-pane:last-child .pane-add-btn { display: flex !important; }
    /* Dropdown for adding a pane */
    .add-pane-dropdown {
      position: absolute;
      right: 0;
      z-index: 100;
      background: var(--panel, var(--bg));
      border: 1px solid var(--border);
      border-radius: 6px;
      max-height: 300px;
      overflow-y: auto;
      min-width: 220px;
      box-shadow: 0 4px 12px rgba(0,0,0,0.3);
      padding: 4px;
    }
    .add-pane-search {
      width: 100%;
      padding: 6px 8px;
      border: 1px solid var(--border);
      border-radius: 4px;
      background: var(--bg);
      color: var(--fg);
      font-size: 0.85em;
      box-sizing: border-box;
      margin-bottom: 4px;
      position: sticky;
      top: 0;
      z-index: 1;
    }
    /* Compare toggle buttons — icon + label stacked */
    .compare-toggle-label {
      display: block;
      font-size: 9px;
      line-height: 1;
      margin-top: 2px;
      font-weight: 500;
    }
    .compare-blind-toggle,
    .compare-save-toggle,
    .compare-dice-toggle,
    .compare-parallel-toggle,
    .compare-reset-toggle {
      display: flex; flex-direction: column; align-items: center; justify-content: center;
      width: 56px; height: auto; flex: 1 1 0;
      padding: 5px 4px 4px; border: 1px solid var(--border); border-radius: 6px;
      cursor: pointer; user-select: none; transition: all 0.15s;
      background: none; color: var(--fg); opacity: 0.35;
      flex-shrink: 0; gap: 0;
    }
    .compare-blind-toggle:hover,
    .compare-save-toggle:hover,
    .compare-dice-toggle:hover,
    .compare-parallel-toggle:hover,
    .compare-reset-toggle:hover {
      opacity: 0.7; background: color-mix(in srgb, var(--fg) 6%, transparent);
    }
    .compare-blind-toggle.active {
      opacity: 1; color: var(--color-blind-orange); border-color: var(--color-blind-orange);
      background: rgba(255, 152, 0, 0.1);
    }
    .compare-save-toggle.active {
      opacity: 1; color: var(--color-save-green); border-color: var(--color-save-green);
      background: color-mix(in srgb, var(--red) 10%, transparent);
    }
    .compare-dice-toggle.active {
      opacity: 1; color: var(--red); border-color: var(--red);
      background: color-mix(in srgb, var(--red) 10%, transparent);
    }
    .compare-parallel-toggle {
      opacity: 1; color: #e0a050; border-color: #e0a050;
      background: rgba(224, 160, 80, 0.1);
    }
    .compare-parallel-toggle.active {
      color: #5b8def; border-color: #5b8def;
      background: rgba(91, 141, 239, 0.1);
    }
    @media (max-width: 520px) {
      .compare-toggle-label { display: none; }
      /* Tab text labels are hidden on mobile, so spell out the active type next
         to "Type:" with its icon. */
      .compare-type-current {
        display: inline-flex;
        align-items: center;
        gap: 4px;
        margin-left: 5px;
        font-weight: 600;
        vertical-align: -3px;
      }
      .compare-type-current svg { width: 14px; height: 14px; }
      /* Mode list — comma-separated, each name already coloured inline. */
      .compare-mode-current {
        display: inline;
        margin-left: 5px;
        font-weight: 600;
      }
      .compare-blind-toggle,
      .compare-save-toggle,
      .compare-dice-toggle,
      .compare-parallel-toggle,
      .compare-reset-toggle {
        width: 32px; height: 32px; min-width: 32px; padding: 0;
      }
      /* The Group tab's seq/parallel toggle is a single full-width button (not
         a row of compact icons like the Compare header), so keep its label and
         make it a comfortable touch target with the text beside the icon. */
      #group-mode-btn {
        flex-direction: row !important;
        width: auto !important;
        height: auto !important;
        min-height: 44px;
        padding: 8px 14px !important;
        gap: 8px !important;
        font-size: 13px;
      }
      #group-mode-btn .compare-toggle-label {
        display: inline !important;
        font-size: 13px;
        margin-top: 0;
      }
      #group-mode-btn svg { width: 18px; height: 18px; }
      /* Compare header: hide labels + close button, show icons only */
      #compare-shuffle-btn span {
        display: none;
      }
      #compare-shuffle-btn,
      #compare-check-btn,
      #compare-add-btn {
        padding: 3px 6px;
      }
      /* Save space so the header buttons fit on one row: tighter padding +
         smaller labels (icons kept full size). (Score now lives in the vote bar.) */
      .compare-header-bar button { padding: 3px 5px !important; }
      .compare-header-bar #compare-check-btn > span,
      .compare-header-bar #compare-add-btn > span {
        font-size: 10px; margin-left: 2px;
      }
      /* Compare mobile: keep the close X visible — it's the only way out
         now that the hamburger is hidden during compare mode. */
      .compare-header-bar {
        padding: 14px 8px 10px 8px !important;
        min-height: 44px;
      }
      /* Mode tabs: icons only, centered */
      .compare-mode-tab span { display: none; }
      .compare-mode-tabs { justify-content: center; }
      /* Header action buttons: hide text labels on Export / Shuffle /
         Model on mobile so the close X fits on the right. Score keeps
         its "Score" label because its icon (4-square grid) reads too
         similar to Shuffle's icon without text. */
      .compare-header-bar #compare-export-btn > span,
      .compare-header-bar #compare-shuffle-btn > span {
        display: none;
      }
      /* Override the desktop override: on mobile the model picker overlay
         MUST cap its height to the viewport so the dropdown doesn't run
         past the bottom of the screen. */
      #compare-model-overlay .modal-content {
        max-height: 85dvh !important;
        max-height: 85vh !important;
        overflow: hidden !important;
      }
      #compare-model-overlay .modal-body {
        overflow: auto !important;
        flex: 1 1 auto !important;
        min-height: 0 !important;
      }
    }
    /* Hide number input spinners */
    input[type="number"]::-webkit-inner-spin-button,
    input[type="number"]::-webkit-outer-spin-button {
      -webkit-appearance: none; margin: 0;
    }

    /* ── Sensitive info censor ── */
    .censored-item {
      filter: blur(5px);
      cursor: pointer;
      transition: filter 0.2s;
      border-radius: 2px;
      padding: 0 2px;
      background: rgba(255,100,100,0.08);
      user-select: none;
    }
    .censored-item:hover { filter: blur(3px); background: rgba(255,100,100,0.15); }
    .censored-item.revealed {
      filter: none;
      background: rgba(100,255,100,0.08);
      user-select: auto;
      cursor: text;
    }

    #settings-menu-list .list-item.active {
      background: color-mix(in srgb, var(--accent) 10%, transparent);
      border-color: var(--accent);
    }

    /* ── Print / PDF Export ── */
    @media print {
      body { background: #fff !important; color: #000 !important; }
      #sidebar, .sidebar, #icon-rail, .hamburger-btn, #sidebar-backdrop, .chat-input-bar, .input-bar-wrapper,
      #welcome-screen, .chat-top-bar, .chat-meta-overlay, .msg-footer,
      .modal, .toast, .overflow-wrapper, .mode-toggle, .incognito-btn,
      button, .dropdown, .session-dropdown,
      .agent-tool-spinner, .agent-thread-node.running { display: none !important; }
      main.chat-container { width: 100% !important; margin: 0 !important; padding: 0 !important; max-height: none !important; overflow: visible !important; }
      #chat-history { max-height: none !important; overflow: visible !important; height: auto !important; padding: 0 !important; }
      .msg { break-inside: avoid; page-break-inside: avoid; border: none !important; box-shadow: none !important; }
      .msg-ai { background: #f5f5f5 !important; color: #000 !important; }
      .msg-user { background: #e8e8e8 !important; color: #000 !important; }
      .msg .role { color: #333 !important; font-weight: bold; }
      .msg .body { color: #000 !important; }
      pre, code { background: #f0f0f0 !important; color: #000 !important; border: 1px solid #ccc !important; }
      details { display: block !important; }
      details[open] summary ~ * { display: block !important; }
      details > summary { list-style: none; }
      details > summary::before { content: "" !important; }
      #chat-history::before { content: attr(data-print-title); display: block; font-size: 1.3em; font-weight: bold; margin-bottom: 1em; color: #000; }
      a { color: #000 !important; text-decoration: underline; }
    }


/* ── Components (from style.css) ── */

/* Self-hosted Fira Code font */
@font-face { font-family: 'Fira Code'; font-weight: 300; font-style: normal; font-display: swap; src: url('/static/fonts/FiraCode-Light.woff2') format('woff2'); }
@font-face { font-family: 'Fira Code'; font-weight: 400; font-style: normal; font-display: swap; src: url('/static/fonts/FiraCode-Regular.woff2') format('woff2'); }
@font-face { font-family: 'Fira Code'; font-weight: 600; font-style: normal; font-display: swap; src: url('/static/fonts/FiraCode-SemiBold.woff2') format('woff2'); }

/* Scrollbar styling */

/* Code block styling */
pre, code, .hljs {
  font-size: 0.95em;
  line-height: 1.5;
}

/* WebKit (Chrome, Edge, Safari) */
/* Utility class for red text */
.red-text {
  color: var(--red);
}

/* Internal chat links (search results, session references) */
a.chat-link {
  color: var(--hl-function);
  text-decoration: none;
  border-bottom: 1px dotted var(--hl-function);
  cursor: pointer;
}
a.chat-link:hover {
  opacity: 0.8;
  border-bottom-style: solid;
}

/* Session items */
.session-item { position: relative; }
.text-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.session-menu-btn { padding: 0 2px !important; min-width: 20px; height: 20px; display: inline-flex !important; align-items: center; justify-content: center; background: none !important; border-color: transparent !important; }
.session-menu-btn:hover { background: none !important; border-color: transparent !important; }
@media (max-width: 768px) {
  .session-menu-btn { display: none !important; }
  .item-drag-handle { display: none !important; }
}
.session-menu-btn svg { transition: transform 0.2s ease; }

/* First-time swipe hint */
.swipe-hint {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 0.7rem;
  color: var(--color-error, #f44);
  opacity: 0.8;
  transition: opacity 0.5s ease;
  pointer-events: none;
  display: flex;
  align-items: center;
  gap: 4px;
  z-index: 2;
}
.swipe-hint-arrow {
  animation: swipe-nudge 1s ease-in-out infinite;
}
@keyframes swipe-nudge {
  0%, 100% { transform: translateX(0); }
  50% { transform: translateX(-6px); }
}

/* Utility classes */
.muted { opacity: 0.5; }
.muted-sm { opacity: 0.35; font-size: 0.8em; }
.accent-link { color: var(--accent-primary, var(--color-accent)); cursor: pointer; font-size: 0.85em; }
.models-empty-state { text-align: center; padding: 12px 8px; line-height: 1.6; }

/* Provider logo inside favorite dot */
.provider-logo {
  border: none !important;
  background: none !important;
  width: 14px !important; height: 14px !important;
  display: inline-flex; align-items: center; justify-content: center;
  transition: opacity 0.15s;
}
.provider-logo svg { width: 14px; height: 14px; display: block; }
.provider-logo:hover { opacity: 1 !important; transform: scale(1.2); }

/* Hide session menu button until hover — use width:0 so it doesn't steal space from text */
.list-item .hamburger { opacity: 0; width: 0; min-width: 0; overflow: hidden; padding: 0 !important; transition: opacity 0.15s, width 0.15s, padding 0.15s; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
.list-item:hover .hamburger { opacity: 1; width: 24px; min-width: 24px; padding: 0 4px !important; }
@media (max-width: 768px) {
  .list-item .hamburger { opacity: 0.5; width: 28px; min-width: 28px; padding: 0 4px !important; }
  .list-item .hamburger:active { opacity: 1; }
}

/* Hamburger menu button styling (overrides default button appearance) */
button.hamburger {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

/* ============================================ */
/* HEADER SIZING FOR CHAT MESSAGES */
/* ============================================ */
/* Markdown in chat messages — colorful, scannable */
.msg h1, .msg h2, .msg h3, .msg h4, .msg h5, .msg h6 {
  margin: 0.6em 0 0.3em 0;
  line-height: 1.3;
  border-bottom: none;
  padding-bottom: 0;
}

.msg h1 {
  font-size: 1.15em;
  font-weight: 700;
  color: var(--hl-keyword, #c678dd);
}

.msg h2 {
  font-size: 1.1em;
  font-weight: 600;
  color: var(--hl-function, #5b8def);
}

.msg h3 {
  font-size: 1.05em;
  font-weight: 600;
  color: var(--hl-string, #98c379);
}

.msg h4 {
  font-size: 1.02em;
  font-weight: 600;
  color: var(--hl-builtin, #e5c07b);
}

.msg h5 {
  font-size: 1em;
  font-weight: 600;
  color: var(--hl-variable, #61afef);
}

.msg h6 {
  font-size: 0.95em;
  font-weight: 600;
  color: var(--hl-number, #d19a66);
}

/* Bold text — subtle accent color */
.msg strong, .msg b {
  color: var(--hl-builtin, #e5c07b);
  font-weight: 600;
}

/* Italic — softer highlight */
.msg em, .msg i {
  color: var(--hl-params, #abb2bf);
  font-style: italic;
}

/* Bold + italic */
.msg strong em, .msg em strong,
.msg b i, .msg i b,
.msg b em, .msg em b,
.msg strong i, .msg i strong {
  color: var(--hl-keyword, #c678dd);
}

/* Strikethrough */
.msg del {
  color: color-mix(in srgb, var(--fg) 45%, transparent);
  text-decoration: line-through;
}

/* Inline code */
.msg code:not(pre code) {
  color: var(--hl-string, #98c379);
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  padding: 1px 5px;
  border-radius: 4px;
  font-size: 0.9em;
}

/* Blockquotes */
.msg blockquote {
  border-left: 3px solid var(--hl-function, #5b8def);
  padding: 4px 12px;
  margin: 0.5em 0;
  color: color-mix(in srgb, var(--fg) 75%, transparent);
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  border-radius: 0 6px 6px 0;
}
.msg blockquote p { margin: 0.3em 0; }

/* Horizontal rules */
.msg hr {
  border: none;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--border), transparent);
  margin: 0.8em 0;
}

/* Lists */
.msg ul, .msg ol {
  margin: 0.3em 0 0.3em 1.2em;
  padding: 0;
}
.msg li {
  margin: 0.15em 0;
}
.msg li::marker {
  color: var(--hl-function, #5b8def);
}

/* Links */
.msg a {
  color: var(--hl-function, #5b8def);
  text-decoration: none;
  border-bottom: 1px solid rgba(91, 141, 239, 0.3);
  transition: border-color 0.15s;
}
.msg a:hover {
  border-bottom-color: var(--hl-function, #5b8def);
}

/* Tables */
.msg table {
  border-collapse: collapse;
  margin: 0.5em 0;
  font-size: 0.9em;
  width: auto;
}
.msg th {
  background: color-mix(in srgb, var(--fg) 7%, transparent);
  color: var(--hl-keyword, #c678dd);
  font-weight: 600;
  padding: 6px 12px;
  border: 1px solid var(--border);
  text-align: left;
}
.msg td {
  padding: 5px 12px;
  border: 1px solid var(--border);
}

/* Agent UI Styling */
.agent-controls {
  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  border: 1px solid #dee2e6;
  border-radius: 8px;
  padding: 12px;
  margin-bottom: 12px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.agent-toggle label {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-weight: 500;
  margin-bottom: 8px;
}

#workflow-selector {
  margin-top: 8px;
}

#workflow-type {
  width: 100%;
  padding: 6px 12px;
  border: 1px solid #ced4da;
  border-radius: 4px;
  background: white;
  font-size: 14px;
}

.agent-progress {
  background: #ffebee;
  border: 1px solid #ef9a9a;
  border-radius: 6px;
  padding: 12px;
  margin: 8px 0;
  text-align: center;
}

.agent-working {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-style: italic;
  color: var(--red);
}

.loading-dots::after {
  content: '...';
  animation: dots 1.5s infinite;
}

@keyframes dots {
  0%, 20% { opacity: 0; }
  40% { opacity: 0.5; }
  60%, 100% { opacity: 1; }
}

.workflow-info {
  background: #f8f9fa;
  border: 1px solid var(--red);
  border-radius: 6px;
  padding: 8px 12px;
  margin: 4px 0;
  font-size: 0.9em;
  color: var(--red);
  text-align: center;
}

/* Scrollbar uses --red from :root (set at top of file) */

/* Loading spinner */
@keyframes spin {
  to { transform: rotate(360deg); }
}
.spinner {
  width: 24px;
  height: 24px;
  margin: 8px auto;
  border: 3px solid var(--border);
  border-top-color: var(--red);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}

/* Inline spinner for buttons */
.btn-spinner {
  display: inline-block;
  width: 12px;
  height: 12px;
  border: 2px solid transparent;
  border-top-color: currentColor;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-right: 6px;
}

/* Loading indicator for messages */
.loading-indicator {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 10px;
}
.loading-dots {
  display: flex;
  gap: 4px;
}
.loading-dot {
  width: 6px;
  height: 6px;
  background-color: var(--fg);
  border-radius: 50%;
  opacity: 0.6;
}
.loading-dot:nth-child(1) {
  animation: loading-bounce 1.4s infinite ease-in-out both;
}
.loading-dot:nth-child(2) {
  animation: loading-bounce 1.4s infinite ease-in-out both;
  animation-delay: -0.32s;
}
.loading-dot:nth-child(3) {
  animation: loading-bounce 1.4s infinite ease-in-out both;
  animation-delay: -0.64s;
}
@keyframes loading-bounce {
  0%, 80%, 100% {
    transform: scale(0);
  }
  40% {
    transform: scale(1);
  }
}

/* Hamburger menu button */
.hamburger {
  display: inline-flex;
  flex-direction: column;
  justify-content: space-between;
  width: 24px;
  height: 18px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}
.hamburger span {
  display: block;
  width: 100%;
  height: 3px;
  background: var(--fg);
  border-radius: 2px;
}

/* Agent indicator */
#agent-indicator {
  position: fixed;
  top: 20px;
  right: 20px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  padding: 6px 12px;
  border-radius: 6px;
  font-size: 12px;
  display: none;
  z-index: 100;
  cursor: pointer;
  transition: all 0.2s ease;
}
#agent-indicator.active {
  display: block;
  border-color: var(--color-agent-active);
  box-shadow: 0 0 10px rgba(0, 255, 0, 0.3);
}
#agent-indicator:hover {
  border-color: var(--color-agent-active);
  background: var(--panel);
}

/* Preset buttons */
.preset-btn {
  height: 27.2px;
  padding: 0 8.5px;
  margin-left: 4px;
  border: 1px solid var(--red);
  border-radius: 4px;
  background: var(--bg);
  color: var(--fg);
  font-family: inherit;
  font-size: 10.2px;
  cursor: pointer;
  transition: all 0.2s ease;
}

.preset-btn:hover {
  background: var(--panel);
  border-color: var(--fg);
}

.preset-btn.active {
  background: var(--panel);
  border-color: var(--red);
  box-shadow: 0 0 0 1px var(--red), 0 0 8px color-mix(in srgb, var(--red) 30%, transparent);
  font-weight: 600;
}

/* Custom preset modal — inherits from .modal base class */

/* Unified chat input area */
.chat-input-area {
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  margin-top: 12px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.chat-controls-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}

.chat-controls-left {
  display: flex;
  align-items: center;
  gap: 8px;
}

.chat-controls-right {
  display: flex;
  align-items: center;
  gap: 8px;
}

.control-group {
  display: flex;
  align-items: center;
  gap: 4px;
}

.control-label {
  font-size: 11px;
  color: var(--fg);
  opacity: 0.8;
}

.toggle-switch {
  position: relative;
  display: inline-block;
  width: 30px;
  height: 16px;
}

.toggle-switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.toggle-slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: color-mix(in srgb, var(--fg) 15%, transparent);
  border-radius: 8px;
  transition: background 0.08s;
}

.toggle-slider:before {
  position: absolute;
  content: "";
  height: 12px;
  width: 12px;
  left: 2px;
  top: 2px;
  background-color: var(--panel);
  border-radius: 50%;
  transition: transform 0.08s;
  box-shadow: 0 1px 2px rgba(0,0,0,0.25);
}

.toggle-switch input:checked + .toggle-slider {
  background-color: var(--toggle-active, var(--red));
}

.toggle-switch input:checked + .toggle-slider:before {
  transform: translateX(14px);
}

.preset-buttons-row {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.chat-input-form {
  display: flex;
  gap: 8px;
  align-items: flex-end;
}

#message {
  flex: 1;
  min-height: 34px;
  max-height: 120px;
  resize: none;
  font-size: 13px !important;
  overflow-y: auto !important;
  line-height: 1.4 !important;
  font-family: inherit !important;
}

.action-button {
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  margin: 0;
  background: none;
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
  color: var(--fg);
  transition: all 0.2s ease;
}

.action-button:hover {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border-color: var(--fg);
}

.action-button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.action-button.recording {
  background: var(--color-recording);
  border-color: var(--color-recording);
  animation: pulse 1.5s infinite;
}

@keyframes pulse {
  0% { opacity: 1; }
  50% { opacity: 0.7; }
  100% { opacity: 1; }
}

#stop-icon {
  display: none;
  width: 14px;
  height: 14px;
  background: var(--color-recording);
  border-radius: 2px;
}

/* Attachment strip — centered + max-width to match the chat-input-bar below,
   otherwise the chip floats flush-left while the input is centered (visible on
   desktop where the chat area is wider than 800px). */
.attach-strip {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  padding: 2px 8px;
  max-width: 800px;
  width: 100%;
  margin-left: auto;
  margin-right: auto;
  box-sizing: border-box;
}
.attach-strip:empty { display: none; }

/* Upload-in-progress feedback: the message bubble shows immediately, so while
   the files are still uploading we put a whirlpool ON each attachment chip and
   dim the chip's content — making it obvious that file is being sent, not stuck. */
.attach-strip.attach-uploading .thumb {
  position: relative;
  pointer-events: none;
}
.attach-strip.attach-uploading .thumb > :not(.thumb-upload-spinner) {
  opacity: 0.4;
}
.thumb-upload-spinner {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 3;
}

.thumb {
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--fg) 11%, transparent);
  padding: 3px 6px;
  font-size: 12px;
  display: flex;
  gap: 6px;
  align-items: center;
  border-radius: 4px;
  transition: all 0.2s ease;
  max-width: 180px;
}
.thumb-img {
  max-width: 60px;
  max-height: 40px;
  border-radius: 3px;
  object-fit: cover;
}
.attach-image-preview {
  margin: 4px 0;
}
.attach-image-preview img {
  box-shadow: 0 1px 4px rgba(0,0,0,0.2);
  /* Same border as the chat bubbles. */
  border: 1px solid var(--bubble-border, var(--border));
}
/* Image chips: image fills the chip, X overlays as a corner accent badge.
   Same on desktop and mobile — doc/text chips keep the beside-X layout. */
.thumb.thumb-image {
  position: relative;
  padding: 0;
}
.thumb.thumb-image .thumb-img {
  max-height: 56px;
  display: block;
}
.thumb.thumb-image button {
  position: absolute;
  /* Sit on the top-right corner edge as an accent badge. */
  top: -7px;
  right: -7px;
  width: 24px;
  height: 24px;
  min-width: 0;
  padding: 0;
  border: 2px solid var(--bg);
  border-radius: 50%;
  background: var(--accent-primary, var(--red));
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  line-height: 1;
  z-index: 3;
  transition: transform 0.12s ease, filter 0.12s ease, box-shadow 0.12s ease;
}
.thumb.thumb-image button:hover {
  transform: scale(1.12);
  filter: brightness(1.12);
  box-shadow: 0 2px 8px rgba(0,0,0,0.25);
}
.thumb.thumb-image button:active {
  transform: scale(0.96);
}
@media (max-width: 768px) {
  /* Collapsed "N files" badge: use the same corner-X accent badge as image thumbs. */
  .thumb-collapsed { position: relative; }
  .thumb-collapsed .thumb-collapsed-x {
    position: absolute;
    top: -7px;
    right: -7px;
    width: 24px;
    height: 24px;
    min-width: 0;
    padding: 0;
    border: 2px solid var(--bg);
    border-radius: 50%;
    background: var(--accent-primary, var(--red));
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 15px;
    line-height: 1;
    z-index: 3;
    opacity: 1;
  }
  /* Bigger remove-X tap target for non-image (doc/text) chips on mobile too. */
  .thumb button {
    height: 28px;
    min-width: 28px;
    font-size: 15px;
  }
}
.thumb span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.thumb:hover {
  background: color-mix(in srgb, var(--fg) 16%, transparent);
  border-color: var(--fg);
}

.thumb button {
  height: 24px;
  padding: 0 7px;
  font-size: 13px;
  border-radius: 4px;
  color: var(--accent-primary, var(--red));
}

.thumb-collapsed {
  cursor: pointer;
  color: var(--red);
  border-color: var(--red);
  background: color-mix(in srgb, var(--red) 10%, transparent);
  font-weight: 600;
  gap: 8px;
  border-radius: 999px;   /* pill — rounder than the square file chips */
  padding-left: 12px;
}
.thumb-collapsed:hover {
  background: color-mix(in srgb, var(--red) 20%, transparent);
}
.thumb-collapsed-label { white-space: nowrap; }
.thumb-collapsed-x {
  height: 24px; padding: 0 7px; font-size: 13px; border-radius: 4px;
  color: var(--accent-primary, var(--red));
  background: none; border: none; cursor: pointer; opacity: 0.6;
}
.thumb-collapsed-x:hover { opacity: 1; }

/* Recording indicator */
#recording-indicator {
  position: fixed;
  top: 10px;
  left: 0;
  right: 0;
  background: rgba(0, 0, 0, 0.8);
  backdrop-filter: blur(10px);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  margin: 10px;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: space-between;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

#recording-indicator.hidden {
  display: none !important;
}

.recording-content {
  display: flex;
  align-items: center;
  gap: 12px;
  color: white;
}

.recording-icon {
  color: var(--color-recording);
  font-size: 20px;
  animation: pulse 1.5s infinite;
}

.recording-text {
  font-size: 16px;
  font-weight: 500;
}

.stop-recording-btn {
  background: var(--color-recording);
  color: white;
  border: none;
  border-radius: 6px;
  padding: 6px 12px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.2s ease;
}

.stop-recording-btn:hover {
  background: var(--color-recording-hover);
}

#recording-indicator.error {
  background: rgba(173, 26, 26, 0.9);
}

.recording-error {
  color: var(--color-recording);
  font-size: 14px;
  margin-top: 4px;
}

/* Mermaid diagram containers */
.mermaid-container {
  margin: 12px 0;
  padding: 16px;
  background: color-mix(in srgb, var(--bg) 95%, var(--fg));
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow-x: auto;
  text-align: center;
}
.mermaid-container svg { max-width: 100%; height: auto; }

/* KaTeX math overrides */
.katex-display { margin: 0.8em 0; overflow-x: auto; overflow-y: hidden; }
.katex { font-size: 1.1em; }

/* Hide thinking sections globally via settings toggle */
body.hide-thinking .thinking-section { display: none !important; }

/* Thinking process styles — colors follow theme accent */
.msg .body .stream-content {
  width: 100%;
}
.thinking-section {
  margin: 12px 0;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--red) 5%, transparent);
  overflow: hidden;
  transition: all 0.3s ease;
}

.thinking-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 12px;
  cursor: pointer;
  user-select: none;
  background: color-mix(in srgb, var(--red) 8%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--red) 20%, transparent);
  transition: background 0.2s ease;
}

.thinking-header:hover {
  background: color-mix(in srgb, var(--red) 12%, transparent);
}

.thinking-header-left {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.9em;
  color: var(--red);
  font-weight: 500;
  overflow: hidden;
  min-width: 0;
}
.thinking-header-left span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: opacity 0.2s ease;
}

.thinking-icon {
  font-size: 1.1em;
}

.thinking-toggle {
  font-size: 0.9em;
  color: var(--red);
  transition: transform 0.3s ease;
}
.thinking-toggle::after {
  content: '\25BC'; /* ▼ */
}

.thinking-toggle.expanded {
  transform: rotate(180deg);
}

.thinking-content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease, padding 0.3s ease;
  padding: 0 12px;
}

.thinking-content.expanded {
  max-height: 300px;
  overflow-y: auto;
  padding: 12px;
}

.thinking-content-inner {
  font-size: 0.85em;
  color: var(--fg);
  opacity: 0.9;
  line-height: 1.5;
}
.live-reply-content {
  animation: fadeSlideIn 0.3s ease-out;
}
@keyframes fadeSlideIn {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Thinking indicator animation */
.thinking-indicator {
  display: flex;
  align-items: center;
  gap: 4px;
  color: var(--red);
  font-style: italic;
  padding: 8px 0;
}

.thinking-dots::after {
  content: '...';
  animation: thinking-dots 1.5s infinite;
  display: inline-block;
  width: 20px;
  text-align: left;
}

@keyframes thinking-dots {
  0%, 20% { content: '.'; }
  40% { content: '..'; }
  60%, 100% { content: '...'; }
}

.thinking-complete {
  color: var(--red);
  font-size: 0.9em;
  padding: 4px 0;
  opacity: 0.8;
}

/* ── Sources section — collapsible source citations ── */
.sources-section {
  margin: 8px 0 12px;
  border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
  border-radius: 8px;
  overflow: hidden;
  transition: all 0.3s ease;
}
.sources-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
  cursor: pointer;
  background: color-mix(in srgb, var(--red) 8%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--red) 20%, transparent);
  transition: background 0.2s ease;
  user-select: none;
}
.sources-header:hover {
  background: color-mix(in srgb, var(--red) 12%, transparent);
}
.sources-header-left {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85em;
  font-weight: 500;
  color: var(--red);
}
.sources-header-left svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  opacity: 0.7;
}
.sources-toggle {
  font-size: 0.8em;
  color: var(--red);
  opacity: 0.7;
  transition: none;
}
.sources-toggle::after {
  content: '\25B6'; /* ▶ right arrow */
}
.sources-toggle[data-arrow="down"]::after {
  content: '\25BC'; /* ▼ down arrow */
}
.sources-content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease, padding 0.3s ease;
  padding: 0 10px;
}
.sources-content.expanded {
  max-height: 3000px;
  padding: 8px 10px;
}
.sources-content-inner {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.source-link {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border-radius: 6px;
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  text-decoration: none;
  color: var(--fg);
  transition: background 0.15s ease;
}
.source-link:hover {
  background: color-mix(in srgb, var(--fg) 10%, transparent);
}
.source-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--fg) 15%, transparent);
  color: var(--fg);
  font-size: 0.7em;
  font-weight: 600;
  flex-shrink: 0;
}
.source-title {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.82em;
}
.source-domain {
  font-size: 0.72em;
  opacity: 0.45;
  flex-shrink: 0;
}

/* ── Processing pulse animation (reused by session-star) ── */
@keyframes research-pulse {
  0%, 100% { opacity: 0.3; transform: scale(0.8); }
  50% { opacity: 1; transform: scale(1.2); }
}
.ai-spinner {
  color: var(--red);
}
/* Nudge the Tidy button 2px left. */
#memory-tidy-btn { position: relative; left: -2px; }
/* Tidy button's whirlpool nudge — sits 1px lower so it visually centers on
   the Tidy label baseline. */
#memory-tidy-btn .ai-spinner-whirlpool,
#memory-tidy-btn .spinner-whirlpool {
  position: relative;
  top: 1px;
}
.list-item.stream-complete {
  animation: stream-complete-pulse 2s ease-in-out infinite;
}
.cookbook-notif-active svg { opacity: 1 !important; }

/* Rail notification dot — pulsing indicator on icon-rail buttons */
.icon-rail-btn.rail-notify {
  opacity: 1 !important;
  position: relative;
}
.icon-rail-btn.rail-notify::before {
  content: '';
  position: absolute;
  top: 4px;
  right: 4px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent, var(--red));
  animation: rail-notif-pulse 2s ease-in-out infinite;
  z-index: 1;
}
.icon-rail-btn.rail-notify.rail-notify-success::before {
  background: var(--color-success, #4caf50);
}
@keyframes rail-notif-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.8); }
}
@keyframes stream-complete-pulse {
  0%, 100% { box-shadow: none; }
  50% { box-shadow: inset 0 0 0 1.5px var(--accent); }
}

/* ===== SLASH COMMAND RESPONSES ===== */
.msg.msg-system {
  padding: 6px 12px; margin: 4px auto 4px 8px;
  max-width: 85%; background: none; border-left: 2px solid var(--border);
}
.msg.msg-system .body { padding: 0; }
.msg.msg-system pre { margin: 4px 0; white-space: pre-wrap; font-size: 0.85em; }
.msg.stream-done-toast {
  cursor: pointer;
  border-left-color: var(--accent, var(--border));
  border-radius: 6px;
  background: color-mix(in srgb, var(--accent) 6%, transparent);
  transition: background 0.15s;
}
.msg.stream-done-toast:hover {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.msg.stream-done-toast .body {
  display: flex;
  align-items: center;
  gap: 8px;
}
.stream-done-indicator {
  font-family: monospace;
  font-size: 1.1em;
  line-height: 1;
  color: var(--accent);
  flex-shrink: 0;
  animation: bar-pulse 1.2s ease-in-out infinite;
}
@keyframes bar-pulse {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 1; }
}

/* ===== AGENT MULTI-BUBBLE ===== */
.msg.msg-tool {
  display: none; /* legacy — hidden, replaced by agent-thread */
}
.msg.msg-continuation {
  margin-top: 2px;
}

/* ===== AGENT THREAD TIMELINE ===== */
.agent-thread {
  position: relative;
  margin: 2px 0 2px 28px;
  padding: 4px 0 4px 22px;
  max-width: calc(85% - 20px);
  box-sizing: border-box;
}
.agent-thread::before {
  content: '';
  position: absolute;
  left: 5px;
  top: 14px;
  bottom: 14px;
  width: 2px;
  background: color-mix(in srgb, var(--red) 18%, transparent);
  border-radius: 1px;
}
/* Extend line to connect to chat bubble above/below */
.agent-thread.has-top::before {
  top: -6.5px;
}
.agent-thread.has-bottom::before {
  bottom: -5px;
}
/* Terminating dot at bottom when no bubble below and last node is expanded */
.agent-thread:not(.has-bottom) .agent-thread-node.open:last-child::after {
  content: '';
  position: absolute;
  /* -17px (was -15) nudges the terminating dot 2px further left so it
     sits flush with the thread's left rail when the search node is
     expanded. This is the "big glow dot at the bottom" the user sees
     after a web_search step. */
  left: -17px;
  bottom: 5px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 4px 1px color-mix(in srgb, var(--red) 55%, transparent),
              0 0 0 3px color-mix(in srgb, var(--red) 18%, transparent);
}

/* Synapse pulse — a bright dot traveling down the line */
.agent-thread::after {
  content: '';
  position: absolute;
  left: 4px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 3px 1px color-mix(in srgb, var(--red) 50%, transparent);
  pointer-events: none;
  top: 0%;
  opacity: 0;
}
.agent-thread.streaming::after {
  animation: synapse-capped-short 0.8s ease-in-out infinite;
}
.agent-thread.streaming.has-top::after {
  animation: synapse-capped 0.8s ease-in-out infinite;
}
.agent-thread.streaming.has-bottom::after {
  animation: synapse-travel-short 0.8s ease-in-out infinite;
}
.agent-thread.streaming.has-top.has-bottom::after {
  animation: synapse-travel 0.8s ease-in-out infinite;
}
@keyframes synapse-travel {
  0% { top: 0%; opacity: 0; }
  5% { opacity: 0.5; }
  85% { opacity: 0.35; }
  100% { top: 100%; opacity: 0; }
}
@keyframes synapse-capped {
  0% { top: 0%; opacity: 0; }
  5% { opacity: 0.5; }
  70% { opacity: 0.35; top: calc(100% - 20px); }
  100% { opacity: 0; top: calc(100% - 20px); }
}
@keyframes synapse-travel-short {
  0% { top: 14px; opacity: 0; }
  5% { opacity: 0.5; }
  85% { opacity: 0.35; }
  100% { top: 100%; opacity: 0; }
}
@keyframes synapse-capped-short {
  0% { top: 14px; opacity: 0; }
  5% { opacity: 0.5; }
  70% { opacity: 0.35; top: calc(100% - 20px); }
  100% { opacity: 0; top: calc(100% - 20px); }
}
.agent-thread-node {
  position: relative;
  padding: 5px 0;
}
.agent-thread-node + .agent-thread-node {
  margin-top: 2px;
}
.agent-thread-dot {
  position: absolute;
  left: -20px;
  top: 10px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--red);
  border: 2px solid var(--bg);
  z-index: 1;
}
.agent-thread-node.running .agent-thread-dot {
  background: var(--red);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--red) 25%, transparent);
  animation: thread-pulse 1.5s ease-in-out infinite;
  top: 10px;
}
@keyframes thread-pulse {
  0%, 100% { box-shadow: 0 0 0 2px color-mix(in srgb, var(--red) 20%, transparent); }
  50% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--red) 10%, transparent); }
}
.agent-thread-node.error .agent-thread-dot {
  background: var(--color-error);
}
.agent-thread-header {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 0.85em;
  color: color-mix(in srgb, var(--fg) 70%, transparent);
  user-select: none;
  padding: 2px 0;
}
.agent-thread-header:hover {
  color: var(--fg);
}
.agent-thread-icon {
  font-size: 0.9em;
  color: var(--red);
}
.agent-thread-node.error .agent-thread-icon {
  color: var(--color-error);
}
.agent-thread-tool {
  font-weight: 600;
  color: var(--red);
  text-transform: uppercase;
  letter-spacing: 0.3px;
  font-size: 0.9em;
}
.agent-thread-status {
  font-size: 0.85em;
  opacity: 0.5;
}
.agent-thread-chevron {
  font-size: 0.7em;
  transition: transform 0.2s ease;
  opacity: 0.4;
}
.agent-thread-node.open .agent-thread-chevron {
  transform: rotate(90deg);
}
.agent-thread-wave {
  font-family: monospace;
  font-size: 0.85em;
  color: var(--red);
  letter-spacing: -1px;
}
/* Live "cooking" timer on a running tool — prominent (accent, tabular) so a
   long-running command always reads as alive, not frozen. */
.agent-thread-elapsed {
  margin: 0 6px 0 4px;
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--accent, var(--red));
}
/* Stall watchdog banner — shown when the stream has been silent for ~1min. */
.stall-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 8px auto;
  padding: 8px 12px;
  max-width: 90%;
  font-size: 12px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--color-warning, #f0ad4e) 12%, var(--bg));
  border: 1px solid color-mix(in srgb, var(--color-warning, #f0ad4e) 40%, transparent);
}
.stall-banner-txt { flex: 1; opacity: 0.85; }
.stall-banner-btn {
  font-size: 11px;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: 6px;
  border: none;
  background: var(--accent, var(--red));
  color: #fff;
  cursor: pointer;
  flex-shrink: 0;
}
.stall-banner-stop {
  background: none;
  color: var(--fg);
  border: 1px solid var(--border);
}
.agent-thread-content {
  display: none;
  padding: 4px 0 2px 0;
  overflow-x: auto;
  overflow-y: hidden;
}
.agent-thread-node.open .agent-thread-content {
  display: block;
}
.agent-thread-cmd {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 12px;
  margin: 4px 0;
  color: var(--fg);
  font-size: 0.85em;
  white-space: pre-wrap;
  word-break: break-word;
  line-height: 1.4;
  overflow-x: auto;
}

/* Mobile: constrain thread content */
@media (max-width: 768px) {
  .agent-thread {
    margin-left: 16px;
    padding-left: 18px;
    max-width: calc(90% - 16px);
  }
  .agent-thread-cmd {
    font-size: 0.78em;
    padding: 6px 8px;
    max-width: 100%;
    overflow-x: auto;
  }
  .agent-thread-content {
    max-width: calc(100vw - 90px);
  }
  .agent-tool-output pre {
    font-size: 0.8em;
    max-width: 100%;
    overflow-x: auto;
  }
  .agent-thread-header {
    font-size: 0.8em;
  }
  .agent-thread-dot {
    left: -16px;
    top: 10px;
  }
}

/* ===== AGENT TOOL OUTPUT (inside thread nodes) ===== */
.agent-tool-output {
  margin-top: 8px;
  background: color-mix(in srgb, var(--red) 5%, transparent);
  border: 1px solid color-mix(in srgb, var(--red) 20%, transparent);
  border-radius: 8px;
  overflow: hidden;
}
.agent-tool-output summary {
  color: var(--red);
  background: color-mix(in srgb, var(--red) 10%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--red) 15%, transparent);
  cursor: pointer;
  font-size: 0.85em;
  user-select: none;
  padding: 6px 10px;
  font-weight: 500;
  /* Chevron on the right (like the thinking section) instead of the default
     left-side disclosure triangle. */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  list-style: none;
}
.agent-tool-output summary::-webkit-details-marker { display: none; }
/* Suppress the global `summary::before { content: '▶' }` left arrow — this
   section uses a right-side chevron instead. */
.agent-tool-output summary::before { content: none; }
.agent-tool-output summary::after {
  content: '\25BC'; /* ▼ */
  color: var(--red);
  font-size: 0.9em;
  transition: transform 0.3s ease;
}
.agent-tool-output[open] > summary::after {
  transform: rotate(180deg);
}
.agent-tool-output summary:hover {
  background: color-mix(in srgb, var(--red) 15%, transparent);
}
.agent-thinking-dots .ai-spinner {
  font-size: 12px;
  letter-spacing: 0.5px;
}

.agent-tool-output[open] {
  background: color-mix(in srgb, var(--red) 6%, transparent);
}
.agent-tool-output[open] > :not(summary) {
  animation: detail-reveal 0.25s ease-out both;
}
@keyframes detail-reveal {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.agent-tool-output pre {
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 10px 14px;
  margin-top: 0;
  max-height: 300px;
  overflow-y: auto;
  font-size: 0.95em;
  color: var(--fg);
  opacity: 0.85;
  white-space: pre-wrap;
  word-break: break-all;
  line-height: 1.5;
}

/* Mobile responsive styles */
@media (max-width: 768px) {
  #recording-indicator {
    margin: 8px;
    padding: 10px;
  }
  
  .recording-text {
    font-size: 14px;
  }
  
  .stop-recording-btn {
    padding: 6px 10px;
    font-size: 12px;
  }
}
/* ===== PANE STYLES (shared by compare) ===== */

.pane-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 0 4px;
  border-bottom: 1px solid var(--border);
}
.close-split-btn,
.pane-close-btn {
  background: none;
  border: none;
  color: var(--color-error);
  font-size: 18px;
  cursor: pointer;
  padding: 0 4px;
  line-height: 1;
  opacity: 0;
  transition: opacity 0.15s;
}
.pane-header:hover .close-split-btn,
.pane-header:hover .pane-close-btn {
  opacity: 1;
}
.close-split-btn:hover,
.pane-close-btn:hover {
  color: var(--color-error-light);
}


/* ============================================ */
/* RESEARCH DETAILS EXPANDABLE SECTION - UPDATED */
/* ============================================ */

/* Style the details element */
details {
  background: color-mix(in srgb, var(--hl-string) 5%, transparent);
  border: 1px solid color-mix(in srgb, var(--hl-string) 30%, transparent);
  border-radius: 8px;
  margin: 12px 0;
  padding: 0;
  overflow: hidden;
  transition: all 0.3s ease;
}

details[open] {
  background: color-mix(in srgb, var(--hl-string) 8%, transparent);
}
details[open] > :not(summary) {
  animation: detail-reveal 0.25s ease-out both;
}

/* Style the summary (clickable header) - NO CURSIVE, NORMAL SIZE */
summary {
  cursor: pointer;
  padding: 10px 14px;
  background: color-mix(in srgb, var(--hl-string) 10%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--hl-string) 20%, transparent);
  font-weight: 500;
  font-size: 0.95em;
  font-style: normal;
  font-family: inherit;
  color: var(--hl-string);
  user-select: none;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
  transition: background 0.2s ease;
}

summary:hover {
  background: color-mix(in srgb, var(--hl-string) 15%, transparent);
}

/* Add custom arrow */
summary::before {
  content: '▶';
  display: inline-block;
  transition: transform 0.3s ease;
  font-size: 0.75em;  /* Smaller arrow */
}

details[open] summary::before {
  transform: rotate(90deg);
}

/* Hide default marker in webkit browsers */
summary::-webkit-details-marker {
  display: none;
}

/* Style the content inside details - SMALLER, MORE COMPACT */
details > div,
details > p,
details > ul,
details > ol {
  padding: 12px 14px;  /* Less padding */
  animation: fadeIn 0.3s ease;
  font-size: 0.9em;  /* Smaller text */
  line-height: 1.5;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Style research findings inside details - SMALLER HEADINGS */
details h3 {
  margin-top: 12px;
  margin-bottom: 6px;
  color: var(--hl-string);
  font-size: 0.95em;
  font-weight: 500;
}

details h4 {
  margin-top: 10px;
  margin-bottom: 5px;
  color: var(--hl-string);
  font-size: 0.9em;
  font-weight: 500;
}

details ul {
  margin-left: 18px;
  margin-bottom: 10px;
}

details li {
  margin-bottom: 5px;
  line-height: 1.4;
  font-size: 0.85em;  /* Smaller list items */
}

details strong {
  color: var(--hl-string);
  font-weight: 500;
}

details a {
  color: var(--accent);
  text-decoration: none;
  transition: color 0.2s ease;
}

details a:hover {
  color: var(--color-link-hover);
  text-decoration: underline;
}

/* Research metadata - SMALLER */
details .research-meta {
  font-size: 0.8em;
  color: var(--fg);
  opacity: 0.8;
  margin-top: 4px;
}

/* Source links - SMALLER */
details .source-link {
  display: inline-block;
  margin-top: 3px;
  padding: 2px 5px;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: 4px;
  font-size: 0.75em;
  color: var(--accent);
}

details .source-link:hover {
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  border-color: var(--accent);
}

/* Research report links - clean and subtle */
details a {
  color: var(--accent);
  text-decoration: none;
  font-weight: normal;  /* Remove bold */
  font-size: 0.85em;
  opacity: 0.9;
  transition: all 0.2s ease;
  word-break: break-all;  /* Break long URLs nicely */
}

details a:hover {
  color: var(--color-link-hover);
  text-decoration: underline;
  opacity: 1;
}

/* ============================================ */
/* CUSTOM SYNTAX HIGHLIGHTING - Theme-Reactive  */
/* ============================================ */
.hljs {
  background: var(--code-bg, var(--hl-bg, var(--panel)));
  color: var(--code-fg, var(--hl-fg, var(--fg)));
  padding: 8px;
  border-radius: 4px;
}

/* Keywords & control flow — purple/magenta */
.hljs-keyword,
.hljs-selector-tag { color: var(--hl-keyword); }

/* Strings & regex — warm yellow/orange */
.hljs-string,
.hljs-regexp,
.hljs-addition { color: var(--hl-string); }

/* Comments & docs — muted. font-style intentionally omitted: italics shift
   glyph widths in the highlight overlay relative to the transparent textarea
   above it, which makes the caret drift away from the visible character. */
.hljs-comment,
.hljs-quote,
.hljs-meta { color: var(--hl-comment); }

/* Functions & method names — blue */
.hljs-function,
.hljs-title,
.hljs-title.function_,
.hljs-section { color: var(--hl-function); }

/* Numbers & constants — distinct from strings */
.hljs-number,
.hljs-literal { color: var(--hl-number, var(--hl-string)); }

/* Built-ins & types — teal/cyan tint */
.hljs-built_in,
.hljs-type,
.hljs-class,
.hljs-title.class_ { color: var(--hl-builtin, var(--hl-function)); }

/* Variables & identifiers — fg with slight distinction */
.hljs-variable,
.hljs-template-variable,
.hljs-attr { color: var(--hl-variable, var(--hl-fg, var(--fg))); }

/* Operators & punctuation — slightly dimmed fg */
.hljs-operator,
.hljs-punctuation { color: var(--hl-fg, var(--fg)); opacity: 0.8; }

/* Parameters */
.hljs-params { color: var(--hl-params, var(--hl-fg, var(--fg))); }

/* Property access, attributes in HTML/XML */
.hljs-property,
.hljs-selector-class,
.hljs-selector-id { color: var(--hl-variable, var(--hl-function)); }

/* Tags (HTML) */
.hljs-tag { color: var(--hl-keyword); }
.hljs-name { color: var(--hl-keyword); }

/* Deletion/diff */
.hljs-deletion { color: var(--red); }

/* Symbol, special */
.hljs-symbol { color: var(--hl-string); }
.hljs-link { color: var(--hl-function); text-decoration: underline; }

/* Emphasis — applied globally (chat messages, rendered markdown in docs
   preview, etc.) but explicitly NEUTRALIZED in the doc editor's overlay so
   the textarea-and-overlay caret alignment stays bit-perfect. Bold / italic
   shift glyph widths and that drifts the click-to-row mapping. */
.hljs-emphasis { font-style: italic; }
.hljs-strong { font-weight: bold; }
.doc-editor-highlight .hljs-emphasis,
.doc-editor-highlight .hljs-strong {
  font-style: normal !important;
  font-weight: inherit !important;
}

/* ===== Markdown highlighting — document editor =====
   IMPORTANT: this is an overlay layered behind a transparent textarea, so
   every styled token must occupy the EXACT same width as the corresponding
   characters in the textarea. Anything that changes glyph metrics
   (font-weight/style, padding, border, letter-spacing) makes the caret drift
   away from the rendered glyph underneath. Color / background / text-
   decoration are safe — they don't change layout width. */
.doc-editor-highlight .language-markdown .hljs-section {
  color: var(--hl-keyword, #c678dd);
}
.doc-editor-highlight .language-markdown .hljs-strong {
  color: var(--hl-number, #d19a66);
}
.doc-editor-highlight .language-markdown .hljs-emphasis {
  color: var(--hl-string, #e5c07b);
}
.doc-editor-highlight .language-markdown .hljs-bullet {
  color: var(--hl-builtin, #56b6c2);
}
.doc-editor-highlight .language-markdown .hljs-code {
  color: var(--hl-builtin, #56b6c2);
  background: color-mix(in srgb, var(--hl-builtin, #56b6c2) 10%, transparent);
  border-radius: 2px;
}
.doc-editor-highlight .language-markdown .hljs-link {
  color: var(--hl-function, #61afef);
  text-decoration: underline;
}
.doc-editor-highlight .language-markdown .hljs-quote {
  color: var(--hl-comment, #828997);
}
.doc-editor-highlight .language-markdown .hljs-symbol {
  color: var(--red);
}
/* Standalone [bracketed text] — scene directions, annotations */
.doc-editor-highlight .language-markdown .md-bracket {
  color: var(--hl-builtin, #56b6c2);
  opacity: 0.85;
}
/* Heading # markers — dimmer than the heading text */
.doc-editor-highlight .language-markdown .md-heading-marker {
  color: var(--hl-comment, #828997);
  font-weight: 400;
}

/* ===== CUSTOM PRESET MODAL ===== */

.preset-modal-content {
  width: min(460px, 90vw);
  border-radius: 12px;
  overflow: hidden;
}
.preset-modal-body {
  display: flex;
  flex-direction: column;
  overflow-x: hidden;
}

/* Footer Start/Cancel buttons get a leading icon. Done via ::before + a masked
   SVG (not an inline <svg> child) because the labels are set with .textContent,
   which would wipe a child element on every tab switch. background:currentColor
   makes the icon follow the button's text color. */
#save-custom-preset,
#cancel-custom-preset {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
#save-custom-preset::before,
#cancel-custom-preset::before {
  content: "";
  width: 13px; height: 13px;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: var(--_btn-ic) center / contain no-repeat;
  mask: var(--_btn-ic) center / contain no-repeat;
}
#save-custom-preset::before {
  --_btn-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpolygon points='5 3 19 12 5 21 5 3'/%3E%3C/svg%3E");
}
#cancel-custom-preset::before {
  --_btn-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3C/svg%3E");
}

.preset-tabs {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--border);
  margin: 0 -10px 12px;
  padding: 0 10px;
  margin: 0 -16px;
  padding: 0 16px;
  margin-bottom: 12px;
}

.preset-tab {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 10px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--color-muted);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.15s;
}
.preset-tab-icon { flex-shrink: 0; }
/* On narrow widths the icon + label can crowd 4 tabs — drop the labels to
   icon-only so the row stays clean. */
@media (max-width: 460px) {
  .preset-tab span { display: none; }
}

.preset-tab:hover {
  color: var(--fg);
  background: color-mix(in srgb, var(--fg) 4%, transparent);
}

.preset-tab.active {
  color: var(--red);
  border-bottom-color: var(--red);
}

.preset-tab-content {
  overflow: hidden;
}
.preset-tab-content.hidden {
  display: none;
}

.preset-templates-hint {
  font-size: 11px;
  color: var(--color-muted);
  margin: 0 0 8px;
}

.prompt-templates-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 300px;
  overflow-y: auto;
}

.prompt-template-card {
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  cursor: pointer;
  transition: all 0.15s;
}

.prompt-template-card:hover {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
}

.prompt-template-card.selected {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.prompt-template-name {
  font-size: 12px;
  font-weight: 600;
  color: var(--fg);
  margin-bottom: 4px;
}

.prompt-template-preview {
  font-size: 11px;
  color: var(--color-muted);
  line-height: 1.4;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.preset-slider-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 12px;
  margin-bottom: 6px;
}

.preset-slider-row label {
  font-size: 13px;
  color: var(--fg);
  font-weight: 500;
  margin: 0;
}

.preset-slider-value {
  font-size: 12px;
  color: var(--fg);
  font-weight: 600;
  min-width: 40px;
  text-align: right;
}

.preset-range {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 6px;
  background: var(--border);
  border-radius: 4px;
  outline: none;
  margin-bottom: 4px;
  box-sizing: border-box;
  display: block;
  padding: 0;
  margin-left: 0;
  margin-right: 0;
}

.preset-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--red, var(--fg));
  cursor: pointer;
  border: 2px solid var(--panel);
  box-shadow: 0 1px 4px rgba(0,0,0,0.2);
}

.preset-range::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--red, var(--fg));
  cursor: pointer;
  border: 2px solid var(--panel);
  box-shadow: 0 1px 4px rgba(0,0,0,0.2);
}

.preset-range::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 4px;
}

.preset-range::-moz-range-track {
  height: 6px;
  background: var(--border);
  border-radius: 4px;
}

.preset-temp-hints {
  display: flex;
  font-size: 10px;
  color: var(--color-muted);
  margin-top: -4px;
  margin-bottom: 10px;
  padding: 0 2px;
  opacity: 0.7;
}
.preset-temp-hints span {
  flex: 1;
}
.preset-temp-hints span:nth-child(2) {
  text-align: center;
}
.preset-temp-hints span:last-child {
  text-align: right;
}

.preset-clear-btn {
  padding: 7px 14px;
  background: none;
  border: 1px solid var(--border);
  color: var(--color-muted);
  border-radius: 6px;
  cursor: pointer;
  font-size: 12px;
  font-weight: 500;
  transition: all 0.15s;
}

.preset-clear-btn:hover {
  color: var(--color-error);
  border-color: var(--color-error);
}

.preset-hint-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 1px solid var(--border);
  font-size: 10px;
  font-weight: 600;
  color: var(--color-muted);
  cursor: help;
  vertical-align: middle;
  margin-left: 4px;
  transition: all 0.15s;
}
.preset-hint-icon:hover {
  color: var(--fg);
  border-color: var(--fg);
}

.preset-section-header {
  font-size: 11px;
  font-weight: 600;
  color: var(--color-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 6px;
  padding: 0 2px;
}

.user-template-card {
  position: relative;
}
.user-template-delete {
  background: none;
  border: none;
  color: var(--color-muted);
  font-size: 13px;
  cursor: pointer;
  padding: 0 2px;
  line-height: 1;
  opacity: 0;
  transition: opacity 0.15s, color 0.15s;
}
.user-template-card:hover .user-template-delete {
  opacity: 1;
}
.user-template-delete:hover {
  color: var(--color-error);
}

.preset-save-template-btn {
  padding: 7px 14px;
  border-radius: 6px;
  font-size: 12px;
  font-weight: 500;
  border: 1px solid var(--border);
  background: none;
  color: var(--fg);
  cursor: pointer;
  transition: all 0.15s;
}
.preset-save-template-btn:hover {
  border-color: var(--accent, var(--red));
  color: var(--accent, var(--red));
}

.char-prompt-wrap {
  position: relative;
}
.char-prompt-wrap textarea {
  padding-bottom: 28px;
}
.char-expand-btn {
  position: absolute;
  bottom: 14px;
  right: 6px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--color-muted);
  font-size: 11px;
  padding: 2px 8px;
  cursor: pointer;
  transition: all 0.15s;
  margin: 0;
  z-index: 1;
}
.char-expand-btn:hover {
  color: var(--red);
  border-color: var(--red);
}
.char-expand-btn.expanding {
  opacity: 0.5;
  pointer-events: none;
}

/* Memory scope bar (My Memories / Characters) */
.memory-scope-bar {
  display: flex;
  gap: 0;
  margin: 0 0 8px 0 !important;
  padding: 0 !important;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.memory-scope-btn {
  flex: 1;
  padding: 8px 12px !important;
  margin: 0 !important;
  font-size: 13px !important;
  font-weight: 600;
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.5;
  cursor: pointer;
  transition: all 0.15s;
}
.memory-scope-btn + .memory-scope-btn {
  border-left: 1px solid var(--border);
}
.memory-scope-btn.active {
  background: color-mix(in srgb, var(--red) 12%, transparent);
  color: var(--red);
  opacity: 1;
}
.memory-scope-btn:hover:not(.active) {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
}
.memory-char-list {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.memory-char-chip {
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 500;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: none;
  color: var(--fg);
  cursor: pointer;
  transition: all 0.15s;
  margin: 0;
}
.memory-char-chip.active {
  background: color-mix(in srgb, var(--red) 12%, transparent);
  border-color: var(--red);
  color: var(--red);
}
.memory-char-chip:hover:not(.active) {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
}

/* Disabled state dims the form */
#char-fields-wrap.disabled {
  opacity: 0.35;
  pointer-events: none;
  filter: grayscale(0.5);
  transition: opacity 0.2s, filter 0.2s;
}
#char-fields-wrap {
  transition: opacity 0.2s, filter 0.2s;
}

/* Name combo: input + delete btn */
.char-name-combo {
  display: flex;
  gap: 4px;
  align-items: center;
  margin-bottom: 8px;
}
.char-name-combo input,
.char-name-combo select {
  flex: 1;
}
.char-template-select {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  padding: 6px 8px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
}
.char-template-select:focus {
  outline: none;
  border-color: var(--red);
}
.char-action-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--color-muted);
  font-size: 11px;
  padding: 4px 8px;
  cursor: pointer;
  transition: all 0.15s;
  flex-shrink: 0;
  margin: 0 !important;
  white-space: nowrap;
  /* Uniform width so the trailing button column matches between the select
     row (+ New) and the name row (Reset) — that keeps the select and the
     name input the same width, since both fields flex:1 into the leftover. */
  min-width: 64px;
  text-align: center;
}
.char-action-btn:hover {
  color: var(--fg);
  border-color: var(--fg);
}
#char-delete-template-btn:hover {
  color: var(--color-error);
  border-color: var(--color-error);
}

/* Character toggle row in preset modal */
.preset-toggle-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 12px;
  font-size: 13px;
  color: var(--fg);
}
.preset-sub-option {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  padding: 8px 10px;
  font-size: 12px;
  color: var(--color-muted);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.preset-mem-choice {
  flex: 1;
  padding: 6px 10px;
  font-size: 12px;
  font-weight: 500;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: none;
  color: var(--fg);
  cursor: pointer;
  transition: all 0.15s;
  margin: 0;
}
.preset-mem-choice.active {
  background: color-mix(in srgb, var(--red) 12%, transparent);
  border-color: var(--red);
  color: var(--red);
}
.preset-mem-choice:hover:not(.active) {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
}


/* ===== MEMORY MODAL ===== */

.memory-modal-content {
  width: min(560px, 90vw);
  max-height: 78vh;
  font-size: 12px;
  overflow: hidden;   /* clip both axes; the inner .memory-modal-body owns scrolling.
                         (overflow-x alone promotes overflow-y to `auto` → a stray vertical scrollbar) */
  /* Subtle synapse-pulse — a soft radial glow that breathes in/out.
     Layered over the existing modal bg so it shows through without
     overpowering content. */
  position: relative;
  isolation: isolate;
}
.memory-modal-content::before { content: none; }
@keyframes memory-synapse-pulse {
  0%, 100% { opacity: 0.35; transform: scale(1); }
  50%      { opacity: 0.65; transform: scale(1.02); }
}
@media (prefers-reduced-motion: reduce) {
  .memory-modal-content::before { animation: none; opacity: 0.4; }
}
.memory-modal-content .modal-header h4 {
  font-size: 1rem;
}

.memory-modal-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow-y: auto;
  overflow-x: hidden;        /* Stop synapse-pulse pseudo-elements from triggering a sideways scrollbar */
  overscroll-behavior: contain;
  min-height: 0;
  /* Fill the modal-content's height so the flex chain (tab-panel → admin-card
     → list → expanded card) is bounded. Without this the chain grows to
     content, so an expanded skill card pushed its footer off-screen; capping
     the preview then left it floating too high. Bounding here lets the
     preview flex to fill and the footer pin to the bottom naturally. */
  flex: 1 1 auto;
}
.memory-tabs {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--border);
  margin: -4px -4px 0;
  padding: 0 4px;
  flex-shrink: 0;
}
.memory-tab {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.5;
  font-size: 12px;
  font-family: inherit;
  padding: 8px 14px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: opacity 0.15s, border-color 0.15s, color 0.15s, background 0.15s;
}
.memory-tab:hover {
  opacity: 0.8;
  background: color-mix(in srgb, var(--fg) 5%, transparent);
}
.memory-tab.active {
  opacity: 1;
  color: var(--red);
  border-bottom-color: var(--red);
}
.memory-tab-panel {
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow-y: auto;
  /* overflow-y:auto makes the browser compute overflow-x to `auto` too, which
     produced a stray horizontal scrollbar whenever a child was slightly too
     wide (long unbroken memory text, or the skills two-column row). Clip X
     explicitly — inner code blocks keep their own overflow-x:auto. */
  overflow-x: hidden;
  flex: 1;
  min-width: 0;
  min-height: 0;
}
.memory-tab-panel.hidden { display: none; }
/* Browse: bounded flex column so #memory-list gets remaining height (not 0px).
   height:min(78vh,max-content) gives a definite cap when long, natural height
   when short. flex-basis:auto (not 0) on the list avoids collapse in auto-sized
   parents. Toolbar siblings are flex-shrink:0; only #memory-list grows. */
#memory-modal .memory-modal-content:has(
  .memory-tab-panel[data-memory-panel="browse"]:not(.hidden)
) {
  display: flex;
  flex-direction: column;
  max-height: 78vh;
  height: min(78vh, max-content);
  overflow: hidden;
}
#memory-modal .memory-modal-content:has(
  .memory-tab-panel[data-memory-panel="browse"]:not(.hidden)
) .modal-header,
#memory-modal .memory-modal-content:has(
  .memory-tab-panel[data-memory-panel="browse"]:not(.hidden)
) .memory-tabs {
  flex: 0 0 auto;
}
#memory-modal .memory-modal-content:has(
  .memory-tab-panel[data-memory-panel="browse"]:not(.hidden)
) .memory-modal-body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}
#memory-modal .memory-tab-panel[data-memory-panel="browse"] {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}
#memory-modal .memory-tab-panel[data-memory-panel="browse"] > .admin-card {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}
#memory-modal .memory-tab-panel[data-memory-panel="browse"] > .admin-card > *:not(#memory-list):not(#memory-suggestions-body) {
  flex: 0 0 auto;
}
#memory-modal .memory-tab-panel[data-memory-panel="browse"] #memory-list:not(.hidden),
#memory-modal .memory-tab-panel[data-memory-panel="browse"] #memory-suggestions-body:not(.hidden) {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
#memory-modal .memory-tab-panel[data-memory-panel="browse"] #memory-suggestions-body:not(.hidden) .memory-suggestions-header {
  flex-shrink: 0;
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--bg);
}
#memory-modal .memory-tab-panel[data-memory-panel].hidden {
  display: none;
}
/* Settings cards dim + mute when their toggle is OFF (matches the
   .memory-toolbar-toggle "off" treatment elsewhere). */
#memory-modal .memory-tab-panel[data-memory-panel="settings"] .admin-card {
  transition: opacity 0.15s, border-color 0.15s, background 0.15s;
}
#memory-modal .memory-tab-panel[data-memory-panel="settings"] .admin-card:has(.admin-switch input:not(:checked)) {
  opacity: 0.55;
  border-color: color-mix(in srgb, var(--fg) 8%, transparent);
  background: color-mix(in srgb, var(--fg) 2%, transparent);
}
/* Skills tab — two-column layout: skills list (left, wider) + Add Skill
   form (right, narrower). Collapses to a single column on narrow screens. */
.memory-tab-panel[data-memory-panel="skills"] {
  flex-direction: row;
  align-items: stretch;
  gap: 10px;
}
.memory-tab-panel[data-memory-panel="skills"] > .admin-card:first-of-type {
  flex: 2 1 0;
  min-width: 0;
}
.memory-tab-panel[data-memory-panel="skills"] > .admin-card:nth-of-type(2) {
  flex: 1 1 0;
  margin-top: 0 !important;
  min-width: 220px;
}
@media (max-width: 640px) {
  .memory-tab-panel[data-memory-panel="skills"] { flex-direction: column; }
  .memory-tab-panel[data-memory-panel="skills"] > .admin-card:nth-of-type(2) {
    margin-top: 12px !important;
  }
}
.memory-desc {
  margin: 0;
  font-size: 11px;
  line-height: 1.5;
  /* 65% keeps this description text above WCAG AA 4.5:1 (50% was ~3.9:1). */
  color: color-mix(in srgb, var(--fg) 65%, transparent);
}

.memory-add-row {
  display: flex;
  gap: 6px;
  align-items: center;
  height: 32px;
}

.memory-add-input {
  flex: 1;
  height: 28px;
  padding: 0 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  font-family: inherit;
  font-size: 12px;
  box-sizing: border-box;
}
/* Textareas need explicit vertical padding — inputs vertically center text
   via line-height/height; textareas would otherwise pin text to the top. */
textarea.memory-add-input {
  height: auto;
  padding: 6px 10px;
  line-height: 1.4;
}
.memory-add-input::placeholder {
  color: color-mix(in srgb, var(--fg) 40%, transparent);
}

.memory-add-input:focus {
  outline: none;
  border-color: var(--red);
}

.memory-add-btn {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  font-size: 16px;
  box-sizing: border-box;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.15s;
  flex-shrink: 0;
}

.memory-add-btn:hover {
  background: var(--panel);
  border-color: var(--red);
  color: var(--red);
}

.memory-toolbar {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 4px 0 8px;
}

.memory-toolbar-row {
  display: flex;
  align-items: center;
  gap: 8px;
}



.memory-toolbar-btn {
  background: none;
  border: 1px solid var(--border);
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  font-size: 11px;
  height: 24px;
  padding: 0 8px;
  border-radius: 6px;
  cursor: pointer;
  font-family: inherit;
  transition: all 0.15s;
  white-space: nowrap;
}



.memory-toolbar-btn:hover {
  border-color: var(--fg);
  color: var(--fg);
}

.memory-toolbar-btn.active {
  background: color-mix(in srgb, var(--red) 15%, transparent);
  border-color: color-mix(in srgb, var(--red) 40%, transparent);
  color: var(--red);
}

.memory-toolbar-btn.danger {
  color: var(--color-error);
  border-color: var(--color-error);
}

.memory-toolbar-btn.danger:hover {
  background: color-mix(in srgb, var(--color-error) 10%, transparent);
}

.memory-toolbar-btn:disabled {
  opacity: 1;
  cursor: default;
}
.memory-toolbar-btn.spinning {
  border-color: transparent;
  background: none;
}
.memory-toolbar-toggle {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  height: 24px;
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  cursor: pointer;
  padding: 0 4px;
  user-select: none;
  transition: all 0.15s;
}
.memory-toolbar-toggle:hover {
  color: var(--fg);
}
.memory-toolbar-toggle .admin-switch {
  vertical-align: middle;
}
.memory-toolbar-toggle:has(input:not(:checked)) {
  opacity: 0.7;
}
.memory-toolbar-toggle:has(input:not(:checked)) > span {
  text-decoration: line-through;
  text-decoration-color: color-mix(in srgb, var(--fg) 30%, transparent);
}

/* Bulk action bar */
.memory-bulk-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 2px;
  border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--red) 5%, transparent);
  font-size: 11px;
}

.memory-bulk-bar.hidden {
  display: none;
}
/* Nudge the bulk-bar action buttons up 2px (and Memory's -2px left) to
   align with the row baseline. Covers both the Memory bulk bar
   (Cancel/Delete) and the Skills bulk bar (Cancel/Approve/Delete) — both
   live inside #memory-modal. */
#memory-modal .memory-bulk-bar #memory-bulk-cancel,
#memory-modal .memory-bulk-bar #memory-bulk-delete {
  position: relative;
  top: -2px;
  left: -2px;
}
#memory-modal .memory-bulk-bar #skills-bulk-cancel,
#memory-modal .memory-bulk-bar #skills-bulk-publish,
#memory-modal .memory-bulk-bar #skills-bulk-audit,
#memory-modal .memory-bulk-bar #skills-bulk-delete-nonpassing,
#memory-modal .memory-bulk-bar #skills-bulk-delete {
  position: relative;
  top: -2px;
}
/* Research bulk bar — right-align the action buttons (keep All + count on
   the left). Cancel now lives on the Select toggle, so Archive anchors. */
#doclib-research-bulk #doclib-research-bulk-archive {
  margin-left: auto;
}
#doclib-research-bulk .memory-toolbar-btn {
  position: relative;
  top: 3px;
  right: 16px;
}
/* Archive bulk buttons — nudge down 1px to match research. */
#doclib-arc-bulk .memory-toolbar-btn {
  position: relative;
  top: 1px;
}
/* Same right-aligned layout for the other bulk bars — Chats, Documents,
   Archive, Skills, Memories. Cancel's auto-margin pushes the action group
   to the right; 8px of extra right padding seats it off the edge (matching
   the research bar's 8px nudge). */
#doclib-chats-bulk #doclib-chats-bulk-archive,
#doclib-bulk-bar #doclib-bulk-archive,
#doclib-arc-bulk #doclib-arc-bulk-restore,
#email-lib-bulk #email-lib-bulk-actions,
#tasks-bulk-bar #tasks-bulk-delete,
#serve-bulk-bar #serve-bulk-delete,
#gallery-bulk-bar #gallery-bulk-actions,
#gallery-editor-drafts-bulk #gallery-editor-drafts-bulk-delete,
#memory-modal .memory-bulk-bar #memory-bulk-delete,
#memory-modal .memory-bulk-bar #skills-bulk-publish {
  margin-left: auto;
  position: relative;
  top: -1px;
}

/* X-icon Cancel button used in every bulk-select bar (Esc target). The bare SVG
   sits slightly too high vs. the adjacent text buttons — nudge it down 2px. */
[id$="-bulk-cancel"] svg {
  position: relative;
  top: 2px;
}
#doclib-chats-bulk,
#doclib-bulk-bar,
#doclib-arc-bulk,
#email-lib-bulk,
#tasks-bulk-bar,
#serve-bulk-bar,
#gallery-bulk-bar,
#gallery-editor-drafts-bulk,
#memory-modal .memory-bulk-bar {
  padding-right: 18px;
}
#email-lib-bulk-delete.email-bulk-loading {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  opacity: 0.9;
  cursor: wait;
}
#email-lib-bulk-delete.email-bulk-loading .email-bulk-whirlpool {
  width: 12px;
  height: 12px;
  margin: 0;
  position: relative;
  top: -1px;
}
#email-lib-bulk-delete.email-bulk-loading .email-bulk-loading-label {
  position: relative;
  top: 0;
}
/* Drafts bulk bar defaults to justify-content:flex-end (whole row hugs the
   right). Reset it so All + count sit on the left and only the action button
   is pushed right — matching every other bulk bar. */
#gallery-editor-drafts-bulk {
  justify-content: flex-start;
}
/* Nudge the whole memory + skills bulk buttons (icon + label together) up. */
#memory-modal #memory-bulk-bar .memory-toolbar-btn,
#memory-modal #skills-bulk-bar .memory-toolbar-btn {
  position: relative;
  top: -2px;
}

.memory-bulk-check-all {
  display: flex;
  align-items: center;
  gap: 5px;
  cursor: pointer;
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  font-size: 10px;
  padding: 4px 8px;
  border-radius: 4px;
  user-select: none;
  position: relative;
  top: 0;
}
.memory-bulk-check-all:hover {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
}

#memory-selected-count {
  color: color-mix(in srgb, var(--fg) 50%, transparent);
  font-size: 10px;
  flex: 1;
}

/* Custom checkbox — toggle dot (shared by select-all and per-item) */
.memory-select-cb,
.memory-bulk-check-all input {
  -webkit-appearance: none;
  appearance: none;
  width: 6px !important;
  height: 6px !important;
  min-width: 6px;
  min-height: 6px;
  max-width: 6px;
  max-height: 6px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  flex-shrink: 0;
  margin: 0;
  align-self: center;
  position: relative;
  box-sizing: content-box;
  transition: all 0.15s;
}

.memory-select-cb:hover,
.memory-bulk-check-all input:hover {
  border-color: var(--red);
}

.memory-select-cb:checked,
.memory-bulk-check-all input:checked {
  background: var(--red);
  border-color: var(--red);
}

.memory-count {
  font-size: 11px;
  color: var(--color-muted);
}

.memory-search-input {
  height: 24px;
  margin-top: 6px;
  padding: 0 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  font-family: inherit;
  font-size: 11px;
  width: 100%;
  box-sizing: border-box;
}

.memory-search-input:focus {
  outline: none;
  border-color: var(--red);
}

.memory-list {
  flex: 1;
  min-height: 0;             /* Required so flex:1 inside a flex parent can shrink rather than push its parent past 85vh */
  overflow-y: auto;
  overflow-x: hidden;        /* Stop the synapse sweep from triggering a sideways scrollbar */
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.memory-item.task-paused {
  opacity: 0.45 !important;
  filter: saturate(0.55);
  background: repeating-linear-gradient(
    45deg,
    color-mix(in srgb, var(--fg) 2%, transparent),
    color-mix(in srgb, var(--fg) 2%, transparent) 8px,
    color-mix(in srgb, var(--fg) 5%, transparent) 8px,
    color-mix(in srgb, var(--fg) 5%, transparent) 16px
  ) !important;
}
.memory-item.task-paused:hover {
  opacity: 0.85 !important;
  filter: saturate(0.9);
}
.task-status-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 9px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  padding: 1px 6px;
  border-radius: 3px;
  flex-shrink: 0;
  cursor: pointer;
  border: 1px solid transparent;
  line-height: 16px;
  font-family: 'Fira Code', monospace;
  transition: transform 0.12s ease, border-color 0.12s ease, background 0.12s ease, filter 0.12s ease;
  user-select: none;
}
.task-paused-badge {
  color: var(--orange, #ffb86c);
  background: color-mix(in srgb, var(--orange, #ffb86c) 22%, transparent);
  border-color: color-mix(in srgb, var(--orange, #ffb86c) 35%, transparent);
}
.task-active-badge {
  color: var(--green, #50fa7b);
  background: color-mix(in srgb, var(--green, #50fa7b) 20%, transparent);
  border-color: color-mix(in srgb, var(--green, #50fa7b) 35%, transparent);
}
.task-run-now-badge {
  color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 34%, transparent);
}
.task-card-run-btn {
  appearance: none;
  height: 20px;
  min-height: 0;
  box-sizing: border-box;
  position: relative;
  top: -4px;
}
.task-state-badge svg {
  position: relative;
  top: -1px;
}
.task-status-badge:hover {
  filter: brightness(1.08) saturate(1.15);
}
.task-paused-badge:hover {
  background: color-mix(in srgb, var(--orange, #ffb86c) 30%, transparent);
  border-color: color-mix(in srgb, var(--orange, #ffb86c) 55%, transparent);
}
.task-active-badge:hover {
  background: color-mix(in srgb, var(--green, #50fa7b) 28%, transparent);
  border-color: color-mix(in srgb, var(--green, #50fa7b) 55%, transparent);
}
.task-run-now-badge:hover {
  background: color-mix(in srgb, var(--accent, var(--red)) 24%, transparent);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 52%, transparent);
}

.task-builtin-badge {
  font-size: 9px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 40%, transparent);
  padding: 1px 6px;
  border-radius: 8px;
  flex-shrink: 0;
  white-space: nowrap;
}
.task-builtin-badge.modified {
  color: var(--orange, #ff9800);
  border-color: color-mix(in srgb, var(--orange, #ff9800) 40%, transparent);
  background: color-mix(in srgb, var(--orange, #ff9800) 12%, transparent);
}
.memory-item {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  max-height: 200px;
  flex-shrink: 0;        /* memory-list is a flex column; without this, items get squeezed to fit */
  transition: all 0.15s;
}

.memory-item-title {
  font-size: 12px;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.memory-item:hover {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  border-color: color-mix(in srgb, var(--fg) 16%, transparent);
}
/* Synapse pulse — a brief horizontal light sweep runs left → right across
   each memory like a signal traversing a neural pathway. Per-item stagger
   via nth-child + varied durations keeps the list shimmering rather than
   pulsing in sync. */
#memory-list .memory-item {
  position: relative;
  overflow: hidden;
}
#memory-list .memory-item::after {
  /* Sweep highlight rides the border ring only — gradient-fill + mask cutout
     keeps the bright pulse on the 1px stroke instead of washing the body. */
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  pointer-events: none;
  background: linear-gradient(
    to right,
    transparent 0%,
    transparent calc(var(--sweep, -20%) - 8%),
    color-mix(in srgb, var(--red) 85%, transparent) var(--sweep, -20%),
    transparent calc(var(--sweep, -20%) + 8%),
    transparent 100%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  animation: memory-synapse-sweep 6.2s linear infinite;
  animation-delay: 0.4s;
}
#memory-list .memory-item:nth-child(2n)::after { animation-duration: 7.4s; animation-delay: 1.6s; }
#memory-list .memory-item:nth-child(3n)::after { animation-duration: 8.8s; animation-delay: 3.2s; }
#memory-list .memory-item:nth-child(5n)::after { animation-duration: 9.3s; animation-delay: 4.7s; }
#memory-list .memory-item:nth-child(7n)::after { animation-duration: 5.5s; animation-delay: 2.3s; }
#memory-list .memory-item:hover::after { animation: none; opacity: 0; }
@property --sweep {
  syntax: '<percentage>';
  inherits: false;
  initial-value: -20%;
}
@keyframes memory-synapse-sweep {
  /* Sweep traverses left → right in the first ~12% of the cycle (≈0.7s of
     a 6.2s loop), then waits offscreen. */
  0%   { --sweep: -20%; }
  12%  { --sweep: 120%; }
  13%, 100% { --sweep: 120%; }
}
@media (prefers-reduced-motion: reduce) {
  #memory-list .memory-item::after { animation: none; opacity: 0; }
}
.memory-pinned:hover {
  background: color-mix(in srgb, var(--red) 4%, transparent);
  border-color: var(--border);
  border-left-color: var(--red);
}

.memory-item-content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.memory-item-text {
  font-size: 11px;
  line-height: 1.5;
  word-break: break-word;
  color: var(--fg);
}

.memory-item-edit-input {
  flex: 1;
  padding: 3px 5px;
  border-radius: 4px;
  border: 1px solid var(--red);
  background: var(--bg);
  color: var(--fg);
  font-family: inherit;
  font-size: 11px;
  min-width: 0;
}

.memory-item-edit-input:focus {
  outline: none;
}

/* Edit row: text input + category select side by side */
.memory-edit-row {
  display: flex;
  gap: 4px;
  flex: 1;
  min-width: 0;
}

.memory-edit-cat-select {
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--red);
  border-radius: 4px;
  font-family: inherit;
  font-size: 9px;
  padding: 2px 3px;
  cursor: pointer;
  flex-shrink: 0;
}

.memory-edit-cat-select:focus {
  outline: none;
}

.memory-item-editing {
  border-color: color-mix(in srgb, var(--red) 40%, transparent);
  background: color-mix(in srgb, var(--red) 3%, transparent);
}

.memory-menu-btn {
  background: none;
  border: 1px solid transparent;
  color: var(--color-muted);
  font-size: 18px;
  width: 24px;
  height: 24px;
  line-height: 24px;
  padding: 0;
  border-radius: 6px;
  cursor: pointer;
  flex-shrink: 0;
  opacity: 0;
  transition: opacity 0.15s, background 0.15s, border-color 0.15s;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.memory-item:hover .memory-menu-btn {
  opacity: 1;
}

.memory-menu-btn:hover {
  background: color-mix(in srgb, var(--fg) 7%, transparent);
  border-color: var(--border);
  color: var(--fg);
}

.memory-item-dropdown {
  display: none;
  position: fixed;
  z-index: 1000;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 4px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.3);
  min-width: auto;
  width: max-content;
}

.memory-item-dropdown .dropdown-item-compact {
  padding: 6px 10px;
  font-size: 12px;
  cursor: pointer;
  border-radius: 6px;
  white-space: nowrap;
}

.memory-item-dropdown .dropdown-item-compact:hover {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.memory-dropdown-delete:hover {
  color: var(--red) !important;
}

.memory-item-actions {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
  margin-left: auto;
  opacity: 0;
  transition: opacity 0.15s;
}

.memory-item:hover .memory-item-actions {
  opacity: 1;
}

/* Skill rows show actions at a dim opacity by default so view/run/delete are
   always discoverable, then brighten on hover. */
.memory-item.skill-row .memory-item-actions {
  opacity: 0.35;
  position: relative;
  top: -1px;
}
.memory-item.skill-row:hover .memory-item-actions {
  opacity: 1;
}

.memory-item-btn {
  background: none;
  border: 1px solid transparent;
  color: var(--color-muted);
  font-size: 11px;
  height: 22px;
  padding: 0 6px;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.15s;
  display: flex;
  align-items: center;
}
@media (max-width: 768px) {
  /* Nudge the ••• menu button up on mobile so it visually aligns with the
     title row rather than sitting a hair below it. */
  .memory-item-actions .memory-item-btn { transform: translateY(-3px); }
  /* Email rows sit on a slightly different baseline (extra meta row /
     nav-arrows cluster), so pull the menu button back down 1px. */
  #email-lib-modal .memory-item-actions .memory-item-btn { transform: translateY(-2px); }
  /* Base rule above hides .memory-item-actions until hover. Mobile has no
     hover → the ⋮ button in cookbook serve / library cards was invisible
     and effectively unclickable. Force-show on mobile. */
  .memory-item-actions { opacity: 0.7 !important; }
  .memory-item-actions .memory-item-btn {
    width: 32px;
    height: 32px;
    min-width: 32px;
  }
}

/* Research-preview sub-sections — used by the research-tab expand pattern. */
.doclib-research-section-label {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.55;
  margin: 8px 0 4px;
}
.doclib-research-sources ol {
  margin: 0;
  padding-left: 18px;
  font-size: 11px;
  line-height: 1.5;
}
.doclib-research-sources a {
  color: var(--accent, var(--red));
  text-decoration: none;
}
.doclib-research-sources a:hover { text-decoration: underline; }
.doclib-research-summary {
  font-size: 11px;
  line-height: 1.5;
}
.doclib-research-summary p { margin: 4px 0; }

.memory-item-btn:hover {
  color: var(--fg);
  border-color: var(--border);
  background: color-mix(in srgb, var(--fg) 6%, transparent);
}

.memory-item-btn.delete:hover {
  color: var(--color-error);
  border-color: var(--color-error);
}

.memory-item-btn.save {
  color: var(--red);
}

.memory-item-btn.save:hover {
  border-color: var(--red);
}

.memory-item-btn.pin {
  padding: 1px 4px;
  opacity: 0.4;
  display: flex;
  align-items: center;
  justify-content: center;
}
.memory-item-btn.pin.active {
  opacity: 1;
}
.memory-pin-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--fg);
  opacity: 0.5;
  transition: all 0.15s;
}
.memory-item-btn.pin.active .memory-pin-dot {
  background: var(--red);
  opacity: 1;
}
.memory-pinned {
  border-left: 3px solid var(--red);
  border-radius: 4px;
  background: color-mix(in srgb, var(--red) 4%, transparent);
}
.memory-pinned .memory-item-actions {
  opacity: 1;
}
.memory-pinned .memory-item-actions .memory-item-btn:not(.pin) {
  opacity: 0;
}
.memory-pinned:hover .memory-item-actions .memory-item-btn:not(.pin) {
  opacity: 1;
}

/* Category filter chips */
.memory-category-filters {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.memory-cat-chip {
  background: none;
  border: 1px solid var(--border);
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  font-size: 10px;
  height: 22px;
  padding: 0 8px;
  display: inline-flex;
  align-items: center;
  border-radius: 10px;
  cursor: pointer;
  font-family: inherit;
  transition: all 0.15s;
  text-transform: lowercase;
}

.memory-cat-chip:hover {
  border-color: var(--red);
  color: var(--red);
}

.memory-cat-chip.active {
  background: color-mix(in srgb, var(--red) 15%, transparent);
  border-color: color-mix(in srgb, var(--red) 40%, transparent);
  color: var(--red);
}

/* Sort select */
.memory-sort-select {
  position: relative;
  top: 3px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: inherit;
  font-size: 11px;
  height: 24px;
  padding: 0 6px;
  cursor: pointer;
}

.memory-sort-select:focus {
  outline: none;
  border-color: var(--red);
}

/* Item metadata row */
.memory-item-meta {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
}

/* Category badge on each item */
.memory-cat-badge {
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  color: color-mix(in srgb, var(--fg) 55%, transparent);
  text-transform: lowercase;
}

.memory-cat-identity {
  background: color-mix(in srgb, var(--hl-function) 15%, transparent);
  color: var(--hl-function);
}
.memory-cat-preference {
  background: color-mix(in srgb, var(--hl-keyword) 15%, transparent);
  color: var(--hl-keyword);
}
.memory-cat-contact {
  background: color-mix(in srgb, #98c379 15%, transparent);
  color: #98c379;
}
.memory-cat-project {
  background: color-mix(in srgb, var(--hl-string) 15%, transparent);
  color: var(--hl-string);
}
.memory-cat-goal {
  background: color-mix(in srgb, var(--red) 15%, transparent);
  color: var(--red);
}
.memory-cat-task {
  background: color-mix(in srgb, #d19a66 15%, transparent);
  color: #d19a66;
}
.memory-cat-pinned {
  background: color-mix(in srgb, var(--red) 15%, transparent);
  color: var(--red);
}

/* Source and time metadata */
.memory-item-source,
.memory-item-time,
.memory-item-uses {
  font-size: 9px;
  color: color-mix(in srgb, var(--fg) 35%, transparent);
}
.memory-item-uses {
  font-family: monospace;
  color: color-mix(in srgb, var(--fg) 55%, transparent);
}

.memory-item-source::before,
.memory-item-time::before {
  content: '\00b7  ';
}

/* Empty state */
.memory-empty {
  padding: 24px 16px;
  color: color-mix(in srgb, var(--fg) 35%, transparent);
  text-align: center;
  font-size: 11px;
  font-style: italic;
}

/* Suggestions area */
.memory-suggestions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  /* Bound the import-review list to the modal like the sibling .memory-list,
     so a long list scrolls internally instead of overflowing the
     overflow:hidden .admin-card — which clipped lower entries and their
     save/discard controls with no usable scroll area. */
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  /* Small gutter so the scrollbar doesn't sit flush against the item cards. */
  padding-right: 4px;
}

.memory-suggestions.hidden {
  display: none;
}

.memory-suggestions-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 11px;
  color: color-mix(in srgb, var(--fg) 70%, transparent);
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
  /* Pin the title + save all/back controls to the top of the scrolling
     review list so they stay reachable while the items scroll under them.
     Opaque background masks items passing beneath. */
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--panel);
}
.memory-suggestions-actions,
.memory-suggestion-actions {
  display: flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
}
.memory-suggestions-actions {
  position: relative;
  left: -4px;
}
.memory-suggestions-actions .memory-item-btn,
.memory-suggestion-actions .memory-item-btn {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border-color: color-mix(in srgb, var(--border) 85%, transparent);
}
.memory-suggestions-actions .memory-item-btn.save,
.memory-suggestion-actions .memory-item-btn.save {
  background: color-mix(in srgb, var(--red) 9%, transparent);
  border-color: color-mix(in srgb, var(--red) 28%, transparent);
}

.memory-suggestion-item {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 6px;
  padding: 5px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: color-mix(in srgb, var(--red) 3%, transparent);
}

/* Tidy animations */
.memory-tidy-editing {
  border-color: color-mix(in srgb, var(--hl-function) 40%, transparent);
  background: color-mix(in srgb, var(--hl-function) 6%, transparent);
  transition: all 0.3s;
}

.memory-tidy-text-old {
  opacity: 0.4;
  text-decoration: line-through;
  transition: opacity 0.25s;
}

.memory-tidy-text-new {
  color: var(--hl-function);
  transition: color 0.4s;
}

.memory-tidy-removing {
  text-decoration: line-through;
  opacity: 0;
  max-height: 0;
  padding-top: 0;
  padding-bottom: 0;
  margin-top: 0;
  margin-bottom: 0;
  border-color: transparent;
  overflow: hidden;
  transition: opacity 0.3s, max-height 0.4s 0.1s, padding 0.4s 0.1s, border-color 0.3s;
}

/* ===== DOCUMENT EDITOR (ARTIFACTS) PANEL ===== */

/* Doc editor is a body-level sibling of chat-container */

/* Divider between chat and doc editor */
.doc-divider {
  width: 1px;
  flex-shrink: 0;
  background: color-mix(in srgb, var(--fg) 11%, transparent);
  cursor: col-resize;
  transition: background 0.15s;
  position: relative;
  z-index: 10;
}
.doc-divider::before {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  left: -10px;
  width: 21px;
  cursor: col-resize;
}
.doc-divider:hover {
  background: color-mix(in srgb, var(--fg) 30%, transparent);
}
/* Always-visible "›" handle on the drag divider — clickable to collapse the
   panel, and signals the divider is interactive. */
.doc-divider-collapse {
  position: absolute;
  top: 50%;
  left: 2px;
  transform: translateY(-50%);
  width: 20px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--panel);
  color: var(--fg);
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  opacity: 0.6;
  cursor: pointer;
  transition: opacity 0.15s, border-color 0.15s, left 0.22s ease;
  z-index: 12;
}
/* When in fullscreen mode (cursor outside the doc), the flap itself slides
   to the OUTSIDE edge of the divider — visually belonging to the chat side
   the cursor is on, not the doc. The left/right shift pairs with the glyph
   rotation for a single coordinated transition. */
.doc-divider-collapse[data-mode="fullscreen"] {
  left: -22px;
}
.doc-divider:hover .doc-divider-collapse { opacity: 0.92; }
.doc-divider-collapse:hover { opacity: 1 !important; border-color: var(--accent, var(--red)); }
/* The same `›` glyph is in the markup; CSS rotates 180° for the left-pointing
   (fullscreen) state. Smooth transition pairs with the chevron's slide. */
.doc-divider-collapse > span {
  display: inline-block;
  transition: transform 0.22s ease, opacity 0.18s ease;
}
.doc-divider-collapse[data-mode="fullscreen"] > span {
  transform: rotate(180deg);
}
/* Secondary "hide panel" X button in the divider — invisible until the pane
   is fullscreen, then floats just below the unfullscreen chevron so the user
   has a one-tap escape that minimises the pane instead of just exiting
   fullscreen. */
.doc-divider-hide {
  position: absolute;
  top: 50%;
  left: 2px;
  width: 20px;
  height: 20px;
  margin-top: 22px;
  display: none;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--panel);
  color: var(--accent, var(--red));
  cursor: pointer;
  padding: 0;
  z-index: 12;
  opacity: 0.85;
  transition: opacity 0.15s, border-color 0.15s;
}
.doc-divider-hide:hover {
  opacity: 1;
  border-color: var(--accent, var(--red));
}
/* Smooth entrance — slide in from the left + fade up so it doesn't snap into
   place when fullscreen activates. The chevron's vertical centering uses
   translateY(-50%), so the animation has to keep that part. */
@keyframes doc-fs-chevron-in {
  from { opacity: 0; transform: translateY(-50%) translateX(-18px); }
  to   { opacity: 0.85; transform: translateY(-50%) translateX(0); }
}

/* Copy / Export split button — main click copies, the caret opens the export menu. */
.doc-split-btn {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--border);
  border-radius: 7px;
  overflow: hidden;
  height: 22px;
  flex-shrink: 0;
}
.doc-split-btn .doc-split-main,
.doc-split-btn .doc-split-caret {
  border: none !important;
  border-radius: 0 !important;
  height: 100% !important;
  min-height: 0 !important;
  /* Keep the element fully opaque so the divider line stays crisp; dim the
     glyph via colour instead (the base .doc-action-icon-btn fades the whole
     element to 0.3, which also hides the divider). */
  opacity: 1 !important;
  color: color-mix(in srgb, var(--fg) 55%, transparent) !important;
  background: none !important;
  transition: color 0.12s, background 0.12s;
}
.doc-split-btn:hover .doc-split-main,
.doc-split-btn:hover .doc-split-caret { color: var(--fg) !important; }
.doc-split-btn .doc-split-caret {
  border-left: 1px solid var(--border) !important;
  padding: 0 5px 0 7px !important;
}
.doc-split-btn .doc-split-main:hover,
.doc-split-btn .doc-split-caret:hover {
  background: color-mix(in srgb, var(--fg) 8%, transparent) !important;
  color: var(--fg) !important;
}

/* Editor pane — body-level flex sibling */
.doc-editor-pane {
  flex: 1;
  min-width: 0;
  max-width: 70vw;
  container-type: inline-size;
  container-name: docpane;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  border-left: 1px solid var(--border);
  box-shadow: -10px 0 22px rgba(0, 0, 0, 0.16);
  overflow: hidden;
  height: 100%;
  position: relative;
  z-index: 1;
  color-scheme: dark;
  /* Smooth open: slide in from the right + fade. Same easing/duration as
     the notes pane so both drawers feel like one mechanism. */
  animation: doc-pane-enter 200ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
  transform-origin: right center;
  will-change: transform, opacity;
}
@keyframes doc-pane-enter {
  from { transform: translateX(24px); opacity: 0; }
  to   { transform: translateX(0);    opacity: 1; }
}
.doc-editor-pane.doc-pane-leaving {
  animation: doc-pane-leave 160ms cubic-bezier(0.4, 0, 1, 1) both;
  pointer-events: none;
}
@keyframes doc-pane-leave {
  from { transform: translateX(0);    opacity: 1; }
  to   { transform: translateX(24px); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .doc-editor-pane,
  .doc-editor-pane.doc-pane-leaving { animation: none; }
}
.doc-loading-overlay {
  position: absolute;
  inset: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
}

/* ---- Tab bar ---- */
.doc-tab-bar {
  display: flex;
  align-items: stretch;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  height: 36px;
}
.doc-tab-scroll {
  display: flex;
  align-items: stretch;
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  justify-content: flex-start;
  /* Fade tabs into the bar's background at the edges (next to the scroll
     arrows) so an overflowing tab dissolves instead of being hard-cut. The
     fade is conditional — when we're at an edge there's nothing to fade to,
     so the mask gradient becomes flat on that side (no shadow). */
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%);
}
.doc-tab-scroll.is-at-left {
  -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 18px), transparent 100%);
          mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 18px), transparent 100%);
}
.doc-tab-scroll.is-at-right {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 18px, #000 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 18px, #000 100%);
}
.doc-tab-scroll.is-at-left.is-at-right {
  -webkit-mask-image: none;
          mask-image: none;
}
.doc-tab-scroll::-webkit-scrollbar { display: none; }
.doc-tab-arrow {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.3;
  cursor: pointer;
  font-size: 18px;
  padding: 0 6px;
  flex-shrink: 0;
  transition: opacity 0.15s;
  line-height: 1;
  display: flex;
  align-items: center;
  position: relative;
  top: -2px;
}
.doc-tab-arrow:hover {
  opacity: 1;
}
#doc-tab-right,
#doc-tab-left {
  position: relative;
  top: 3px;
}
/* Mobile swipe-down grab handle at the top of the doc sheet. */
.doc-mobile-grabber { display: none; }
@media (max-width: 768px) {
  body.doc-view .doc-mobile-grabber {
    display: block;
    flex-shrink: 0;
    height: 18px;
    position: relative;
    background: transparent;
    background-color: transparent;
    background-image: none;
    touch-action: none;
    cursor: grab;
  }
  body.doc-view .doc-mobile-grabber::before {
    content: '';
    position: absolute;
    top: 7px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    background: var(--fg);
    opacity: 0.25;
    border-radius: 2px;
  }
}

/* Ghost tab shown in the empty state (new, not-yet-saved document). Muted +
   italic so it reads as a placeholder, and non-interactive so clicking it
   can't hit the tab handlers (it has no data-doc-id). */
.doc-tab.doc-tab-ghost {
  /* New, not-yet-saved doc tab. It already carries .active, so it shows the
     accent underline like any active tab — that's all we want. The old dashed
     border + italic/dim "pending" styling looked weird, so they're gone. */
  pointer-events: none;
}
.doc-tab {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  font-family: inherit;
  font-size: 11px;
  color: var(--fg);
  opacity: 0.4;
  cursor: pointer;
  white-space: nowrap;
  transition: opacity 0.1s, background 0.1s;
  flex-shrink: 0;
  border-right: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
  position: relative;
}
.doc-tab:hover {
  opacity: 0.7;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
}
.doc-tab.active {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  border-radius: 7px 7px 0 0;
}
.doc-tab.active::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--red);
}
.doc-tab-title {
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.doc-tab-title-input {
  background: transparent;
  border: 1px solid var(--fg);
  border-radius: 2px;
  color: var(--fg);
  font-family: inherit;
  font-size: 11px;
  padding: 0 4px;
  height: 18px;
  width: 120px;
  max-width: 180px;
  outline: none;
}
.doc-tab-lang {
  font-size: 9px;
  opacity: 0.5;
}
.doc-tab-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.4;
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  padding: 0 5px;
  margin-left: 3px;
  flex-shrink: 0;
  align-self: center;
  transition: opacity 0.12s;
}
.doc-tab-close:hover { opacity: 1; }
/* Mobile-only footer (Close + Copy); hidden on desktop and in email mode. */
.doc-mobile-footer { display: none; }
.doc-tab-new {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.25;
  cursor: pointer;
  font-size: 11px;
  font-weight: 600;
  padding: 0 10px;
  transition: opacity 0.1s;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  height: 100%;
}
.doc-tab-new:hover {
  opacity: 0.7;
}
.doc-tab-play {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.3;
  cursor: pointer;
  padding: 0 2px;
  font-size: 10px;
  line-height: 1;
  transition: opacity 0.1s, color 0.1s;
  flex-shrink: 0;
}
.doc-tab-play:hover {
  opacity: 1;
  color: var(--green, #4ec970);
}
.doc-tab-play.active {
  opacity: 1;
  color: var(--green, #4ec970);
}
.doc-tab.dragging {
  opacity: 0.3;
}
.doc-tab.drag-over {
  border-left: 2px solid var(--fg);
}

/* ---- HTML preview iframe ---- */
.doc-html-preview {
  flex: 1;
  width: 100%;
  border: none;
  background: #fff;
}

/* ---- Editor header ---- */
.doc-editor-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  /* Moved to the bottom as a footer: flex order pushes it last in the pane
     column, and the divider flips to the top edge. */
  order: 99;
  border-top: 1px solid var(--border);
  flex-shrink: 0;
  flex-wrap: nowrap;
  min-height: 36px;
  background: var(--bg);
}
/* Version now lives in the document tab, not the footer. */
#doc-version-badge { display: none !important; }
/* Language icon chip on doc tabs — sits between the version pill and title.
   Hidden when empty so docs without a language don't get awkward spacing. */
.doc-tab-lang {
  display: inline-flex; align-items: center;
  flex-shrink: 0;
  align-self: center;
}
.doc-tab-lang:empty { display: none; }
.doc-tab-lang svg { display: block; }

.doc-tab-version {
  font-size: 9px;
  font-weight: 600;
  padding: 1px 6px;
  cursor: pointer;
  flex-shrink: 0;
  align-self: center;
  line-height: 1.4;
  /* Sits to the LEFT of the title now — space it off the title text. */
  margin-right: 6px;
  /* Accent pill so it's obvious the version is a clickable control. */
  color: var(--accent-primary, var(--red));
  border: 1px solid color-mix(in srgb, var(--accent-primary, var(--red)) 45%, transparent);
  background: color-mix(in srgb, var(--accent-primary, var(--red)) 12%, transparent);
  border-radius: 9px;
}
.doc-tab-version:hover {
  border-color: var(--accent-primary, var(--red));
  background: color-mix(in srgb, var(--accent-primary, var(--red)) 22%, transparent);
}
.doc-close-btn {
  order: -1;
  opacity: 0.5;
  flex-shrink: 0;
}
.doc-close-btn:hover {
  opacity: 1;
}

.doc-editor-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1;
  justify-content: flex-end;
}
.doc-left .doc-editor-actions {
  margin-left: 0;
}

.doc-version-badge {
  background: color-mix(in srgb, var(--red) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--red) 55%, transparent);
  color: var(--red);
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 600;
  line-height: 1.4;
  cursor: pointer;
  user-select: none;
  transition: background 0.1s, border-color 0.1s;
  opacity: 0.9;
}
.doc-version-badge:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--red) 20%, transparent);
  border-color: var(--red);
}

.doc-stream-indicator {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  color: var(--accent);
  opacity: 0.9;
  white-space: nowrap;
  animation: doc-stream-pulse 1.5s ease-in-out infinite;
}
.doc-stream-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}
@keyframes doc-stream-pulse {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 1; }
}
.doc-updated-flash {
  animation: doc-update-flash 0.6s ease-out;
}
@keyframes doc-update-flash {
  0%   { box-shadow: inset 0 0 0 2px var(--accent); }
  100% { box-shadow: inset 0 0 0 2px transparent; }
}

/* In the doc footer the type picker sits next to the accent Copy/Export split —
   match its 28px height and 6px radius so the right-hand controls line up. */
.doc-actions-footer #doc-language-select {
  height: 28px;
  border-radius: 6px;
  font-size: 11px;
  top: 0;
}
/* Lang-type icon shown to the LEFT of the language select. Browsers won't
   render SVG inside <option>, so this surface the current selection's icon
   externally. */
#doc-language-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px; height: 18px;
  color: var(--accent-primary, var(--red));
  flex-shrink: 0;
}
#doc-language-icon:empty { display: none; }
#doc-language-icon svg { display: block; }

/* Visually hidden but available to assistive tech (screen readers, axe).
   Use for content that should be announced/structural but not painted —
   e.g. the persistent page <h1>. */
.a11y-visually-hidden {
  position: absolute !important;
  width: 1px !important; height: 1px !important;
  padding: 0 !important; margin: -1px !important;
  overflow: hidden !important; clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important; border: 0 !important;
}

/* ── Custom language type picker (replaces visible chrome of native <select>
   — <option>s can't render SVG). Hidden select stays as the source of truth. */
.doc-langpicker-native-hidden {
  position: absolute !important;
  width: 1px !important; height: 1px !important;
  padding: 0 !important; margin: -1px !important;
  overflow: hidden !important; clip: rect(0,0,0,0) !important;
  border: 0 !important;
}
.doc-langpicker-trigger {
  display: inline-flex; align-items: center; gap: 6px;
  height: 28px; padding: 0 8px 0 10px;
  background: var(--bg); color: var(--fg);
  border: 1px solid var(--border); border-radius: 6px;
  font-size: 11px; cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.doc-langpicker-trigger:hover {
  border-color: color-mix(in srgb, var(--accent-primary, var(--red)) 60%, var(--border));
}
.doc-langpicker-trigger svg { display: block; flex-shrink: 0; }
.doc-langpicker-label {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 90px;
}
.doc-langpicker-ico-blank {
  display: inline-block; width: 14px; height: 14px; flex-shrink: 0;
}
.doc-langpicker-menu {
  background: var(--bg);
  border: 1px solid var(--border); border-radius: 8px;
  padding: 4px;
  box-shadow: 0 10px 28px rgba(0,0,0,0.32);
  max-height: 60vh; overflow-y: auto;
  z-index: 10000;
  min-width: 160px;
}
.doc-langpicker-item {
  display: flex; align-items: center; gap: 8px;
  width: 100%;
  padding: 6px 10px;
  background: none; color: var(--fg);
  border: none; border-radius: 5px;
  font-size: 12px; text-align: left;
  cursor: pointer;
}
.doc-langpicker-item:hover {
  background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.doc-langpicker-item.is-selected {
  background: color-mix(in srgb, var(--accent-primary, var(--red)) 14%, transparent);
  color: var(--accent-primary, var(--red));
}
.doc-langpicker-item svg { display: block; flex-shrink: 0; }
.doc-langpicker-item .doc-langpicker-label { max-width: 220px; }

.doc-language-select {
  background-color: var(--bg);
  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='%239cdef2' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 15 12 9 18 15'/></svg>");
  background-repeat: no-repeat;
  background-position: right 4px center;
  color: var(--fg);
  color-scheme: dark;
  border: 1px solid var(--border);
  border-radius: 5px;
  position: relative;
  top: 0;
  font-family: inherit;
  font-size: 10px;
  padding: 2px 20px 2px 8px;
  height: 22px;
  /* Fixed width so the right-anchored chevron doesn't shift when the selected
     option's text width changes. */
  width: 96px;
  text-overflow: ellipsis;
  opacity: 0.8;
  cursor: pointer;
  -moz-appearance: none;
  appearance: none;
}
/* Light theme: tint the chevron with the light foreground instead of cyan. */
:root.light .doc-language-select {
  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='%232b2b2b' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 15 12 9 18 15'/></svg>");
}
/* New-tab "+" spins on hover, like the library sidebar "+". */
.doc-tab-new svg { transition: transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1); }
.doc-tab-new:hover svg { transform: rotate(180deg) scale(1.15); }

.doc-action-btn {
  background: none;
  color: var(--fg);
  border: none;
  font-family: inherit;
  font-size: 10px;
  padding: 2px 8px;
  height: 22px;
  cursor: pointer;
  opacity: 0.45;
  transition: opacity 0.1s;
}
.doc-action-btn:hover {
  opacity: 1;
}
.doc-action-icon-btn {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.3;
  cursor: pointer;
  padding: 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.1s;
}
.doc-action-icon-btn:hover {
  opacity: 1;
}
/* The "T" icon stays centered in the fixed-size button so it grows
   symmetrically (from the center) instead of jumping around as the size
   changes. The S/M/L letter is pinned to the bottom-right corner, absolutely
   positioned so it never shifts the icon either. */
#doc-fontsize-btn { transform: translateY(-1px); }
#doc-fontsize-btn svg { flex: 0 0 auto; }
.doc-fontsize-levels {
  position: absolute;
  right: 6px;
  bottom: 3px;
  display: inline-flex;
  line-height: 1;
  pointer-events: none;
}
.doc-fontsize-levels i {
  font-style: normal;
  font-weight: 700;
  font-size: 7px;
  /* Bare --accent is undefined in this codebase, was falling back to the
     hardcoded blue #4a9eff — use the real theme accent. */
  color: var(--accent-primary, var(--red));
  opacity: 1;
}
.doc-fontsize-levels i.active { opacity: 1; }
/* Collapsed buttons hidden in header, shown in overflow menu */
.doc-collapsible-btn.doc-collapsed { display: none !important; }
.doc-overflow-wrapper { order: -1; }
.doc-overflow-toggle { opacity: 0.5; }
.doc-overflow-toggle:hover { opacity: 1; }
.doc-overflow-menu {
  display: none;
  position: fixed;
  z-index: 9999;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 4px 0;
  min-width: 140px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.doc-overflow-menu.open { display: block; }
.doc-overflow-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 5px 12px;
  background: none;
  border: none;
  color: var(--fg);
  font-family: inherit;
  font-size: 11px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.1s;
}
.doc-overflow-item:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.doc-overflow-item svg { flex-shrink: 0; opacity: 0.5; }

/* Markdown Edit/Preview two-icon switch — segmented, styled to match the Copy
   split button. The active half gets a static highlight (no sliding). */
.md-view-toggle {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--border);
  border-radius: 7px;
  overflow: hidden;
  flex-shrink: 0;
  height: 22px;
}
.md-view-toggle .md-view-opt {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 100%;
  padding: 0 !important;
  border: none !important;
  border-radius: 0 !important;
  background: none !important;
  color: var(--fg);
  opacity: 0.45;
  cursor: pointer;
  transition: opacity 0.12s, color 0.12s, background 0.12s;
}
.md-view-toggle .md-view-opt:hover { opacity: 0.8; }
.md-view-toggle .md-view-opt.active {
  opacity: 1;
  color: var(--fg);
  background: color-mix(in srgb, var(--fg) 8%, transparent) !important;
  /* "Punch" pop when a side becomes active (only fires on an actual switch —
     re-applying .active without a change doesn't restart the animation). */
  animation: md-view-punch 0.28s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes md-view-punch {
  0%   { transform: scale(0.8); }
  55%  { transform: scale(1.18); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .md-view-toggle .md-view-opt.active { animation: none; }
}

/* Mobile: the doc/email footer controls (format toggle, export button, export
   menu) were slimmer than the Send button, so the row looked ragged. Bump them
   to match the Send button height (and give the export/overflow menu bigger,
   touch-friendly rows). */
@media (max-width: 768px) {
  .md-view-toggle { height: 28px; }
  .md-view-toggle .md-view-opt { width: 38px; }
  .doc-action-icon-btn { padding: 6px; }
  .email-send-btn { height: 28px; }
  /* The type/language picker was the slim one stuck at 22px — match it to the
     rest of the row. */
  .doc-language-select { height: 28px; font-size: 13px; padding: 2px 22px 2px 10px; }
  .doc-overflow-item { font-size: 13px; padding: 9px 14px; }
  .doc-overflow-item .overflow-icon svg,
  .doc-overflow-item svg { width: 16px; height: 16px; }
  .email-more-menu .dropdown-item-compact { padding: 9px 12px; font-size: 13px; }

  /* The doc footer is tight once the run/preview toggle joins it, so on mobile
     go icon-only for Close, Undo & Copy (their icons are clear).
     font-size:0 drops the text node next to the SVG (the SVG has fixed w/h, so
     it's unaffected). Slightly narrower type picker buys a little more room. */
  #doc-actions-footer #doc-undo-btn span,
  #doc-actions-footer #doc-footer-close-btn span,
  #doc-email-actions #doc-email-discard-btn span { display: none; }
  #doc-actions-footer #doc-undo-btn,
  #doc-actions-footer #doc-footer-close-btn,
  #doc-email-actions #doc-email-discard-btn { gap: 0; padding: 0 9px; }
  #doc-actions-footer #doc-footer-copy-btn { gap: 0; padding: 0 13px; font-size: 0; }
  /* In reply mode the button carries a "Reply" label that should stay visible
     (the icon-only treatment above is only for the plain Copy action). */
  #doc-actions-footer #doc-footer-copy-btn[data-mode="reply"] { gap: 5px; padding: 0 13px; font-size: 12px; }
  #doc-actions-footer #doc-language-select { width: 80px; }
}

/* Markdown formatting toolbar */
/* In code-mode the toolbar only hosts the view toggles + utility buttons
   (font-size, diff). Hide markdown-only formatting controls — bold,
   italic, headings, list, link, attach, code-dropdown, emoji, hr, and
   the PDF-only buttons. The view toggles + fontsize + diff stay. */
.doc-md-toolbar[data-mode="code"] [data-md],
.doc-md-toolbar[data-mode="code"] .md-dd-toggle,
.doc-md-toolbar[data-mode="code"] #md-toolbar-attach-btn,
.doc-md-toolbar[data-mode="code"] #md-toolbar-emoji-slot,
.doc-md-toolbar[data-mode="code"] .md-toolbar-pdf-only,
.doc-md-toolbar[data-mode="code"] .md-toolbar-sep {
  display: none !important;
}
.doc-md-toolbar {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 1px;
  padding: 2px 8px;
  border-bottom: 1px solid var(--border);
  background: var(--bg);
  flex-shrink: 0;
  overflow: hidden;
  height: 36px;
  position: relative;
  /* Same edge fade as the tab strip — toolbar buttons dissolve into the bar's
     background at the edges instead of being hard-cut when they overflow. */
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%);
}
.doc-md-toolbar button {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.35;
  padding: 4px 7px;
  font-family: inherit;
  font-size: 11px;
  cursor: pointer;
  white-space: nowrap;
  line-height: 1.3;
  transition: opacity 0.1s;
  min-height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.doc-md-toolbar button:hover {
  opacity: 1;
}
.doc-md-toolbar button:active {
  opacity: 0.6;
}
/* Active formatting state — set by the email WYSIWYG sync. Mirrors the
   selection's current marks (B/I/S, heading level, list) so the toolbar
   acts as an indicator, not just a launcher. */
.doc-md-toolbar button.is-active {
  opacity: 1;
  color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
}
.doc-md-toolbar button.is-active:hover {
  background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
}
/* No black tap-flash / focus background on the doc toolbar + tab controls —
   they're icon buttons, the opacity change is the only feedback we want. */
.doc-md-toolbar button,
.doc-tab-new,
.doc-tab-arrow,
.doc-tab-play {
  -webkit-tap-highlight-color: transparent;
}
.doc-md-toolbar button:focus,
.doc-md-toolbar button:focus-visible,
.doc-tab-new:focus,
.doc-tab-new:focus-visible,
.doc-tab-arrow:focus,
.doc-tab-arrow:focus-visible {
  outline: none;
  background: none;
}
/* Grouped formatting dropdown toggles (heading / code / list) */
.md-dd-toggle { gap: 1px !important; }
/* Drop the dropdown chevron lower and tint it accent so it reads as a menu cue. */
.md-dd-toggle svg { opacity: 1; margin-left: 1px; flex-shrink: 0; transform: translateY(4px); color: var(--accent-primary, var(--red, #4a9eff)); }
.md-dd-ico {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  flex-shrink: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  opacity: 0.7;
}
.md-toolbar-sep {
  width: 1px;
  height: 12px;
  background: color-mix(in srgb, var(--border) 60%, transparent);
  margin: 0 4px;
  flex-shrink: 0;
}
.md-toolbar-items {
  display: flex;
  align-items: center;
  gap: 1px;
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  scroll-behavior: smooth;
}
.md-toolbar-items::-webkit-scrollbar { display: none; }
/* On a PDF, the annotation tools (text / checkmark / signature) are the primary
   actions — pull them to the far left of the toolbar. `order` is visual only
   (they're display:none on non-PDF docs), so it has no effect elsewhere. */
.md-toolbar-items #doc-pdf-add-text-btn { order: -3; }
.md-toolbar-items #doc-pdf-add-check-btn { order: -2; }
.md-toolbar-items #doc-pdf-add-sign-btn { order: -1; }
@media (min-width: 769px) {
  .md-toolbar-items #doc-pdf-add-text-btn {
    margin-left: 14px;
  }
}
/* Stack the signature icon over a tiny "sign" caption so the tool reads
   clearly (the icon alone is ambiguous). The JS toggles inline display
   none/'' for PDF mode, so this flex display only applies when shown. */
#doc-pdf-add-sign-btn {
  flex-direction: column;
  align-items: center;
  gap: 0;
  line-height: 1;
}
/* Pull the icon down toward the "sign" label so they read as one unit. */
#doc-pdf-add-sign-btn svg { transform: translateY(2px); }
.doc-pdf-sign-label {
  font-size: 6.5px;
  letter-spacing: 0.3px;
  margin-top: 0;
  opacity: 1;
  color: var(--accent, var(--red));
}
/* Edge scroll arrows — appear when the toolbar has more icons off-screen. */
.md-scroll-arrow {
  position: absolute;
  top: 1px;
  bottom: 1px;
  width: 28px;
  display: flex;
  align-items: center;
  border: none;
  cursor: pointer;
  color: var(--fg);
  padding: 0 !important;
  min-height: 0 !important;
  opacity: 1 !important;
  z-index: 6;
}
.md-scroll-arrow svg { position: relative; top: 2px; opacity: 0.75; transition: opacity 0.1s; }
.md-scroll-arrow:hover svg { opacity: 1; }
@media (min-width: 769px) {
  /* On desktop the arrow rides 2px lower than the toolbar baseline. */
  .md-scroll-arrow svg { top: 0; }
}
.md-scroll-left {
  left: 0;
  justify-content: flex-start;
  padding-left: 3px;
  /* Mostly-solid toolbar bg so the icons behind the arrow are hidden, fading
     to transparent only at the inner edge. */
  background: linear-gradient(to right, var(--bg) 0%, var(--bg) 80%, transparent 100%);
}
.md-scroll-right {
  right: 0;
  justify-content: flex-end;
  padding-right: 3px;
  background: linear-gradient(to left, var(--bg) 0%, var(--bg) 80%, transparent 100%);
}
.md-toolbar-overflow-wrapper {
  position: relative;
  flex-shrink: 0;
  margin-left: auto;
}
.md-toolbar-overflow-toggle {
  background: none;
  border: 1px solid transparent;
  color: var(--fg);
  opacity: 0.4;
  padding: 2px 4px;
  border-radius: 4px;
  cursor: pointer;
  transition: opacity 0.1s;
}
.md-toolbar-overflow-toggle:hover {
  opacity: 1;
}
.md-toolbar-overflow-menu {
  display: none;
  position: fixed;
  z-index: 1000;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 4px;
  min-width: 0;
  width: max-content;
  box-shadow: 0 8px 24px rgba(0,0,0,0.3);
  backdrop-filter: blur(12px);
}
.md-toolbar-overflow-menu.open {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  max-width: 200px;
}
.md-toolbar-overflow-item {
  background: none;
  border: 1px solid transparent;
  color: var(--fg);
  opacity: 0.5;
  padding: 4px 8px;
  border-radius: 4px;
  font-family: inherit;
  font-size: 11px;
  cursor: pointer;
  white-space: nowrap;
  line-height: 1.3;
  transition: opacity 0.1s, background 0.1s;
}
.md-toolbar-overflow-item:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  border-color: var(--border);
}

/* Find bar */
.doc-find-bar {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  background: var(--panel, var(--bg));
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.doc-find-input {
  flex: 1;
  min-width: 0;
  padding: 3px 6px;
  font-size: 12px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--fg);
  outline: none;
}
.doc-find-input:focus { border-color: var(--accent); }
.doc-find-count {
  font-size: 11px;
  opacity: 0.6;
  white-space: nowrap;
  min-width: 50px;
  text-align: center;
}
.doc-find-nav, .doc-find-close {
  background: none;
  border: none;
  color: var(--fg);
  cursor: pointer;
  padding: 2px 5px;
  font-size: 14px;
  border-radius: 3px;
  opacity: 0.7;
}
.doc-find-nav:hover, .doc-find-close:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 10%, transparent); }

/* Editor — highlighted overlay + transparent textarea.
   Lock the editor wrap to a fixed text-column width, centered horizontally
   in the doc pane. This eliminates wrap-related drift bugs as a class:
   the textarea + overlay always have the same available width regardless
   of viewport, sidebar state, dock state, or window resize. Whitespace on
   either side reads like a writing-app column (Bear / iA Writer pattern).
   container-type: inline-size lets us hide the line-number gutter via a
   container query when the editor's own width is narrow (rather than
   viewport-width, which would miss the case of a narrow side-docked
   editor inside a wide window). */
.doc-editor-wrap {
  position: relative;
  flex: 1;
  min-height: 0;
  /* Cap the column at ~820px outer (~760px content area after the 60px
     of horizontal padding the textarea+overlay use for the gutter). */
  max-width: 820px;
  width: 100%;
  margin-left: auto;
  margin-right: auto;
  overflow: hidden;
  background: var(--bg);
  container-type: inline-size;
  container-name: doceditor;
}

/* When the editor itself is narrow, soft-wrap makes one logical line
   span multiple visual rows but the gutter still shows ONE number per
   logical line — so the gutter and the visible rows fall out of sync.
   Hide the gutter (and reclaim the 48px of left padding it reserved)
   below a threshold where the mismatch reads as a glitch. */
@container doceditor (max-width: 360px) {
  .doc-line-numbers { display: none !important; }
  .doc-editor-textarea,
  .doc-editor-highlight {
    padding-left: 12px !important;
  }
}
.doc-line-numbers {
  position: absolute;
  top: 0; left: 0; bottom: 0;
  width: 36px;
  padding: 10px 8px 10px 0;
  margin: 0;
  font-family: inherit;
  font-size: 11px;
  line-height: 1.45;
  text-align: right;
  color: var(--fg);
  opacity: 0.18;
  background: var(--bg);
  overflow: hidden;
  white-space: pre;
  tab-size: 4;
  font-variant-ligatures: none !important;
  font-feature-settings: "kern" 0, "liga" 0, "calt" 0, "dlig" 0 !important;
  font-kerning: none !important;
  text-rendering: geometricPrecision !important;
  z-index: 2;
  pointer-events: none;
  user-select: none;
}
.doc-line-number-content {
  display: block;
  will-change: transform;
}
.doc-line-number-row {
  position: relative;
  box-sizing: border-box;
}
.doc-line-number-label {
  position: absolute;
  top: 0;
  left: 0;
  width: 36px;
  text-align: right;
}
.doc-line-number-measure {
  position: absolute !important;
  visibility: hidden !important;
  pointer-events: none !important;
  left: -9999px !important;
  top: 0 !important;
  height: 0 !important;
  min-height: 0 !important;
  max-height: none !important;
  overflow: hidden !important;
  padding: 0 !important;
  border: 0 !important;
  resize: none !important;
  box-sizing: content-box !important;
  color: transparent !important;
  background: transparent !important;
}
/* Find marks live in the syntax-highlight overlay, which sits at
   z-index:0 under a transparent textarea — so they're always visible
   through the text layer. The previous color-mix variant could
   compute to near-invisible on themes with dark/desaturated accents;
   forced solid colors + !important so it ALWAYS pops. */
mark.doc-find-mark {
  background: var(--accent) !important;
  color: var(--bg) !important;
  border-radius: 2px;
  padding: 0 1px;
  box-shadow: 0 0 0 1px var(--accent);
}
mark.doc-find-mark.current {
  background: var(--accent) !important;
  color: var(--bg) !important;
  box-shadow: 0 0 0 2px var(--fg);
  outline: 1px solid var(--fg);
}

/* Find-match overlay rects — drawn on top of the textarea, work in
   every doc mode (markdown, email, plain) regardless of whether the
   syntax-highlight overlay is shown. Translucent band so the underlying
   text stays readable; current match gets a brighter solid band. */
.doc-find-rect {
  background: color-mix(in srgb, var(--accent) 25%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 65%, transparent);
}
.doc-find-rect.current {
  background: color-mix(in srgb, var(--accent) 55%, transparent);
  box-shadow: inset 0 0 0 2px var(--accent);
}

.doc-editor-highlight {
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  margin: 0;
  padding: 10px 12px 10px 48px;
  font-family: inherit;
  font-size: 11px !important;
  line-height: 1.45 !important;
  tab-size: 4;
  white-space: pre-wrap;
  word-wrap: break-word;
  overflow: hidden;
  /* `scrollbar-gutter: stable` reserves scrollbar-track width in the
     layout even when no scrollbar is shown. The textarea below has the
     same setting — without this, the textarea would consume scrollbar
     space the moment its content overflows vertically, shrinking its
     content width and wrapping lines earlier than the overlay. The
     visible-text-drift bug user reports as "after ~16 rows it wraps
     even though there's space" was caused by exactly that. */
  scrollbar-gutter: stable;
  scrollbar-width: none;
  pointer-events: none;
  z-index: 0;
  background: var(--hl-bg, var(--bg)) !important;
  color: var(--hl-fg, var(--fg));
  border: none;
  /* Disable ligatures + kerning everywhere in the editor. Monospace fonts
     like Fira Code form ligatures for `=>`, `!=`, `->`, `==` etc., but
     hljs splits those pairs into separate <span>s in the overlay, which
     breaks the ligature on this side while the textarea still forms it.
     Result: visible row drift whenever code contains those pairs. Pin
     ligatures off in both layers (textarea below) so widths stay equal. */
  font-variant-ligatures: none !important;
  font-feature-settings: "kern" 0, "liga" 0, "calt" 0, "dlig" 0 !important;
  font-kerning: none !important;
  text-rendering: geometricPrecision !important;
  box-sizing: border-box;
}
.doc-editor-highlight::-webkit-scrollbar { display: none; }

/* Document font size options */
.doc-font-m .doc-editor-textarea,
.doc-font-m .doc-editor-highlight,
.doc-font-m .doc-line-numbers { font-size: 13px !important; }
.doc-font-l .doc-editor-textarea,
.doc-font-l .doc-editor-highlight,
.doc-font-l .doc-line-numbers { font-size: 15px !important; }
.doc-email-richbody.doc-font-m { font-size: 15px !important; }
.doc-email-richbody.doc-font-l { font-size: 17px !important; }
/* Markdown base text should match chat text color, not code color */
.doc-editor-highlight .language-markdown {
  color: var(--fg) !important;
}
.doc-editor-highlight code,
.doc-editor-highlight code.hljs,
.doc-editor-highlight .hljs {
  font-family: inherit;
  font-size: inherit !important;
  line-height: inherit !important;
  background: transparent !important;
  padding: 0 !important;
  margin: 0 !important;
  border-radius: 0 !important;
  overflow: hidden !important;
  display: block;
  pointer-events: none;
}
.doc-editor-textarea {
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  width: 100%;
  height: 100% !important;
  max-height: none !important;
  min-height: 0 !important;
  z-index: 1;
  background: transparent !important;
  color: transparent !important;
  color-scheme: dark;
  caret-color: var(--fg);
  border: none;
  outline: none;
  resize: none;
  font-family: inherit;
  /* Caret position only matches the underlying highlight if BOTH layers use
     identical metrics — !important on font-size + line-height defends against
     anything else in the cascade nudging the textarea but not the overlay. */
  font-size: 11px !important;
  line-height: 1.45 !important;
  padding: 10px 12px 10px 48px;
  overflow-y: scroll;
  /* Pair with .doc-editor-highlight's scrollbar-gutter: stable so the
     textarea's content width DOESN'T shrink the moment its scrollbar
     appears (overflow-y: scroll keeps scrollbar permanent, gutter
     reserves the space layout-wise). Without this, line wrap diverges
     between textarea and overlay whenever content exceeds the visible
     area — caret stays right, but typed text appears on a different row
     than the caret. */
  scrollbar-gutter: stable;
  /* Show a real scrollbar for long documents. scrollbar-gutter above keeps
     the text column stable so the gutter, textarea, and find overlay stay
     metrically aligned while the scrollbar is present. */
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--fg) 28%, transparent) transparent;
  -webkit-overflow-scrolling: touch;
  tab-size: 4;
  white-space: pre-wrap;
  word-wrap: break-word;
  -moz-appearance: none;
  appearance: none;
  box-sizing: border-box;
  /* Mirror the ligature/kerning lockdown from .doc-editor-highlight above.
     Without this the textarea forms `=>`/`!=`/`->`/`==` as single-glyph
     ligatures while the overlay can't (hljs splits the pair into
     separate spans), so glyph widths diverge and the visible text drifts
     down relative to the caret as code accumulates. */
  font-variant-ligatures: none !important;
  font-feature-settings: "kern" 0, "liga" 0, "calt" 0, "dlig" 0 !important;
  font-kerning: none !important;
  text-rendering: geometricPrecision !important;
}
.doc-editor-textarea::-webkit-scrollbar { width: 8px; }
.doc-editor-textarea::-webkit-scrollbar-track { background: transparent; }
.doc-editor-textarea::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--fg) 24%, transparent);
  border-radius: 999px;
}
.doc-editor-textarea::-webkit-scrollbar-thumb:hover {
  background: color-mix(in srgb, var(--fg) 36%, transparent);
}
.doc-editor-textarea:hover,
.doc-editor-textarea:focus,
.doc-editor-textarea:active {
  background: transparent !important;
  /* Used to force `color: transparent` here so the hidden two-layer
     textarea wouldn't bleed through on hover/focus. Now that the
     textarea renders its OWN visible text (overlay is hidden), forcing
     transparent here makes typed text disappear the moment the cursor
     enters the page. Keep the visible fg color instead. */
  color: var(--fg) !important;
  outline: none !important;
}
.doc-editor-textarea::placeholder {
  color: var(--fg);
  opacity: 0.25;
}
/* Show real text when selecting so copy/paste is visible */
.doc-editor-textarea::selection {
  color: var(--fg);
  background: color-mix(in srgb, var(--color-accent) 30%, transparent);
}

/* ---- Selection indicator badge ---- */
.doc-selection-badge {
  font-size: 10px;
  color: var(--red);
  background: color-mix(in srgb, var(--red) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
  border-radius: 4px;
  padding: 1px 4px 1px 6px;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 4px;
}
.doc-selection-clear {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.5;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 0 2px;
}
.doc-selection-clear:hover {
  opacity: 1;
  color: var(--red, var(--color-error));
}
.doc-edit-tag {
  font-size: 0.75em;
  opacity: 0.5;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  border-radius: 4px;
  padding: 1px 5px;
  margin-right: 2px;
  white-space: nowrap;
}
/* Attachment cards in user messages */
.attach-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.attach-card {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border-radius: 6px;
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  /* Same border as the chat bubbles. */
  border: 1px solid var(--bubble-border, var(--border));
  font-size: 12px;
  transition: background 0.15s;
}
.attach-card[style*="cursor: pointer"]:hover,
.attach-card[data-file-id]:hover {
  background: color-mix(in srgb, var(--fg) 12%, transparent);
}
.attach-card-icon {
  flex-shrink: 0;
  opacity: 0.6;
}
.attach-card-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 200px;
}
.attach-card-size {
  opacity: 0.45;
  font-size: 11px;
  white-space: nowrap;
}
/* Import prompt banner (above chatbar) */
.import-prompt-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  margin: 0 auto 6px;
  max-width: 800px;
  background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-left: 3px solid var(--accent);
  border-radius: 6px;
  font-size: 12px;
  color: var(--fg);
  box-shadow: 0 4px 12px rgba(0,0,0,0.2);
  backdrop-filter: blur(12px);
  animation: modal-enter 0.15s ease-out;
}
.import-prompt-banner span { flex: 1; }
.import-prompt-banner button {
  padding: 3px 12px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: none;
  color: var(--fg);
  cursor: pointer;
  font-size: 12px;
  white-space: nowrap;
}
.import-prompt-banner button:hover {
  border-color: var(--fg);
}
.import-prompt-dismiss {
  border: none !important;
  background: none !important;
  opacity: 0.5;
  font-size: 16px !important;
  padding: 0 4px !important;
}
.import-prompt-dismiss:hover { opacity: 1; background: none !important; }
.doc-selection-overlay {
  position: absolute;
  background: color-mix(in srgb, var(--red) 10%, transparent);
  border-left: 2px solid color-mix(in srgb, var(--red) 50%, transparent);
  pointer-events: none;
  z-index: 0;
  transition: top 0.05s;
}

/* ── Suggestion comments (Google Docs style) ── */
.doc-suggestion-highlight {
  position: absolute;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-left: 3px solid var(--accent);
  pointer-events: none;
  z-index: 1;
  transition: top 0.1s, opacity 0.15s;
}
/* Suggestion card — fixed next to editor, anchored to the change */
.doc-suggestion-card {
  position: fixed;
  width: 250px;
  background: var(--panel);
  border: 1px solid var(--accent);
  border-radius: 10px;
  padding: 12px 12px 10px;
  font-size: 12px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.25);
  animation: suggestion-enter 0.25s ease-out;
  z-index: 250;
  overflow: visible;
}
/* Arrow pointing toward the editor */
.doc-suggestion-card::before {
  content: '';
  position: absolute;
  top: 16px;
  left: -10px;
  width: 18px;
  height: 18px;
  background: var(--panel);
  border-left: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(45deg);
  z-index: 1;
}
.doc-suggestion-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}
.doc-suggestion-nav {
  display: flex;
  align-items: center;
  gap: 4px;
}
.doc-suggestion-nav-btn {
  background: none;
  border: none;
  color: var(--fg);
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  padding: 2px 4px;
  opacity: 0.35;
  transition: opacity 0.1s;
}
.doc-suggestion-nav-btn:hover { opacity: 1; }
.doc-suggestion-close {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.3;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 6px 8px;
  margin: -6px -8px;
  border-radius: 6px;
  transition: opacity 0.1s, background 0.1s;
}
.doc-suggestion-close:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 8%, transparent); }
.doc-suggestion-counter {
  font-size: 10px;
  opacity: 0.4;
  font-weight: 600;
  letter-spacing: 0.5px;
}
/* Inline diff markers — injected into the code highlight element */
.sugg-inline-del {
  background: color-mix(in srgb, var(--red) 20%, transparent);
  color: color-mix(in srgb, var(--red) 70%, var(--fg));
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, var(--red) 40%, transparent);
  border-radius: 2px;
  padding: 0 2px;
}
.sugg-inline-add {
  background: color-mix(in srgb, var(--green) 25%, transparent);
  color: var(--green);
  border-radius: 2px;
  padding: 0 2px;
}
/* ---- Diff mode ---- */
.diff-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  background: color-mix(in srgb, var(--fg) 4%, var(--bg));
  border-bottom: 1px solid var(--border);
  font-size: 11px;
  flex-shrink: 0;
}
.diff-toolbar-status {
  opacity: 0.5;
  font-size: 10px;
  margin-right: auto;
}
.diff-toolbar-btn {
  background: none;
  border: 1px solid var(--border);
  color: var(--fg);
  font-size: 10px;
  padding: 3px 10px;
  border-radius: 4px;
  cursor: pointer;
  font-family: inherit;
  transition: border-color 0.15s, background 0.15s;
}
.diff-toolbar-btn:hover {
  border-color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent);
}
.diff-toolbar-btn-accept { color: var(--green); border-color: color-mix(in srgb, var(--green) 30%, transparent); }
.diff-toolbar-btn-accept:hover { border-color: var(--green); background: color-mix(in srgb, var(--green) 10%, transparent); }
.diff-toolbar-btn-reject { color: var(--red); border-color: color-mix(in srgb, var(--red) 30%, transparent); }
.diff-toolbar-btn-reject:hover { border-color: var(--red); background: color-mix(in srgb, var(--red) 10%, transparent); }
.diff-line-del {
  display: block;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-left: 3px solid var(--accent);
  padding-left: 4px;
  margin-left: -7px;
  text-decoration: line-through;
  opacity: 0.7;
}
.diff-line-add {
  display: block;
  background: color-mix(in srgb, var(--accent) 28%, transparent);
  border-left: 3px solid var(--accent);
  padding-left: 4px;
  margin-left: -7px;
}
/* Inline diff summary (version history cards) */
.diff-del {
  color: var(--accent);
  text-decoration: line-through;
  opacity: 0.7;
}
.diff-add {
  color: var(--accent);
  font-weight: 600;
}
.diff-line-equal {
  display: block;
}
.diff-chunk-resolved {
  opacity: 0.3;
  transition: opacity 0.3s;
}
.diff-chunk-actions {
  position: absolute;
  right: 8px;
  top: 0;
  display: flex;
  gap: 4px;
  z-index: 5;
  pointer-events: auto;
}
/* Diff mode: textarea sits on top of the highlight where chunk buttons live.
   Disable its pointer events so clicks reach the buttons in the layer below. */
.doc-editor-wrap.diff-mode .doc-editor-textarea {
  pointer-events: none;
}
.doc-editor-wrap.diff-mode .doc-editor-highlight {
  pointer-events: auto;
  z-index: 2;
}
.diff-chunk-btn {
  width: 20px;
  height: 20px;
  border-radius: 4px;
  border: 1px solid var(--border);
  background: var(--bg);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  line-height: 1;
  transition: border-color 0.15s, background 0.15s;
  opacity: 0.6;
}
.diff-chunk-btn:hover { opacity: 1; }
.diff-chunk-btn-accept { color: var(--green); }
.diff-chunk-btn-accept:hover { border-color: var(--green); background: color-mix(in srgb, var(--green) 15%, transparent); }
.diff-chunk-btn-reject { color: var(--red); }
.diff-chunk-btn-reject:hover { border-color: var(--red); background: color-mix(in srgb, var(--red) 15%, transparent); }

.doc-suggestion-accept-all {
  flex: 1;
  padding: 5px 8px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 10px;
  font-family: inherit;
  background: transparent;
  border: 1px solid var(--accent);
  color: var(--accent);
  transition: background 0.1s;
}
.doc-suggestion-accept-all:hover {
  background: color-mix(in srgb, var(--accent) 15%, transparent);
}
.doc-suggestion-reason {
  opacity: 0.6;
  margin-bottom: 6px;
  font-size: 11px;
  line-height: 1.4;
}
.doc-suggestion-diff {
  background: var(--bg);
  border-radius: 4px;
  padding: 6px 8px;
  font-family: var(--code-font, monospace);
  font-size: 11px;
  margin-bottom: 8px;
  max-height: 100px;
  overflow-y: auto;
  word-break: break-word;
}
.doc-suggestion-del {
  color: var(--red);
  text-decoration: line-through;
  opacity: 0.7;
}
.doc-suggestion-add {
  color: var(--green);
}
.doc-suggestion-actions {
  display: flex;
  gap: 6px;
}
.doc-suggestion-accept,
.doc-suggestion-dismiss {
  flex: 1;
  padding: 5px 8px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 11px;
  font-family: inherit;
  font-weight: 500;
  transition: background 0.1s;
}
.doc-suggestion-accept {
  background: var(--accent, var(--red));
  color: #fff;
}
.doc-suggestion-accept:hover {
  filter: brightness(1.15);
}
.doc-suggestion-dismiss {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg);
}
.doc-suggestion-dismiss:hover {
  background: color-mix(in srgb, var(--fg) 8%, transparent);
}
@keyframes suggestion-enter {
  from { opacity: 0; transform: translateX(10px); }
  to { opacity: 1; transform: translateX(0); }
}
/* Mobile: suggestion card overlays top of editor (no room on side) */
@media (max-width: 768px) {
  .doc-suggestion-card {
    right: 8px;
    right: 8px;
    width: auto;
    top: 8px !important;
  }
  .doc-suggestion-card::before { display: none; }
}

/* ---- Streaming animation ---- */
.doc-editor-textarea[readonly] {
  caret-color: var(--red);
}
.doc-editor-wrap.animating::after {
  content: '';
  position: absolute;
  top: 6px; right: 8px;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--red);
  animation: doc-pulse 0.8s ease-in-out infinite;
  z-index: 3;
  pointer-events: none;
}
@keyframes doc-pulse {
  0%, 100% { opacity: 0.3; transform: scale(0.8); }
  50% { opacity: 1; transform: scale(1.2); }
}

/* Diff overlay */
.doc-diff-overlay {
  position: absolute;
  inset: 0;
  background: var(--bg);
  z-index: 5;
  overflow-y: auto;
  padding: 8px 12px;
  font-family: 'Fira Code', monospace;
  font-size: 0.85rem;
  line-height: 1.5;
  opacity: 0;
  transition: opacity 0.3s ease;
  border-radius: 6px;
}
.doc-diff-overlay.visible { opacity: 1; }
.doc-diff-overlay.fading { opacity: 0; transition: opacity 0.4s ease; }
.doc-diff-stats {
  display: flex;
  gap: 10px;
  padding: 4px 0 8px;
  font-size: 0.8rem;
  font-weight: 600;
}
.diff-stat-del { color: var(--warn); }
.diff-stat-add { color: var(--green); }
.doc-diff-content { }
.doc-diff-line {
  padding: 1px 8px;
  border-radius: 4px;
  white-space: pre-wrap;
  word-break: break-all;
}
.doc-diff-line.same {
  opacity: 0.4;
}
.doc-diff-line.del {
  background: color-mix(in srgb, var(--warn) 15%, transparent);
  color: var(--warn);
  text-decoration: line-through;
  text-decoration-color: color-mix(in srgb, var(--warn) 40%, transparent);
}
.doc-diff-line.add {
  background: color-mix(in srgb, var(--green) 12%, transparent);
  color: var(--green);
}
.doc-diff-sep {
  text-align: center;
  padding: 2px 0;
  font-size: 0.7rem;
  opacity: 0.3;
  color: var(--fg);
}

/* Version history panel (slide-out) */
.doc-version-panel {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  width: 300px;
  background: var(--bg);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  z-index: 200;
  box-shadow: 4px 0 16px rgba(0,0,0,0.35);
  animation: version-slide-in 0.2s ease-out;
  /* Match the app modal aesthetic so nothing inherits the large body font. */
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 12px;
  letter-spacing: -0.01em;
}
@keyframes version-slide-in {
  from { opacity: 0; transform: translateX(-30px); }
  to { opacity: 1; transform: translateX(0); }
}
.doc-version-panel.hidden {
  display: none;
}
@media (max-width: 768px) {
  .doc-version-panel {
    left: 0;
    right: 0;
    top: auto;
    bottom: 0;
    width: 100%;
    height: 50vh;
    border-right: none;
    border-left: none;
    border-top: 1px solid var(--border);
    border-radius: 14px 14px 0 0;
    box-shadow: 0 -4px 16px rgba(0,0,0,0.3);
    /* It's a bottom sheet on mobile — slide UP from the bottom, not in from
       the left (the desktop animation looked like a black box janking in). */
    animation: version-slide-up 0.2s ease-out;
  }
}
@keyframes version-slide-up {
  from { opacity: 0; transform: translateY(30px); }
  to   { opacity: 1; transform: translateY(0); }
}

.doc-version-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  font-size: 12px;
  font-weight: 600;
  color: var(--fg);
}

.doc-version-list {
  flex: 1;
  overflow-y: auto;
  padding: 8px;
}

.doc-version-item {
  padding: 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 6px;
  cursor: pointer;
  transition: all 0.15s;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
}
.doc-version-item:hover {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border-color: var(--fg);
}

.doc-version-info {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}

.doc-version-num {
  font-weight: 600;
  font-size: 12px;
  color: var(--fg);
}
.doc-version-source {
  font-size: 10px;
  color: var(--fg);
  opacity: 0.5;
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  padding: 1px 6px;
  border-radius: 4px;
}
.doc-version-time {
  font-size: 10px;
  color: var(--fg);
  opacity: 0.4;
  margin-left: auto;
}
.doc-version-summary {
  font-size: 11px;
  color: var(--fg);
  opacity: 0.5;
  margin-bottom: 4px;
}

.doc-version-restore {
  background: none;
  border: 1px solid var(--border);
  color: var(--fg);
  font-size: 10px;
  padding: 2px 8px;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.15s;
}
.doc-version-restore:hover {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border-color: var(--fg);
}
/* "latest" badge */
.doc-version-latest {
  font-size: 10px;
  font-weight: 600;
  color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
  padding: 1px 6px;
  border-radius: 4px;
}
/* Diff preview lines — small and muted so they don't dominate the item. */
.doc-version-diff {
  font-size: 10px;
  line-height: 1.5;
  opacity: 0.8;
  margin-top: 2px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  word-break: break-word;
}
.doc-version-diff .diff-del { color: var(--red); opacity: 0.75; }
.doc-version-diff .diff-add { color: #3fb950; }

/* Mobile: doc editor takes over full screen as toggle */
@media (max-width: 768px) {
  body.doc-view .doc-editor-pane {
    /* Force its own full-screen window on mobile. !important on these so an
       inline width/position left over from desktop drag-resize, or the base
       desktop split layout (flex: 1; max-width: 70vw; border-left), can never
       render it as a narrow side pane ("sidebar") on a phone. */
    position: fixed !important;
    inset: 0 !important;
    top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important;
    max-width: 100% !important;
    width: 100% !important;
    flex: none !important;
    z-index: 170;
    /* Stroke the top edge so the rounded corners read as a curved sheet edge. */
    border: 1px solid var(--border);
    border-bottom: none;
    /* Rounded top corners like the other mobile sheet windows. */
    border-radius: 14px 14px 0 0;
    /* Slide up from the bottom (sheet), not in from the side, on mobile. */
    animation: sheet-enter 0.25s cubic-bezier(0.2, 0.8, 0.2, 1) both;
    transform-origin: bottom center;
  }
  body.doc-view .doc-divider {
    display: none;
  }
  /* Hide chat behind doc panel on mobile */
  body.doc-view .chat-container {
    display: none;
  }
  /* Doc + email windows alternate: whichever was opened last is in front.
     Default (a doc was opened last) → email windows sit BELOW the full-screen
     doc pane (170) so the draft is on top. When the user re-opens an email
     window (body.email-front, set by openEmailLibrary / reader open+restore) →
     the email windows jump ABOVE the doc. Covers the email library AND any open
     reader window (email-reader-<uid>); both are .modal at z-250 by default. */
  body.doc-view #email-lib-modal,
  body.doc-view .modal[id^="email-reader-"] { z-index: 150 !important; }
  body.doc-view.email-front #email-lib-modal,
  body.doc-view.email-front .modal[id^="email-reader-"] { z-index: 300 !important; }
  /* Hide new-session button and hamburger when doc editor is open on mobile */
  body.doc-view .mobile-new-chat-btn,
  /* Hide the global hamburger while Compare Mode is active — the compare
   header has its own close button in the top-right, and overlapping the
   two looks like a bug. The .compare-active class lives on the chat
   container, not body, so use :has(). */
body:has(.compare-active) .hamburger-btn { display: none !important; }
/* Mobile: hide the sidebar hamburger when a document panel (or notes pane)
   is open — those sheets cover the whole screen on mobile, so a floating
   hamburger sticking out over them is just clutter / mis-tap bait. */
@media (max-width: 768px) {
  body.doc-view .hamburger-btn,
  body:has(#notes-pane) .hamburger-btn { display: none !important; }
}
  /* Make room for hamburger button alongside the tab/header bars (fallback if shown) */
  body.doc-view.sidebar-collapsed.hamburger-left .doc-tab-bar,
  body.doc-view.sidebar-collapsed.hamburger-left .doc-editor-header,
  body.doc-view.sidebar-collapsed.hamburger-left .doc-md-toolbar {
    padding-left: 44px;
  }
  body.doc-view.sidebar-collapsed.hamburger-right .doc-tab-bar,
  body.doc-view.sidebar-collapsed.hamburger-right .doc-editor-header,
  body.doc-view.sidebar-collapsed.hamburger-right .doc-md-toolbar {
    padding-right: 44px;
  }
  /* ── Tab bar — match header height, bigger touch targets ── */
  .doc-tab-bar {
    padding: 0;
    height: 40px;
  }
  .doc-tab {
    padding: 0 12px;
    font-size: 13px;
  }
  /* Bigger × touch target on mobile. */
  .doc-tab-close {
    font-size: 22px;
    padding: 0 8px;
    opacity: 0.5;
  }
  .doc-tab .doc-tab-menu-btn {
    opacity: 0.4 !important;
    padding: 4px 6px !important;
  }
  .doc-tab .doc-tab-menu-btn svg {
    width: 14px !important;
    height: 14px !important;
  }
  /* Footer is identical to desktop now, so the separate mobile Close/Copy
     footer is dropped and the per-tab × stays (matching desktop). */
  .doc-mobile-footer { display: none !important; }
  .doc-tab-new {
    font-size: 13px !important;
    padding: 0 14px !important;
    gap: 4px;
  }
  /* No hamburger padding — it's hidden in doc-view */
  body.doc-view.sidebar-collapsed.hamburger-left .doc-tab-bar,
  body.doc-view.sidebar-collapsed.hamburger-left .doc-editor-header,
  body.doc-view.sidebar-collapsed.hamburger-left .doc-md-toolbar {
    padding-left: 0 !important;
  }
  body.doc-view.sidebar-collapsed.hamburger-right .doc-tab-bar,
  body.doc-view.sidebar-collapsed.hamburger-right .doc-editor-header,
  body.doc-view.sidebar-collapsed.hamburger-right .doc-md-toolbar {
    padding-right: 0 !important;
  }
  /* ── Header — identical layout to desktop (left-aligned), match tab height ── */
  .doc-editor-header {
    padding: 6px 12px 6px 8px;
    gap: 6px;
    min-height: 40px;
  }
  .doc-import-label,
  #doc-version-badge {
    display: none !important;
  }
  /* ── Markdown toolbar — same height as tab bar ── */
  .doc-md-toolbar {
    height: 40px !important;
    padding: 4px 8px;
    gap: 2px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  /* The toolbar's padding-left is forced to 0 by the hamburger rules, so give
     the Edit/Preview toggle its own left margin to clear the screen edge +
     the 18px mask fade. Margin isn't overridden by those !important paddings. */
  .doc-md-toolbar .md-view-toggle { margin-left: 8px; }
  .doc-md-toolbar button {
    font-size: 13px !important;
    padding: 6px 10px !important;
    min-width: 34px;
    min-height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }
  .md-toolbar-sep {
    height: 18px;
    margin: 0 4px;
  }
}

/* Opacity slider in the theme tabs row — lets the user see the page behind
   the modal while tweaking colors. JS toggles .hidden based on active tab. */
.theme-opacity-wrap {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-left: auto;
  margin-right: 6px;
  padding: 2px 9px;
  border: 1px solid var(--border);
  border-radius: 999px;
  opacity: 0.65;
  transition: opacity 0.15s, border-color 0.15s, background 0.15s, color 0.15s;
  height: 22px;
  align-self: center;
  /* Now a <button> toggle, not a slider wrapper. */
  cursor: pointer;
  color: var(--fg);
  font: inherit;
  background: transparent;
}
.theme-opacity-wrap.hidden { display: none; }
.theme-opacity-wrap:hover { opacity: 1; }
/* The title h4 already carries margin-right:auto to group header controls
   on the right. BOTH the Peek button AND the injected minimize button also
   have margin-left:auto — and multiple competing auto-margins split the
   free space, stranding Peek in the middle. Zero their left-autos here so
   the h4 alone pushes Peek + minimize + close together, flush right. */
.modal-header .theme-opacity-wrap,
.modal-header .modal-minimize-btn { margin-left: 0 !important; }
/* On = peeking through; highlight with the accent so the state is obvious. */
.theme-opacity-wrap.active {
  opacity: 1;
  border-color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
  color: var(--accent, var(--red));
}
.theme-opacity-label { font-size: 10px; font-weight: 600; letter-spacing: 0.02em; }
.theme-opacity-wrap > svg { flex-shrink: 0; opacity: 0.7; }
.theme-opacity-wrap input[type="range"] {
  width: 92px;
  height: 4px;
  -webkit-appearance: none;
  appearance: none;
  background: color-mix(in srgb, var(--fg) 18%, transparent);
  border-radius: 2px;
  outline: none;
  margin: 0;
}
.theme-opacity-wrap input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent, var(--red));
  border: none;
  cursor: pointer;
}
.theme-opacity-wrap input[type="range"]::-moz-range-thumb {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent, var(--red));
  border: none;
  cursor: pointer;
}

/* ── Theme editor: hover-to-highlight zone ── */
.theme-zone-highlight {
  position: fixed;
  pointer-events: none;
  z-index: 9998;
  border: 2px dashed var(--accent, var(--red));
  border-radius: 6px;
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent, var(--red)) 40%, transparent),
              0 0 18px color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
  animation: theme-zone-pulse 1.4s ease-in-out infinite;
}
@keyframes theme-zone-pulse {
  0%, 100% { opacity: 0.95; }
  50%      { opacity: 0.55; }
}
/* Highlight the color row itself too so the user has a strong visual link
   between the input they're hovering and the zone overlay on the page. */
#theme-tab-customize .color-row {
  transition: background 0.15s, border-color 0.15s;
  border-radius: 6px;
}
#theme-tab-customize .color-row:hover {
  background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent);
}

/* "Auto-saved" pill that flashes after each color/font change. */
.theme-autosaved-pill {
  position: sticky;
  bottom: 8px;
  margin: 8px auto 0;
  width: fit-content;
  display: flex;
  align-items: center;
  gap: 5px;
  background: color-mix(in srgb, var(--color-success, #4caf50) 18%, var(--bg));
  color: var(--color-success, #4caf50);
  border: 1px solid color-mix(in srgb, var(--color-success, #4caf50) 40%, transparent);
  border-radius: 12px;
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 500;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.2s ease-out, transform 0.2s ease-out;
  pointer-events: none;
  z-index: 5;
}
.theme-autosaved-pill.visible {
  opacity: 1;
  transform: translateY(0);
}

/* ---- Doc fullscreen ---- */
.doc-editor-pane.doc-fullscreen {
  flex: 1;
  max-width: 100%;
  width: 100% !important;
  border-left: none;
}
/* Keep the hamburger reachable in fullscreen — version history can still
   hide it (the panel covers that area). */
body:has(.doc-version-panel:not(.hidden)) .hamburger-btn {
  display: none !important;
}

/* ---- Document run output ---- */
.doc-run-output {
  border-top: 2px solid var(--hl-function, #61afef);
  background: var(--bg);
  padding: 6px 12px 8px;
  max-height: 200px;
  overflow: auto;
  font-family: 'Fira Code', 'Courier New', monospace;
  font-size: 0.85em;
  line-height: 1.5;
  position: relative;
}
.doc-run-output .code-runner-pre { background: none !important; border: none !important; margin: 0; padding: 0; }
.doc-run-output .code-runner-error { color: var(--red); }
.doc-run-output .code-runner-loading { font-style: italic; color: var(--red); }
.doc-run-output .code-runner-close { position: absolute; top: 4px; right: 4px; background: none; border: none; color: var(--fg); cursor: pointer; opacity: 0.5; font-size: 14px; padding: 2px 6px; }
.doc-run-output .code-runner-close:hover { opacity: 1; }
.doc-run-pre { color: var(--fg); white-space: pre-wrap; word-break: break-word; margin: 0; }
.doc-run-error { color: var(--red); white-space: pre-wrap; word-break: break-word; margin: 0; }

/* ---- Markdown preview ---- */
.doc-md-preview {
  flex: 1;
  overflow-y: auto;
  padding: 16px 20px;
  font-family: inherit;
  font-size: 12px;
  line-height: 1.6;
  color: var(--fg);
  background: var(--bg);
}
.doc-md-preview pre {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px;
  overflow-x: auto;
}
.doc-md-preview code {
  font-size: 0.95em;
}
.doc-md-preview p { margin: 0.6em 0; }
.doc-md-preview h1, .doc-md-preview h2, .doc-md-preview h3 {
  margin: 0.8em 0 0.4em;
  color: var(--hl-string);
}
.doc-md-preview ul, .doc-md-preview ol {
  margin-left: 20px;
  margin-bottom: 0.6em;
}
.doc-md-preview blockquote {
  border-left: 3px solid var(--border);
  padding-left: 12px;
  margin: 0.6em 0;
  opacity: 0.8;
}

/* ---- Active state for doc action icon buttons ---- */
.doc-action-icon-btn.active {
  opacity: 1;
  color: var(--red);
  background: color-mix(in srgb, var(--red) 12%, transparent);
}
#doc-run-btn:hover {
  color: var(--hl-function, #61afef);
}

/* ===== ADMIN PANEL (inside settings modal) ===== */

/* Cards */
.admin-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  margin-bottom: 10px;
}
.admin-card h2 {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.03em;
  margin-bottom: 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent);
}
.admin-danger-card {
  border-color: color-mix(in srgb, var(--color-error) 27%, transparent);
}

/* Toggle switch */
.admin-toggle-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.admin-toggle-label {
  font-size: 13px;
  font-weight: 500;
}
.admin-toggle-sub {
  /* 65% mix keeps this helper text above WCAG AA 4.5:1 on the dark panel
     (50% only reached ~3.9:1). */
  color: color-mix(in srgb, var(--fg) 65%, transparent);
  font-size: 11px;
  margin-top: 2px;
}
/* Hide the native up/down spinners on the Max auto-skills number input. */
#skill-max-input {
  -moz-appearance: textfield;
  appearance: textfield;
}
#skill-max-input::-webkit-inner-spin-button,
#skill-max-input::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.admin-switch {
  position: relative;
  width: 30px;
  height: 16px;
  flex-shrink: 0;
  display: inline-block;
}
.admin-switch input {
  opacity: 0;
  width: 0;
  height: 0;
}
.admin-slider {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--fg) 50%, transparent);
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.08s;
}
.admin-slider::before {
  content: '';
  position: absolute;
  left: 2px;
  top: 2px;
  width: 12px;
  height: 12px;
  background: var(--panel);
  border-radius: 50%;
  transition: transform 0.08s;
  box-shadow: 0 1px 2px rgba(0,0,0,0.25);
}
.admin-switch input:checked + .admin-slider {
  background: var(--red);
}
.admin-switch input:checked + .admin-slider::before {
  transform: translateX(14px);
}

/* User rows */
.admin-user-row {
  display: flex;
  flex-direction: column;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-bottom: 6px;
  transition: border-color 0.15s;
}
.admin-user-row:hover {
  border-color: color-mix(in srgb, var(--fg) 20%, var(--border));
}
.admin-user-info {
  display: flex;
  align-items: center;
  gap: 8px;
}
.admin-user-name { font-size: 13px; font-weight: 500; }
/* Privilege panel */
.admin-priv-panel {
  max-height: 600px;
  transition: max-height 0.3s ease, opacity 0.2s ease, padding 0.2s ease;
  overflow: hidden;
}
.admin-priv-panel.hidden {
  max-height: 0 !important;
  opacity: 0;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  margin-top: 0 !important;
  border-top: none !important;
}
/* Privilege toggle rows */
.admin-priv-panel [data-priv] {
  accent-color: var(--accent, var(--red));
}
/* Section headers */
.admin-priv-section {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  opacity: 0.35;
  font-weight: 600;
  margin: 10px 0 4px;
}
.admin-priv-section:first-child {
  margin-top: 0;
}
/* Model checkbox list */
.priv-models-list {
  max-height: 150px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 6px;
  background: var(--bg);
  scrollbar-width: thin;
}
.priv-models-list::-webkit-scrollbar {
  width: 4px;
}
.priv-models-list::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--fg) 15%, transparent);
  border-radius: 2px;
}
.priv-models-list label {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 2px;
  font-size: 11px;
  cursor: pointer;
  border-radius: 3px;
  transition: background 0.1s;
}
.priv-models-list label:hover {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
}
.priv-models-list input[type="checkbox"] {
  accent-color: var(--accent, var(--red));
  margin: 0;
  flex-shrink: 0;
}
/* All/None links */
.priv-models-all,
.priv-models-none {
  font-size: 10px;
  opacity: 0.5;
  color: var(--fg);
  text-decoration: none;
  cursor: pointer;
  transition: opacity 0.1s;
}
.priv-models-all:hover,
.priv-models-none:hover {
  opacity: 1;
}

/* MCP tool toggles panel */
.mcp-tools-panel {
  width: 100%;
  padding: 8px 0 4px;
  border-top: 1px solid var(--border);
  margin-top: 8px;
  max-height: 600px;
  transition: max-height 0.3s ease, opacity 0.2s ease, padding 0.2s ease;
  overflow: hidden;
}
.mcp-tools-panel.hidden {
  max-height: 0 !important;
  opacity: 0;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  margin-top: 0 !important;
}
.mcp-tools-panel .mcp-tools-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 6px;
}
.mcp-tools-panel .mcp-tools-header span:first-child {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  opacity: 0.35;
  font-weight: 600;
}
.mcp-tools-panel .mcp-tools-header a {
  font-size: 10px;
  opacity: 0.5;
  color: var(--fg);
  text-decoration: none;
  cursor: pointer;
  transition: opacity 0.1s;
}
.mcp-tools-panel .mcp-tools-header a:hover {
  opacity: 1;
}
.mcp-tools-list {
  max-height: 200px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 6px;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
}
.mcp-tools-list::-webkit-scrollbar {
  width: 4px;
}
.mcp-tools-list::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--fg) 15%, transparent);
  border-radius: 2px;
}
.mcp-tools-list label {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px;
  font-size: 11px;
  cursor: pointer;
  border-radius: 3px;
  min-width: 0;
}
.mcp-tools-list label > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}
.mcp-tools-list label:hover {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
}
.mcp-tools-list input[type="checkbox"] {
  accent-color: var(--accent, var(--red));
  margin: 0;
  flex-shrink: 0;
}
/* Dot-style toggle (mirrors .note-check-dot) for the Add Models row,
   replacing the native checkbox while keeping it for click handling. */
.adm-cb-hidden {
  position: absolute;
  width: 0;
  height: 0;
  opacity: 0;
  pointer-events: none;
}
.adm-check-dot {
  /* Lock the dot to a fixed 12×12 box. The parent rule
     `.mcp-tools-list label > span { flex: 1 }` was stretching this span
     to fill the row when it didn't have its own flex override. */
  flex: 0 0 12px !important;
  width: 12px !important;
  height: 12px;
  min-width: 12px;
  max-width: 12px;
  box-sizing: border-box;
  border-radius: 50%;
  border: 1.5px solid color-mix(in srgb, var(--fg) 35%, transparent);
  position: relative;
  transition: background 0.2s, border-color 0.2s, transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.adm-check-dot::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 45%;
  width: 5px;
  height: 2.5px;
  border-left: 1.5px solid #fff;
  border-bottom: 1.5px solid #fff;
  transform: translate(-50%, -50%) rotate(-45deg) scale(0);
  transform-origin: center;
  transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.adm-model-row:hover .adm-check-dot {
  border-color: var(--accent, var(--red));
  transform: scale(1.15);
}
.adm-model-row:active .adm-check-dot {
  transform: scale(0.9);
}
.adm-cb-hidden:checked + .adm-check-dot {
  background: var(--accent, var(--red));
  border-color: var(--accent, var(--red));
}
.adm-cb-hidden:checked + .adm-check-dot::after {
  transform: translate(-50%, -50%) rotate(-45deg) scale(1);
}
/* Disabled endpoints — dim the row but keep the toggle/delete buttons
   at full opacity so the user can re-enable or remove without squinting. */
.admin-user-row.admin-ep-disabled { opacity: 0.55; }


/* Most recently added endpoint — brief accent glow so the user can
   spot the new row immediately after Adding / Find. Fades out cleanly. */
@keyframes adm-ep-just-added-glow {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent, var(--red)) 55%, transparent); background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent); }
  60%  { box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent, var(--red)) 0%, transparent); background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent); }
  100% { box-shadow: 0 0 0 0 transparent; background: transparent; }
}
.admin-user-row.adm-ep-just-added {
  border-radius: 8px;
  animation: adm-ep-just-added-glow 2.2s ease-out;
}
.admin-user-row.admin-ep-disabled .admin-btn-sm,
.admin-user-row.admin-ep-disabled .admin-btn-delete,
.admin-user-row.admin-ep-disabled .admin-badge-off { opacity: 1; }
/* Local / API subsection labels inside the Endpoints card */
.adm-ep-section-head {
  display: flex;
  align-items: center;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fg);
  opacity: 0.5;
  margin-bottom: 4px;
}

/* Collapsible Add-Models subsections (API / Local) — the whole header row
   acts as a toggle so a long cloud-API form can be tucked away when you
   only want to paste a local URL. */
.adm-section-toggle {
  cursor: pointer;
  user-select: none;
  opacity: 0.8;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 9px;
  margin-bottom: 6px;
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  transition: border-color 0.12s, background 0.12s, opacity 0.12s;
}
.adm-section-toggle:hover {
  opacity: 1;
  border-color: var(--red);
  background: color-mix(in srgb, var(--red) 8%, transparent);
}
.adm-section-toggle .adm-section-caret { opacity: 0.6; }
.adm-section-toggle:focus-visible { outline: 1px solid var(--red); outline-offset: 1px; }
/* When expanded, square off the bottom so the header reads as attached to
   the form it controls. */
.adm-add-section:not(.collapsed) .adm-section-toggle {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  margin-bottom: 0;
}
.adm-section-caret {
  margin-left: auto;
  flex-shrink: 0;
  transition: transform 0.18s ease;
}
/* Collapsed: hide the form body and point the caret right. */
.adm-add-section.collapsed .admin-model-form { display: none; }
.adm-add-section.collapsed .adm-section-caret { transform: rotate(-90deg); }
.adm-quickstart-section {
  margin-top: 7px;
}
.adm-quickstart-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
  opacity: 0.72;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 8px;
  font-size: 11px;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
}
.adm-quickstart-toggle:hover {
  opacity: 1;
  border-color: var(--red);
}
.adm-quickstart-section:not(.collapsed) .adm-quickstart-toggle {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}
.adm-quickstart-body {
  display: flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--border);
  border-top: 0;
  border-radius: 0 0 6px 6px;
  padding: 6px 8px;
}
.adm-quickstart-section.collapsed .adm-quickstart-body { display: none; }
.adm-quickstart-section.collapsed .adm-section-caret { transform: rotate(-90deg); }

/* Custom provider picker (logo + name) replacing the native <select> */
.adm-provider-picker { position: relative; margin-bottom: 6px; }
.adm-provider-combo {
  display: flex;
  align-items: stretch;
}
.adm-provider-combo input {
  flex: 1;
  min-width: 0;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.adm-provider-btn {
  width: 100%;
  display: flex; align-items: center; gap: 8px;
  background: var(--bg); color: var(--fg);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 5px 8px; font-family: inherit; font-size: 12px;
  cursor: pointer; text-align: left;
}
.adm-provider-combo .adm-provider-btn {
  width: 128px;
  flex-shrink: 0;
  border-left: 0;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  justify-content: space-between;
}
.adm-provider-btn:hover { border-color: color-mix(in srgb, var(--fg) 30%, var(--border)); }
.adm-provider-current { flex: 1; display: flex; align-items: center; gap: 8px; min-width: 0; }
.adm-provider-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.adm-provider-logo {
  width: 14px; height: 14px;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; color: var(--fg); opacity: 0.85;
}
.adm-provider-logo svg { width: 14px; height: 14px; }
.adm-provider-logo:empty {
  background: color-mix(in srgb, var(--fg) 12%, transparent);
  border-radius: 50%;
}
.adm-provider-caret { flex-shrink: 0; opacity: 0.5; transition: transform 0.15s; }
.adm-provider-picker:has(.adm-provider-menu:not(.hidden)) .adm-provider-caret {
  transform: rotate(180deg);
}
.adm-provider-menu {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 100;
  max-height: 280px; overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--border); border-radius: 6px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
  padding: 4px;
}
.adm-provider-menu.hidden { display: none; }
.adm-provider-item {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 8px; border-radius: 4px;
  font-size: 12px; cursor: pointer;
}
.adm-provider-item:hover { background: color-mix(in srgb, var(--fg) 8%, transparent); }
.adm-provider-item.active { background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent); }

/* When the Appearance tab is open, the #settings-modal-opacity slider (in
   the header) fades the modal so the user can see the rest of the UI react
   as toggles flip. The fade is applied as a background color-mix in JS
   (settings.js) rather than element opacity, so the controls/text stay
   crisp — mirroring the Theme customizer's opacity slider. */

/* Search provider fallback chain — chips + drag-reorder */
.search-fallback-chain {
  flex: 1; display: flex; align-items: center;
  flex-wrap: wrap; gap: 6px; min-height: 28px;
}
.search-fb-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 4px 3px 6px;
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border: 1px solid var(--border); border-radius: 10px;
  font-size: 11px; cursor: grab; user-select: none;
}
.search-fb-chip.dragging { opacity: 0.4; }
.search-fb-grip { opacity: 0.35; font-size: 10px; line-height: 1; cursor: grab; }
.search-fb-logo {
  width: 12px; height: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; opacity: 0.85;
}
.search-fb-logo svg { width: 12px; height: 12px; }
.search-fb-remove {
  background: none; border: none; color: var(--fg);
  opacity: 0.45; cursor: pointer;
  font-size: 14px; line-height: 1; padding: 0 2px;
  transition: opacity 0.15s, color 0.15s;
}
.search-fb-remove:hover { opacity: 1; color: var(--red); }
.search-fb-add {
  background: var(--bg); color: var(--fg);
  border: 1px dashed var(--border); border-radius: 10px;
  padding: 2px 6px; font-family: inherit; font-size: 11px;
  outline: none; cursor: pointer;
}
.search-fb-add:focus,
.search-fb-add:hover {
  border-color: var(--accent, var(--red));
  border-style: solid;
}

.mcp-tools-search {
  width: 100%;
  box-sizing: border-box;
  margin-bottom: 6px;
  padding: 5px 8px;
  font-size: 11px;
  font-family: inherit;
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg);
  outline: none;
}
.mcp-tools-search:focus {
  border-color: color-mix(in srgb, var(--accent) 60%, var(--border));
}
.mcp-tools-count {
  font-size: 11px;
  font-weight: 600;
  opacity: 0.7;
}

/* Badges */
.admin-badge {
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--red) 20%, transparent);
  color: var(--red);
  font-weight: 600;
}
.admin-badge-off {
  background: color-mix(in srgb, var(--color-error) 20%, transparent);
  color: var(--color-error);
}

/* Buttons */
.admin-btn-delete {
  background: none;
  border: 1px solid color-mix(in srgb, var(--color-error) 27%, transparent);
  color: var(--color-error);
  padding: 4px 10px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 11px;
  font-family: inherit;
  transition: all 0.15s;
}
.admin-btn-delete:hover {
  background: var(--color-error);
  border-color: var(--color-error);
  color: #fff;
}
.admin-btn-add {
  padding: 4px 10px;
  border: none;
  border-radius: 6px;
  background: var(--red);
  color: #fff;
  cursor: pointer;
  font-weight: 600;
  font-size: 11px;
  font-family: inherit;
  transition: all 0.15s;
}
.admin-btn-add:hover {
  background: color-mix(in srgb, var(--red) 80%, white);
}
.admin-btn-add:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.admin-btn-sm {
  padding: 3px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel);
  color: var(--fg);
  cursor: pointer;
  font-size: 11px;
}
.admin-btn-sm:hover {
  background: var(--border);
  border-color: var(--red);
}
.admin-spinner {
  display: inline-block;
  width: 12px;
  height: 12px;
  border: 2px solid var(--border);
  border-top-color: var(--red);
  border-radius: 50%;
  animation: admin-spin 0.6s linear infinite;
  vertical-align: -2px;
  margin-right: 2px;
}
@keyframes admin-spin {
  to { transform: rotate(360deg); }
}

/* Forms */
.admin-add-form {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.admin-add-form input {
  flex: 1;
  min-width: 120px;
  padding: 5px 8px;
  height: 32px;
  box-sizing: border-box;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  font-family: inherit;
  font-size: 12px;
}
.admin-add-form input:focus { outline: none; border-color: var(--red); }
.admin-switch-inline {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  cursor: default;
}

.admin-model-form {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 8px;
}
.admin-model-form input,
.admin-model-form select {
  padding: 5px 8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  font-size: 12px;
}
.admin-model-form input:focus { outline: none; border-color: var(--red); }
.admin-model-form-row {
  display: flex;
  gap: 6px;
}
.admin-model-form-row input { flex: 1; }
.adm-ep-inline-msg {
  min-height: 16px;
  margin-top: 5px;
  font-size: 11px;
}

/* Endpoint items */
.admin-ep-item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  background: var(--bg);
  border-radius: 6px;
  margin-bottom: 4px;
  font-size: 12px;
}
.admin-ep-info { flex: 1; overflow: hidden; }
.admin-ep-name { color: var(--fg); font-weight: 500; }
.admin-ep-detail {
  color: color-mix(in srgb, var(--fg) 40%, transparent);
  font-size: 10px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.admin-ep-actions {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
}

/* Status messages */
.admin-error { color: var(--color-error); font-size: 11px; margin-top: 4px; }
.admin-success { color: var(--color-success); font-size: 11px; margin-top: 4px; }
.admin-empty {
  color: color-mix(in srgb, var(--fg) 40%, transparent);
  font-size: 12px;
  padding: 10px 0;
  text-align: center;
}
/* Endpoint-list empty states ("No endpoints configured" / "None") get a
   left-aligned, accent-colored treatment so they read as "this row is your
   placeholder, click Add" rather than a centered "nothing here" grey blob. */
#adm-epList-local .admin-empty,
#adm-epList-api .admin-empty {
  text-align: left;
  padding: 8px 4px;
  color: var(--accent-primary, var(--accent, var(--red)));
}

/* RAG upload zone */
.admin-rag-upload-zone {
  border: 2px dashed var(--border);
  border-radius: 8px;
  padding: 14px;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s;
  margin-bottom: 8px;
}
.admin-rag-upload-zone:hover,
.admin-rag-upload-zone.dragover {
  border-color: var(--red);
  background: color-mix(in srgb, var(--red) 7%, transparent);
}
.admin-rag-upload-zone p {
  color: color-mix(in srgb, var(--fg) 50%, transparent);
  font-size: 11px;
  margin-top: 4px;
}
.admin-rag-icon { font-size: 18px; }
.admin-rag-dir-row {
  display: flex;
  gap: 6px;
  margin-bottom: 8px;
}
.admin-rag-dir-row input {
  flex: 1;
  padding: 5px 8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  font-size: 12px;
}
.admin-rag-dir-row input:focus { outline: none; border-color: var(--red); }
.admin-rag-dir-row button {
  padding: 5px 8px;
  border: none;
  border-radius: 6px;
  background: var(--red);
  color: #fff;
  cursor: pointer;
  font-size: 12px;
  white-space: nowrap;
}
.admin-rag-toolbar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 6px;
}
.admin-rag-section-label {
  font-size: 10px;
  color: color-mix(in srgb, var(--fg) 50%, transparent);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin: 8px 0 4px;
}
.admin-rag-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 8px;
  background: var(--bg);
  border-radius: 6px;
  margin-bottom: 4px;
  font-size: 12px;
}
.admin-rag-item-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--fg);
}
.admin-rag-item-meta {
  color: color-mix(in srgb, var(--fg) 40%, transparent);
  font-size: 10px;
  margin: 0 6px;
  flex-shrink: 0;
}
.admin-rag-item .admin-btn-delete { font-size: 10px; padding: 2px 6px; }
.admin-rag-status {
  font-size: 11px;
  color: color-mix(in srgb, var(--fg) 50%, transparent);
  margin-top: 6px;
}

/* Token reveal */
.admin-token-reveal {
  margin-top: 8px;
  padding: 8px;
  background: color-mix(in srgb, var(--red) 8%, var(--bg));
  border: 1px solid color-mix(in srgb, var(--red) 25%, var(--border));
  border-radius: 8px;
}

/* Selects in admin cards */
.admin-card select {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  font-size: 12px;
}
.admin-card input {
  font-family: inherit;
  font-size: 12px;
}
.admin-card label {
  font-size: 12px;
}

/* ---- Document Library ---- */

.doclib-modal-content {
  width: min(600px, 92vw);
  max-height: 85vh;
  font-size: 12px;
  background: var(--bg);
}
/* Anchor doclib modal to top so only the bottom edge moves on resize */
#doclib-modal {
  align-items: flex-start;
  padding-top: 8vh;
}
.doclib-modal-content .modal-header h4 {
  font-size: 1rem;
}
/* The "open in new tab" email modal inherited the 1rem doclib header, which
   read way bigger than the email subject in the regular (inline) reader. Pin
   it smaller so the two views are consistent. */
.email-reader-tab-modal .modal-header h4 {
  font-size: 13px;
  font-weight: 600;
}
/* Match the sticky modal-header to the doclib modal-content body color
   (var(--bg)) instead of the global var(--panel) default — otherwise the
   header reads as a darker stripe above the email list. */
.doclib-modal-content > .modal-header {
  background: var(--bg);
}
.doclib-stats {
  font-size: 11px;
  color: var(--fg-muted);
  margin-bottom: 8px;
}
.doclib-toolbar {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
  align-items: center;
}
.doclib-search {
  flex: 1;
  padding: 6px 8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  font-size: 12px;
  font-family: inherit;
  outline: none;
  transition: border-color 0.15s;
}
#archive-select-btn { height: auto; padding: 5px 8px; }
.doclib-search:focus {
  border-color: var(--red);
}
.doclib-sort {
  padding: 6px 8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  font-size: 12px;
  font-family: inherit;
  outline: none;
  cursor: pointer;
}
.doclib-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 8px;
}
.doclib-chip {
  padding: 2px 10px;
  border-radius: 12px;
  font-size: 10px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--fg-muted);
  cursor: pointer;
  user-select: none;
  transition: background 0.15s, border-color 0.15s;
}
.doclib-chip:hover {
  border-color: var(--red);
}
.doclib-chip.active {
  background: color-mix(in srgb, var(--red) 15%, transparent);
  border-color: color-mix(in srgb, var(--red) 40%, transparent);
  color: var(--red);
}
/* Document library — language chip row */
.doclib-lang-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 2px 0;
  max-height: 52px;
  overflow-y: auto;
}
.doclib-lang-chips:empty { display: none; }
/* Mobile: keep tag chips on ONE row and scroll horizontally instead of wrapping
   into a tall multi-line block (Serve, library — anywhere .doclib-lang-chips is used). */
@media (max-width: 768px) {
  .doclib-lang-chips {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    max-height: none;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .doclib-lang-chips::-webkit-scrollbar { display: none; }
  .doclib-lang-chips > * { flex-shrink: 0; }
}
#doclib-tidy-btn, #doclib-select-btn, #doclib-chats-tidy-btn {
  position: relative;
  top: -3px;
}
/* Document library — list layout */
.doclib-grid {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 400px;
  overflow-y: auto;
  padding: 0;
  position: relative;
}

/* Gallery grid domino-in cascade on open — same recipe as the document/email
   libraries. Applied to #gallery-grid in gallery.js on the first render after
   each open, removed after the longest delay so re-renders feel instant. */
.gallery-just-opened > .gallery-card {
  animation: section-domino-in 0.36s cubic-bezier(0.22, 1.61, 0.36, 1) backwards;
}
.gallery-just-opened > :nth-child(1)  { animation-delay: 0.02s; }
.gallery-just-opened > :nth-child(2)  { animation-delay: 0.04s; }
.gallery-just-opened > :nth-child(3)  { animation-delay: 0.06s; }
.gallery-just-opened > :nth-child(4)  { animation-delay: 0.08s; }
.gallery-just-opened > :nth-child(5)  { animation-delay: 0.10s; }
.gallery-just-opened > :nth-child(6)  { animation-delay: 0.12s; }
.gallery-just-opened > :nth-child(7)  { animation-delay: 0.14s; }
.gallery-just-opened > :nth-child(8)  { animation-delay: 0.16s; }
.gallery-just-opened > :nth-child(9)  { animation-delay: 0.18s; }
.gallery-just-opened > :nth-child(10) { animation-delay: 0.20s; }
.gallery-just-opened > :nth-child(11) { animation-delay: 0.22s; }
.gallery-just-opened > :nth-child(12) { animation-delay: 0.24s; }
.gallery-just-opened > :nth-child(13) { animation-delay: 0.26s; }
.gallery-just-opened > :nth-child(14) { animation-delay: 0.28s; }
.gallery-just-opened > :nth-child(15) { animation-delay: 0.30s; }
.gallery-just-opened > :nth-child(16) { animation-delay: 0.32s; }
.gallery-just-opened > :nth-child(17) { animation-delay: 0.34s; }
.gallery-just-opened > :nth-child(18) { animation-delay: 0.36s; }
.gallery-just-opened > :nth-child(19) { animation-delay: 0.38s; }
.gallery-just-opened > :nth-child(20) { animation-delay: 0.40s; }
.gallery-just-opened > :nth-child(n+21) { animation-delay: 0.42s; }

/* Tasks list domino-in cascade on open — mirrors gallery / doclib. Applied to
   #tasks-list in tasks.js on the first render after each open, removed after
   the longest delay so re-renders feel instant. */
.tasks-just-opened > .task-card {
  animation: section-domino-in 0.36s cubic-bezier(0.22, 1.61, 0.36, 1) backwards;
}
.tasks-just-opened > :nth-child(1)  { animation-delay: 0.02s; }
.tasks-just-opened > :nth-child(2)  { animation-delay: 0.04s; }
.tasks-just-opened > :nth-child(3)  { animation-delay: 0.06s; }
.tasks-just-opened > :nth-child(4)  { animation-delay: 0.08s; }
.tasks-just-opened > :nth-child(5)  { animation-delay: 0.10s; }
.tasks-just-opened > :nth-child(6)  { animation-delay: 0.12s; }
.tasks-just-opened > :nth-child(7)  { animation-delay: 0.14s; }
.tasks-just-opened > :nth-child(8)  { animation-delay: 0.16s; }
.tasks-just-opened > :nth-child(9)  { animation-delay: 0.18s; }
.tasks-just-opened > :nth-child(10) { animation-delay: 0.20s; }
.tasks-just-opened > :nth-child(11) { animation-delay: 0.22s; }
.tasks-just-opened > :nth-child(12) { animation-delay: 0.24s; }
.tasks-just-opened > :nth-child(13) { animation-delay: 0.26s; }
.tasks-just-opened > :nth-child(14) { animation-delay: 0.28s; }
.tasks-just-opened > :nth-child(15) { animation-delay: 0.30s; }
.tasks-just-opened > :nth-child(16) { animation-delay: 0.32s; }
.tasks-just-opened > :nth-child(17) { animation-delay: 0.34s; }
.tasks-just-opened > :nth-child(18) { animation-delay: 0.36s; }
.tasks-just-opened > :nth-child(19) { animation-delay: 0.38s; }
.tasks-just-opened > :nth-child(20) { animation-delay: 0.40s; }
.tasks-just-opened > :nth-child(n+21) { animation-delay: 0.42s; }

/* Document library cascade — same recipe as email library, applied per-tab
   the first time content loads (chats / archive / research / documents).
   Module-level Set in documentLibrary.js prevents re-firing on tab swaps
   or filter/sort re-renders within the same page session. */
.doclib-just-opened > .memory-item,
.doclib-just-opened > .doclib-card {
  animation: section-domino-in 0.36s cubic-bezier(0.22, 1.61, 0.36, 1) backwards;
}
.doclib-just-opened > :nth-child(1)  { animation-delay: 0.02s; }
.doclib-just-opened > :nth-child(2)  { animation-delay: 0.04s; }
.doclib-just-opened > :nth-child(3)  { animation-delay: 0.06s; }
.doclib-just-opened > :nth-child(4)  { animation-delay: 0.08s; }
.doclib-just-opened > :nth-child(5)  { animation-delay: 0.10s; }
.doclib-just-opened > :nth-child(6)  { animation-delay: 0.12s; }
.doclib-just-opened > :nth-child(7)  { animation-delay: 0.14s; }
.doclib-just-opened > :nth-child(8)  { animation-delay: 0.16s; }
.doclib-just-opened > :nth-child(9)  { animation-delay: 0.18s; }
.doclib-just-opened > :nth-child(10) { animation-delay: 0.20s; }
.doclib-just-opened > :nth-child(11) { animation-delay: 0.22s; }
.doclib-just-opened > :nth-child(12) { animation-delay: 0.24s; }
.doclib-just-opened > :nth-child(13) { animation-delay: 0.26s; }
.doclib-just-opened > :nth-child(14) { animation-delay: 0.28s; }
.doclib-just-opened > :nth-child(15) { animation-delay: 0.30s; }
.doclib-just-opened > :nth-child(16) { animation-delay: 0.32s; }
.doclib-just-opened > :nth-child(17) { animation-delay: 0.34s; }
.doclib-just-opened > :nth-child(18) { animation-delay: 0.36s; }
.doclib-just-opened > :nth-child(19) { animation-delay: 0.38s; }
.doclib-just-opened > :nth-child(20) { animation-delay: 0.40s; }
.doclib-just-opened > :nth-child(n+21) { animation-delay: 0.42s; }

/* Domino cascade on first open of the email library — mirrors the sidebar
   .section-just-expanded recipe so list items spring in staggered. Class
   is applied to #email-lib-grid in emailLibrary.js on the first render
   only, then removed after the longest delay so re-renders feel instant. */
.email-lib-just-opened .doclib-card {
  animation: section-domino-in 0.36s cubic-bezier(0.22, 1.61, 0.36, 1) backwards;
}
.email-lib-just-opened .doclib-card:nth-child(1)  { animation-delay: 0.02s; }
.email-lib-just-opened .doclib-card:nth-child(2)  { animation-delay: 0.04s; }
.email-lib-just-opened .doclib-card:nth-child(3)  { animation-delay: 0.06s; }
.email-lib-just-opened .doclib-card:nth-child(4)  { animation-delay: 0.08s; }
.email-lib-just-opened .doclib-card:nth-child(5)  { animation-delay: 0.10s; }
.email-lib-just-opened .doclib-card:nth-child(6)  { animation-delay: 0.12s; }
.email-lib-just-opened .doclib-card:nth-child(7)  { animation-delay: 0.14s; }
.email-lib-just-opened .doclib-card:nth-child(8)  { animation-delay: 0.16s; }
.email-lib-just-opened .doclib-card:nth-child(9)  { animation-delay: 0.18s; }
.email-lib-just-opened .doclib-card:nth-child(10) { animation-delay: 0.20s; }
.email-lib-just-opened .doclib-card:nth-child(11) { animation-delay: 0.22s; }
.email-lib-just-opened .doclib-card:nth-child(12) { animation-delay: 0.24s; }
.email-lib-just-opened .doclib-card:nth-child(13) { animation-delay: 0.26s; }
.email-lib-just-opened .doclib-card:nth-child(14) { animation-delay: 0.28s; }
.email-lib-just-opened .doclib-card:nth-child(15) { animation-delay: 0.30s; }
.email-lib-just-opened .doclib-card:nth-child(16) { animation-delay: 0.32s; }
.email-lib-just-opened .doclib-card:nth-child(17) { animation-delay: 0.34s; }
.email-lib-just-opened .doclib-card:nth-child(18) { animation-delay: 0.36s; }
.email-lib-just-opened .doclib-card:nth-child(19) { animation-delay: 0.38s; }
.email-lib-just-opened .doclib-card:nth-child(20) { animation-delay: 0.40s; }
/* Cap the cascade at 20 — beyond that they animate together so opening a
   long inbox doesn't take forever to settle. */
.email-lib-just-opened .doclib-card:nth-child(n+21) { animation-delay: 0.42s; }

/* Inside the email library modal, the grid needs to grow with the modal —
   but `flex: 1 1 0` collapses to 0 when the parent isn't height-constrained
   (which is the non-fullscreen default). Use `auto` basis + a sensible
   `min-height` floor so it shows ~400px naturally and absorbs more when
   the modal is resized / fullscreened. */
#email-lib-modal .doclib-grid {
  max-height: none;
  height: auto;
  flex: 1 1 auto;
  min-height: 400px;
}

/* ── Mobile compose FAB (email) ──
   Replaces the top-right "New" button on phones with a round mail-icon button
   bottom-right that collapses to a circle while the list scrolls and springs
   back out to "New" when scrolling stops. Desktop is unchanged. */
.email-lib-fab { display: none; }
@media (max-width: 768px) {
  /* The absolute FAB anchors to the email panel card. */
  #email-lib-modal .admin-card { position: relative; }
  /* Hide the toolbar "New" button — the FAB is the mobile entry point. */
  #email-lib-compose-btn { display: none; }

  #email-lib-modal .email-lib-fab {
    --fab-size: 56px;
    display: flex;
    align-items: center;
    gap: 9px;
    /* Hidden until the email list has rendered; JS adds .fab-revealed to pop
       it in (scale-from-center). Avoids the button flashing at the top and
       sliding down before _positionFab() places it. */
    transform: scale(0);
    opacity: 0;
    transform-origin: center center;
    position: absolute;
    right: calc(16px + env(safe-area-inset-right, 0px));
    bottom: calc(18px + env(safe-area-inset-bottom, 0px));
    height: var(--fab-size);
    min-width: var(--fab-size);
    padding: 0 20px 0 18px;
    border: none;
    border-radius: 999px;
    background: var(--accent-primary, var(--red));
    color: #fff;
    font-family: inherit;
    font-size: 15px;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    box-shadow: 0 6px 18px rgba(0,0,0,.38), 0 2px 6px rgba(0,0,0,.28);
    z-index: 30;
    pointer-events: auto;
    /* This (base) transition governs the EXPAND — slower + graceful. */
    transition:
      padding 420ms cubic-bezier(0.22, 1, 0.36, 1),
      box-shadow 240ms ease,
      transform 140ms ease,
      background 140ms ease;
    will-change: padding, transform;
  }
  #email-lib-modal .email-lib-fab svg { flex: 0 0 auto; display: block; }
  #email-lib-modal .email-lib-fab .email-lib-fab-label {
    overflow: hidden;
    white-space: nowrap;
    max-width: 120px;
    opacity: 1;
    /* EXPAND timing — label eases out a touch after the width opens up. */
    transition:
      max-width 420ms cubic-bezier(0.22, 1, 0.36, 1),
      opacity 300ms 60ms cubic-bezier(0.22, 1, 0.36, 1),
      margin 420ms cubic-bezier(0.22, 1, 0.36, 1);
  }
  /* Collapsed (while scrolling) → pure circle, icon only. We DON'T set an
     explicit width: the container is auto-width and shrinks smoothly as the
     padding + label max-width animate (min-width keeps it circular). Setting a
     fixed width here made the collapse snap, since auto↔px width can't tween.
     This (.collapsed) transition governs the COLLAPSE — kept snappy. */
  #email-lib-modal .email-lib-fab.collapsed {
    padding: 0;
    justify-content: center;
    transition:
      padding 230ms cubic-bezier(0.4, 0, 0.2, 1),
      box-shadow 200ms ease,
      transform 140ms ease;
  }
  #email-lib-modal .email-lib-fab.collapsed .email-lib-fab-label {
    max-width: 0;
    opacity: 0;
    margin-left: -9px;
    transition:
      max-width 230ms cubic-bezier(0.4, 0, 0.2, 1),
      opacity 150ms cubic-bezier(0.4, 0, 0.2, 1),
      margin 230ms cubic-bezier(0.4, 0, 0.2, 1);
  }
  /* Entrance: circle-expand-from-middle pop, played once when revealed. No
     `forwards` fill so the resting transform (scale 1) comes from this rule —
     that keeps the :active press transform working afterwards. */
  #email-lib-modal .email-lib-fab.fab-revealed {
    transform: scale(1);
    opacity: 1;
    animation: emailFabPop 380ms cubic-bezier(0.34, 1.56, 0.64, 1);
  }
  #email-lib-modal .email-lib-fab:active {
    transform: scale(0.93);
    filter: brightness(0.92);
    box-shadow: 0 3px 10px rgba(0,0,0,.4);
  }
}
@keyframes emailFabPop {
  from { transform: scale(0); opacity: 0; }
  60%  { transform: scale(1.06); opacity: 1; }
  to   { transform: scale(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  #email-lib-modal .email-lib-fab,
  #email-lib-modal .email-lib-fab .email-lib-fab-label { transition-duration: 1ms; }
  #email-lib-modal .email-lib-fab.fab-revealed { animation: none; }
}
/* When the modal-content has explicit height (fullscreen or user-resized),
   drop the floor so the grid sizes purely from flex. */
#email-lib-modal.email-lib-fullscreen .doclib-grid,
#email-lib-modal .modal-content[style*="height"] .doclib-grid {
  min-height: 0;
}
/* Document library: same fix as the email library (#74). When fullscreened
   the modal-content gets an inline `height: 100vh`, but the inner
   .doclib-grid stays capped at the 400px default so the list looks like a
   tiny strip floating in a giant empty modal. Mirror the email recipe:
   make the modal a flex column, give the body/admin-card claim of the
   remaining height, and let the grid take the rest. Scoped to the
   fullscreen class so windowed-mode layout is unchanged. */
/* Inner flex chain that lets the chats/documents grid claim the full
   remaining height of the modal. Applies in BOTH fullscreen AND
   right-docked states — without the docked state included, dragging a
   fullscreen doclib to the right snap zone breaks the flex layout
   because exitFullscreen removes .doclib-fullscreen, and the grid
   falls back to its base max-height: 400px showing only ~8 items.
   The CSS variable :is() selector lets both states share one rule. */
#doclib-modal.doclib-fullscreen .doclib-modal-content,
#doclib-modal.modal-right-docked .doclib-modal-content,
#doclib-modal.modal-left-docked .doclib-modal-content {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* Fullscreen positioning — scoped to NOT apply when also right-docked.
   Without this exclusion, dragging a fullscreen doclib to the right snap
   zone keeps .doclib-fullscreen on the modal and these !important rules
   override the dock's inline styles, leaving the modal stuck fullscreen
   instead of becoming a side panel. */
#doclib-modal.doclib-fullscreen:not(.modal-right-docked) .doclib-modal-content {
  position: fixed !important;
  top: 0 !important;
  left: calc(var(--icon-rail-w, 48px) + var(--sidebar-w, 0px)) !important;
  right: 0 !important;
  bottom: 0 !important;
  width: auto !important;
  max-width: none !important;
  height: 100vh !important;
  max-height: 100vh !important;
  border-radius: 0 !important;
  transform: none !important;
}
#doclib-modal.doclib-fullscreen .modal-header,
#doclib-modal.doclib-fullscreen .lib-tabs,
#doclib-modal.modal-right-docked .modal-header,
#doclib-modal.modal-right-docked .lib-tabs,
#doclib-modal.modal-left-docked .modal-header,
#doclib-modal.modal-left-docked .lib-tabs {
  flex: 0 0 auto;
}
#doclib-modal.doclib-fullscreen .modal-body,
#doclib-modal.modal-right-docked .modal-body,
#doclib-modal.modal-left-docked .modal-body {
  flex: 1 1 0;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
#doclib-modal.doclib-fullscreen .admin-card,
#doclib-modal.modal-right-docked .admin-card,
#doclib-modal.modal-left-docked .admin-card {
  flex: 1 1 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
#doclib-modal.doclib-fullscreen .doclib-grid:not(:has(.doclib-card-expanded)),
#doclib-modal.modal-right-docked .doclib-grid:not(:has(.doclib-card-expanded)),
#doclib-modal.modal-left-docked .doclib-grid:not(:has(.doclib-card-expanded)) {
  max-height: none;
  height: auto;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
/* "Reply" docks the open email modal to the left as a sidebar so the doc
   panel (which slides in from the right of the chat area) is visible
   side-by-side. The .modal backdrop is already pointer-events:none for
   email modals — only the content takes pointer events — so the chat /
   doc panel underneath stays interactive. */
.modal.email-snap-left {
  align-items: stretch;
  justify-content: flex-start;
  left: 0 !important;
  width: 100% !important;
}
.modal.email-snap-left .modal-content {
  left: var(--email-doc-split-left-x, 0px) !important;
  width: var(--email-doc-split-email-w, 420px) !important;
  max-width: var(--email-doc-split-email-w, 420px) !important;
  border-right: 1px solid var(--border);
  box-shadow: 4px 0 14px rgba(0, 0, 0, 0.18);
  border-radius: 0;
}
@media (min-width: 769px) {
  body.email-doc-split-active.doc-view #email-lib-modal,
  body.email-doc-split-active.doc-view.email-front #email-lib-modal,
  body.email-doc-split-active.doc-view .modal[id^="email-reader-"],
  body.email-doc-split-active.doc-view.email-front .modal[id^="email-reader-"] {
    z-index: 150 !important;
  }
  body.email-doc-split-active #email-lib-modal.email-snap-left,
  body.email-doc-split-active #email-lib-modal.modal-left-docked {
    left: var(--email-doc-split-left-x, 0px) !important;
    width: var(--email-doc-split-email-w, 420px) !important;
    overflow: hidden !important;
    z-index: 155 !important;
  }
  body.email-doc-split-active #email-lib-modal.email-snap-left:not(.modal-dragging) .modal-content,
  body.email-doc-split-active #email-lib-modal.modal-left-docked:not(.modal-dragging) .modal-content,
  body.email-doc-split-active .modal[id^="email-reader-"].email-snap-left:not(.modal-dragging) .modal-content,
  body.email-doc-split-active .modal[id^="email-reader-"].modal-left-docked:not(.modal-dragging) .modal-content {
    position: absolute !important;
    left: 0 !important;
    top: 0 !important;
    bottom: 0 !important;
    right: auto !important;
    width: var(--email-doc-split-email-w, 420px) !important;
    max-width: var(--email-doc-split-email-w, 420px) !important;
    height: 100vh !important;
    max-height: 100vh !important;
    transform: none !important;
    margin: 0 !important;
  }
  body.email-doc-split-active.doc-view .doc-divider {
    display: none !important;
  }
  body.email-doc-split-active.doc-view .doc-editor-pane {
    position: fixed !important;
    left: var(--email-doc-split-right-x, 420px) !important;
    right: 0 !important;
    top: 0 !important;
    bottom: 0 !important;
    width: auto !important;
    max-width: none !important;
    height: 100vh !important;
    z-index: 260 !important;
    margin-top: 0 !important;
    transform: none !important;
    border-left: none !important;
  }
}

/* Email reader "Search text in this thread" / from-sender toggle —
   DISABLED on all viewports while the search/threaded-sidebar UX is too
   buggy to ship. Re-enable by removing this rule + the JS .remove()
   guards in emailLibrary.js. */
body [data-act="from-sender"] {
  display: none !important;
}

/* Snap-to-right docking. A modal dragged to the right edge becomes a
   docked side panel (mirrors Notes/Doc panels). Body reserves space via
   padding-right so the chat / notes / doc panel underneath shrinks to
   fit instead of being hidden behind the panel. */
body.right-dock-active {
  padding-right: var(--right-dock-w, 0px);
}
body.left-dock-active {
  padding-left: var(--left-dock-w, 0px);
}
.modal.modal-right-docked {
  align-items: stretch;
  justify-content: flex-end;
}
.modal.modal-right-docked .modal-content {
  border-left: 1px solid var(--border);
  box-shadow: -4px 0 14px rgba(0, 0, 0, 0.18);
  border-radius: 0;
}
.modal.modal-left-docked {
  align-items: stretch;
  justify-content: flex-start;
}
.modal.modal-left-docked .modal-content {
  border-right: 1px solid var(--border);
  box-shadow: 4px 0 14px rgba(0, 0, 0, 0.18);
  border-radius: 0;
  /* Pin transitions OFF on the dock's positioning properties. The wide
     sidebar collapse/expand changes --sidebar-w instantly, which means
     the docked modal's `left: calc(...)` jumps by ~240px. Any CSS
     transition on `left` (inherited or otherwise) would animate that
     jump as a fly-across. Same defense for right-docked / fullscreen
     panels in case future themes add a generic transition. */
  transition: none !important;
}
.modal.modal-right-docked .modal-content,
#email-lib-modal.email-lib-fullscreen .modal-content,
#doclib-modal.doclib-fullscreen .doclib-modal-content {
  transition: none !important;
}
.modal.modal-right-docked .email-reader-header,
.modal.modal-left-docked .email-reader-header {
  flex-direction: column;
  gap: 6px;
}
.modal.modal-right-docked .email-reader-actions,
.modal.modal-left-docked .email-reader-actions {
  align-self: flex-end;
}
.modal.modal-right-docked .email-reader-meta-row,
.modal.modal-left-docked .email-reader-meta-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2px;
  align-items: start;
}
.modal.modal-right-docked .email-reader-meta-row strong,
.modal.modal-left-docked .email-reader-meta-row strong {
  min-width: 0;
}
.modal.modal-right-docked .recipient-chip,
.modal.modal-left-docked .recipient-chip {
  max-width: 100%;
}
.archive-list {
  margin-top: 8px;
  border-top: 1px solid var(--border);
  padding-top: 8px;
}
#archive-modal .memory-sort-select,
#archive-modal .memory-toolbar-btn {
  height: 32px;
}
.archive-menu-btn {
  position: relative;
  top: 2px;
}
#archive-chips .doclib-chip {
  height: 22px;
  display: inline-flex;
  align-items: center;
  font-size: 10px;
  padding: 0 8px;
}
/* Library tab panels — Chats / Archive / Research / Documents share the
   same toolbar dimensions so the sort dropdown + Select button line up
   identically across tabs. */
#doclib-panel-chats .memory-sort-select,
#doclib-panel-archive .memory-sort-select,
#doclib-panel-research .memory-sort-select,
[data-doclib-panel="documents"] .memory-sort-select {
  height: 24px;
  font-size: 11px;
  width: 86px;
}
#doclib-panel-chats .memory-toolbar-btn,
#doclib-panel-archive .memory-toolbar-btn,
#doclib-panel-research .memory-toolbar-btn,
[data-doclib-panel="documents"] .memory-toolbar-btn {
  height: 24px;
  font-size: 11px;
  position: relative;
  top: -3px;
}
/* Research's Recent (sort) + Select sat 1px lower than the other tabs — nudge up. */
#doclib-panel-research .memory-sort-select { position: relative; top: 1.5px; }
#doclib-panel-research .memory-toolbar-btn { top: -4.5px; }
#doclib-research-search { position: relative; top: -1.5px; }
[data-doclib-panel] { padding-top: 6px !important; }
#doclib-panel-chats, #doclib-panel-archive { padding-top: 14px !important; }
#doclib-panel-chats .memory-desc, #doclib-panel-archive .memory-desc, #doclib-panel-research .memory-desc { margin-top: 7px; }
#doclib-panel-chats .memory-search-input,
#doclib-panel-archive .memory-search-input {
  height: 30px !important;
  min-height: 30px !important;
  flex-shrink: 0;
  font-size: 11px;
}
/* Unified search bar across Library tabs + Memory modal — same height,
   same magnifying-glass icon at the start, consistent padding. */
#doclib-panel-chats .memory-search-input,
#doclib-panel-archive .memory-search-input,
#doclib-panel-research .memory-search-input,
[data-doclib-panel="documents"] .memory-search-input,
#memory-modal .memory-search-input,
#tasks-modal .memory-search-input {
  height: 30px;
  min-height: 30px;
  font-size: 11px;
  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='%23e06c75' stroke-opacity='0.85' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='8'/><line x1='21' y1='21' x2='16.65' y2='16.65'/></svg>");
  background-repeat: no-repeat;
  background-position: 9px center;
  padding-left: 28px;
}
#doclib-panel-chats .doclib-chip {
  height: 28px;
}
#doclib-panel-archive .doclib-chip {
  height: 28px;
}
#doclib-panel-chats .doclib-chip,
#doclib-panel-archive .doclib-chip {
  display: inline-flex;
  align-items: center;
  font-size: 9px;
  padding: 0 8px;
  border-radius: 10px;
}
.doclib-grid:has(.doclib-card-expanded) > .doclib-card:not(.doclib-card-expanded) {
  display: none;
}
/* Hide everything except the grid when a card is expanded */
.admin-card:has(.doclib-card-expanded) > *:not(.doclib-grid):not(.hwfit-cached-list) {
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  pointer-events: none;
  margin: 0 !important;
  padding: 0 !important;
  transition: opacity 0.12s ease, max-height 0.2s ease;
}
.admin-card:has(.doclib-card-expanded) > .memory-bulk-bar {
  display: none;
}
.admin-card:has(.doclib-card-expanded) > .doclib-grid,
.admin-card:has(.doclib-card-expanded) > .hwfit-cached-list {
  max-height: none !important;
  min-height: 0 !important;
  overflow: visible !important;
}
/* Firefox-mobile (no :has()) fallback: when reading an email, fully remove the
   list-mode chrome (accounts row, toolbar, bulk bar, desc) — display:none so no
   residual flex-gap leaves an empty strip above the reader — and zero the
   admin-card gap so the grid/reader starts flush at the top. */
#email-lib-modal.email-reading .admin-card > *:not(.doclib-grid):not(.hwfit-cached-list) {
  display: none !important;
}
#email-lib-modal.email-reading .admin-card { gap: 0 !important; }
/* Override for chat-rows + research-rows: those expand inline with a
   bounded preview (max-height: 60vh), so the grid must stay clipped
   and scrollable. Without this scoping the overflow:visible above lets
   the expanded chat preview escape the grid and overlap whatever's
   underneath/beside the modal (e.g., a docked sibling panel) — what
   the user calls "merging with other windows". */
.admin-card:has(.doclib-chat-row.doclib-card-expanded) > .doclib-grid {
  overflow: hidden auto !important;
}
/* Email modal needs the grid to STAY a constrained flex container when
   expanded — overflow:visible (set above for cookbook) lets content escape
   instead of letting the expanded reader fill via flex:1. We keep the same
   max-height:none + min-height:0 but flip overflow back so the reader
   actually fills the modal. */
#email-lib-modal .admin-card:has(.doclib-card-expanded) > .doclib-grid,
#email-lib-modal.email-reading .admin-card > .doclib-grid {
  overflow: hidden !important;
  display: flex !important;
  flex: 1 1 auto !important;
}
#email-lib-modal.email-reading .doclib-modal-content {
  min-height: var(--email-reading-modal-min-h, auto);
}
#email-lib-modal .doclib-card.doclib-card-expanded {
  flex: 1 1 auto !important;
  height: 100% !important;
  /* Neutral frame on the active email — the accent border + glow felt
     overbearing on desktop; the size jump alone is enough signal. */
  border: 1px solid var(--border) !important;
  box-shadow: 0 6px 18px rgba(0,0,0,0.12) !important;
  animation: none !important;
}
/* Desktop-only, ONLY on the currently-expanded email card. Nudges the title
   row down 6px / right 2px, bolds the subject, hides the timestamp from the
   meta line (the reader header carries its own date), and pulls the prev/next
   nav arrows in 4px. */
@media (min-width: 769px) {
  #email-lib-modal .doclib-card-expanded .email-card-titlerow,
  #email-lib-modal .doclib-card-expanded .email-card-titlerow > * {
    position: relative;
    top: 6px;
    left: 2px;
  }
  /* Smaller subject — the reader header below already carries weight. */
  #email-lib-modal .doclib-card-expanded .email-card-titlerow .memory-item-title {
    font-weight: 700;
    font-size: 12px;
  }
  /* A bit more breathing room around the title row so the smaller bold text
     doesn't crowd the meta line below. (Timestamp kept — user changed mind.) */
  #email-lib-modal .doclib-card-expanded .email-card-titlerow {
    padding: 4px 0 6px;
    line-height: 1.35;
  }
  /* Nudge the date down/left in the meta row, and give the meta line +4px of
     extra bottom space so the shifted date isn't clipped. */
  #email-lib-modal .doclib-card-expanded .email-meta-date {
    position: relative;
    top: 4px;
    left: 0;
  }
  #email-lib-modal .doclib-card-expanded .memory-item-meta {
    padding-bottom: 4px;
  }
  #email-lib-modal .doclib-card-expanded .email-card-nav-arrows {
    position: relative;
    left: -4px;
  }
}
/* Skills modal: same mechanism as email above. The default expanded-grid
   rule (overflow:visible, no flex) lets the card expand inline to ~content
   height instead of filling — which is why the skill preview stopped at
   partial height. Flip the grid back to a constrained flex container and
   let the expanded card fill it, exactly like the email reader. */
#memory-modal .admin-card:has(.doclib-card-expanded) > .doclib-grid {
  overflow: hidden !important;
  display: flex !important;
  flex-direction: column !important;
  flex: 1 1 auto !important;
}
#memory-modal .doclib-card.doclib-card-expanded {
  flex: 1 1 auto !important;
  height: 100% !important;
}
/* When the from-sender sidebar is open inside the email card, the email
   body would otherwise lose 280px to padding-right and read as a narrow
   strip. Widen the whole modal so the body keeps its normal text width
   and the sidebar appears alongside, not on top of it. Also force the
   modal to its full max-height — short emails would otherwise leave the
   sidebar squeezed into a tiny vertical strip. */
#email-lib-modal .modal-content:has(.from-sender-panel) {
  width: min(1020px, 95vw) !important;
  height: 85vh !important;
  transition: width 0.22s ease-out, height 0.22s ease-out;
}
#email-lib-modal .modal-content {
  transition: width 0.22s ease-out, height 0.22s ease-out;
}
@media (min-width: 769px) {
  body:not(.email-doc-split-active) #email-lib-modal:not(.email-lib-fullscreen):not(.modal-left-docked):not(.modal-right-docked) .modal-content {
    min-height: min(560px, 85vh);
  }
}

/* Cookbook's cached-model list should scale with viewport height, not be capped at 400px */
.hwfit-cached-list {
  max-height: min(75vh, 900px) !important;
  overflow-y: auto;
}
/* Drag-and-drop visual hint for the email compose pane. Subtle accent
   outline + tinted overlay so it's obvious files will attach if dropped. */
.doc-editor-pane.email-dragover {
  outline: 2px dashed var(--accent, #2563eb);
  outline-offset: -8px;
  background: color-mix(in srgb, var(--accent, #2563eb) 6%, transparent);
}
.doc-editor-pane.email-dragover::after {
  content: 'Drop to attach';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--accent, #2563eb);
  color: #fff;
  padding: 8px 16px;
  border-radius: 6px;
  font-size: 12px;
  pointer-events: none;
  z-index: 1000;
}

/* Email reader window — make the body always fill the available vertical
   space regardless of how the window is positioned/resized. The user can
   drag the bottom-right corner to resize; the body re-flexes automatically. */
.email-window-modal .modal-content {
  display: flex !important;
  flex-direction: column !important;
  resize: both;
  overflow: hidden;
  min-width: 320px;
  min-height: 240px;
  /* When user resizes/drags so the window covers a side, force the inner
     body to fill all the way to the bottom. The default max-height: 85vh
     (set inline by _makeDraggable's exitFullscreen) and 80vh from the
     opener both cap at the *viewport* — if the browser window then changes
     size (e.g. OS aero-snap), the modal stays at the old height and
     truncates rows. Drop the cap whenever an explicit width OR height has
     been written inline by the resize handle / drag handler. */
}
.email-window-modal .modal-content[style*="width"],
.email-window-modal .modal-content[style*="height"] {
  max-height: calc(100vh - 16px) !important;
}
.email-window-modal .email-window-body {
  flex: 1 1 auto !important;
  min-height: 0 !important;
  /* Explicit basis of 0 makes the body grow to fill remaining space rather
     than its content's intrinsic height (which was capping the thread). */
  flex-basis: 0 !important;
  max-height: none !important;
  overflow: auto !important;
  overscroll-behavior: contain;
}

/* Individual email reader window — fullscreen mode (drag-to-top snap or
   double-click header). Same recipe as the library modal: pin to viewport
   edges and let the inner body absorb the remaining height. */
body:not(.email-doc-split-active) .email-window-modal.email-window-fullscreen .modal-content {
  position: fixed !important;
  inset: 0 !important;
  left: 0 !important;
  top: 0 !important;
  width: 100vw !important;
  max-width: 100vw !important;
  height: 100vh !important;
  max-height: 100vh !important;
  border-radius: 0 !important;
  transform: none !important;
  display: flex !important;
  flex-direction: column !important;
}
body:not(.email-doc-split-active) .email-window-modal.email-window-fullscreen .email-window-body {
  flex: 1 1 auto !important;
  min-height: 0 !important;
  max-height: none !important;
}

/* Email library modal: fullscreen mode + larger mobile sheet (mirrors the
   cookbook treatment so the toolbar and bulk actions stay reachable).
   Leaves whichever navigation strip is currently visible on the left
   (the wide sidebar OR the icon rail — they're mutually exclusive, so
   one of the two CSS vars is 0 at any given time, set by init.js). */
body:not(.email-doc-split-active) #email-lib-modal.email-lib-fullscreen:not(.modal-right-docked) .modal-content {
  position: fixed !important;
  top: 0 !important;
  left: calc(var(--icon-rail-w, 48px) + var(--sidebar-w, 0px)) !important;
  right: 0 !important;
  bottom: 0 !important;
  width: auto !important;
  max-width: none !important;
  height: 100vh !important;
  max-height: 100vh !important;
  border-radius: 0 !important;
  transform: none !important;
}
/* Mobile: the /email route adds .email-lib-fullscreen, but the desktop full-
   bleed rule above squares off the corners and offsets the email by the icon
   rail. On phones the email should be the normal bottom-sheet — full width,
   rounded top corners. Same specificity as the rule above + later in source,
   so it wins on mobile. */
@media (max-width: 768px) {
  body:not(.email-doc-split-active) #email-lib-modal.email-lib-fullscreen:not(.modal-right-docked) .modal-content {
    left: 0 !important;
    right: 0 !important;
    width: 100vw !important;
    max-width: 100vw !important;
    height: 100dvh !important;
    max-height: 100dvh !important;
    border-radius: 14px 14px 0 0 !important;
    border-top: 1px solid var(--border) !important;
  }
}
/* Make the inner panes actually grow to fill the fullscreen container
   instead of staying at their natural size. The body owns the remaining
   height below the header; the admin-card + grid then expand into it. */
#email-lib-modal.email-lib-fullscreen .modal-body {
  flex: 1 1 auto !important;
  min-height: 0 !important;
  overflow: hidden !important;
}
#email-lib-modal.email-lib-fullscreen .modal-body > .admin-card {
  flex: 1 1 auto !important;
  min-height: 0 !important;
}
#email-lib-modal.email-lib-fullscreen .doclib-grid {
  flex: 1 1 auto !important;
  min-height: 0 !important;
  max-height: none !important;
  overflow: auto !important;
}
@media (max-width: 768px) {
  /* Mobile email modal sizing — keep in sync with the rule earlier in the
     file. 75dvh tall always so flex children (the expanded email reader)
     have a real height to grow into. */
  #email-lib-modal .modal-content {
    max-height: 90dvh !important;
    max-height: 90vh !important;
    height: 90dvh !important;
    height: 90vh !important;
  }
  /* Inner panes grow to fill the modal-content — without flex:1 on the
     body, the expanded email reader sits in a tiny box because there's
     nothing pushing it to take the remaining height. overflow:hidden +
     min-height:0 lets each layer pass its constraints down. */
  #email-lib-modal .modal-body,
  #email-lib-modal .admin-card {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    overflow: hidden !important;
    max-height: none !important;
  }
  #email-lib-modal .doclib-grid {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    max-height: none !important;
    overflow-y: auto !important;
  }
  /* modal-content keeps its scroll OFF here — the inner flex children
     (doclib-grid for the list view, email-reader-body for the expanded
     reader) own the scroll surfaces. Without this, double-scroll layouts
     trap touches and the reader can't claim full height. */
  #email-lib-modal .modal-content {
    overflow: hidden !important;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    touch-action: pan-y;
  }
  /* Attachment row on phones: cap to ~2 rows so it never dominates the
     reader. If more chips exist, the row scrolls vertically. Smaller chip
     padding + max-width keeps each chip compact so two rows usually fit
     everything anyway. */
  #email-lib-modal .email-reader-atts {
    padding: 4px 10px !important;
    gap: 3px !important;
    max-height: calc(2 * 22px + 12px); /* 2 rows × chip height + padding */
    overflow-y: auto;
    align-content: flex-start;
  }
  #email-lib-modal .email-attachment-chip {
    padding: 1px 6px !important;
    font-size: 10px !important;
    max-width: 130px !important;
    line-height: 18px !important;
  }
}

/* Mobile: only ONE scroll surface inside the cookbook modal. The
   modal-content is the scroller. Everything inside (cookbook-body,
   cookbook-group, all the inner lists/panels) gets overflow: visible
   so touch-pan never gets trapped in a nested scroller.
   Without this, three levels of overflow:auto + max-height combinations
   produce dead-zone areas where swipes do nothing. */
@media (max-width: 768px) {
  #cookbook-modal .modal-content {
    overflow-y: auto !important;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    touch-action: pan-y;
  }
  #cookbook-modal .cookbook-body,
  #cookbook-modal .cookbook-group,
  #cookbook-modal .cookbook-section-body,
  #cookbook-modal .hwfit-cached-list,
  #cookbook-modal .doclib-grid,
  #cookbook-modal .hwfit-list,
  #cookbook-modal .hwfit-panel-cmd {
    overflow: visible !important;
    max-height: none !important;
    min-height: 0 !important;
  }
  /* Running tmux output: cap how tall an expanded card gets (a long-lived job
     can leave thousands of lines) so it doesn't balloon, but keep its own
     scroll so you can read it. Outputs default collapsed on mobile now, so
     you're usually looking at one expanded card at a time — no wall of nested
     scrollers, and you collapse it to move past. */
  #cookbook-modal .cookbook-output-pre,
  #cookbook-modal .cookbook-task .cookbook-output-pre {
    max-height: 45vh !important;
    min-height: 0 !important;
    overflow-y: auto !important;
    overflow-x: hidden !important;
    overscroll-behavior: auto;  /* chain to the modal at the scroll boundary */
  }
  /* cookbook-body's flex:1 was needed when it owned scrolling — drop it
     so the inner content drives modal-content's scroll height. */
  #cookbook-modal .cookbook-body {
    flex: 0 0 auto !important;
    height: auto !important;
  }
}
.memory-toolbar {
  transition: opacity 0.12s ease, max-height 0.2s ease;
  max-height: 120px;
}
/* The Servers list reuses .memory-toolbar for layout but must grow with every
   added server — the 120px cap above was clipping manually-added servers. */
.memory-toolbar.cookbook-servers-toolbar {
  max-height: none;
  overflow: visible;
}
.doclib-card {
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.15s, opacity 0.3s, max-height 0.3s ease;
  position: relative;
}
.doclib-card.doclib-card-deleting {
  opacity: 0;
  max-height: 0 !important;
  overflow: hidden;
  margin: 0;
  padding: 0;
  border-color: transparent;
}
.doclib-card:hover {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  border-color: color-mix(in srgb, var(--fg) 16%, transparent);
}
.doclib-card.selected,
/* Universal selection highlight: any card-shaped row (documents, chats,
   archive, research, skills, memories, tasks) gets the same accent outline
   when its checkbox is checked. Done via :has() so the highlight follows
   selection without per-renderer JS changes. The canonical checkbox class
   is .memory-select-cb across every list. */
.memory-item:has(.memory-select-cb:checked),
.doclib-card:has(.memory-select-cb:checked),
.doclib-chat-row:has(.memory-select-cb:checked) {
  border-color: color-mix(in srgb, var(--red) 40%, transparent);
  background: color-mix(in srgb, var(--red) 4%, transparent);
  /* 2px accent outline overlaid on the 1px border — reads as a thicker
     selected border without shifting layout (every card would otherwise
     need a 2px transparent border to keep the same width). */
  outline: 2px solid var(--accent-primary, var(--red));
  outline-offset: -1px;
}
.doclib-card-header {
  display: flex;
  align-items: center;
  padding: 7px 8px;
  gap: 6px;
  min-width: 0;
}
/* Mobile only: push the unexpanded email card's title-row text down 4px so
   it sits visually centered with the surrounding card padding. Targets the
   actual title-row class (.email-card-titlerow) — the earlier attempt used
   .doclib-card-header which the email card builds differently. */
@media (max-width: 768px) {
  #email-lib-modal .doclib-card:not(.doclib-card-expanded) .email-card-titlerow {
    margin-top: 4px;
  }
}
.doclib-card-title {
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 1;
}
.doclib-card-ver {
  padding: 1px 6px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--accent, var(--red)) 15%, transparent);
  color: var(--accent, var(--red));
  font-size: 9px;
  flex-shrink: 0;
  font-weight: 600;
  letter-spacing: 0.3px;
  min-width: 24px;
  text-align: left;
  box-sizing: border-box;
}
.doclib-card-ver-muted {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  color: color-mix(in srgb, var(--fg) 35%, transparent);
}
.doclib-card-lang {
  padding: 1px 6px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent);
  font-size: 9px;
  text-align: left;
  flex-shrink: 0;
  min-width: 52px;
  box-sizing: border-box;
}
.doclib-card-session {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 60px;
  width: 100px;
  max-width: 100px;
  font-size: 9px;
  color: var(--fg-muted);
  flex-shrink: 0;
  text-align: left;
}
.doclib-card-time {
  flex-shrink: 0;
  opacity: 0.5;
  font-size: 9px;
  color: var(--fg-muted);
  min-width: 50px;
  text-align: left;
}
/* Footer — used by archive cards */
.doclib-card-footer {
  display: contents;
}
/* Preview — hidden by default, shown on expand */
.doclib-card-preview {
  display: none;
  position: relative;
  padding: 8px 12px;
  font-family: 'Berkeley Mono', 'SF Mono', 'Fira Code', monospace;
  font-size: 9.5px;
  line-height: 1.5;
  color: var(--code-fg, var(--hl-fg, var(--fg)));
  border-top: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
  margin: 0;
}
.doclib-card-preview pre {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-all;
}
.doclib-card-expanded .doclib-card-preview pre {
  flex: 1;
  overflow-y: auto;
  min-height: 0;
}
.doclib-card-preview code.hljs {
  background: none;
  padding: 0;
}
/* Expanded-only action bar — inside preview. Buttons shifted up 4px by
   trimming the row's top margin so they sit closer to the preview text. */
.doclib-card-expanded-actions {
  display: none;
  align-items: flex-start;
  gap: 6px;
  padding: 8px 0 2px;
  border-top: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
  margin-top: 4px;
}
.doclib-card-expanded-actions > .doclib-card-action-btn,
.doclib-card-expanded-actions .doclib-action-btn-row > .doclib-card-action-btn {
  position: relative;
  top: -4px;
}
.doclib-action-group {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.doclib-action-btn-row {
  display: flex;
  gap: 6px;
}
.doclib-action-hint-row {
  display: flex;
  gap: 6px;
}
/* Match the chat/research footer buttons exactly: flat, bordered,
   app font, accent on hover. */
.doclib-card-action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  box-sizing: border-box;
  font-size: 10px;
  padding: 3px 8px;
  border-radius: 4px;
  background: none;
  border: 1px solid var(--border);
  color: var(--fg-muted);
  cursor: pointer;
  /* These buttons live inside .doclib-card-preview, which forces a
     monospace font. The chat/research footer buttons instead inherit the
     modal font, which is the app font (Fira Code) on mobile and Inter on
     desktop (.modal-content's Inter rule is inside @media min-width:769px).
     Mirror that here so all three footers match in both contexts. */
  font-family: var(--font-family, 'Fira Code', monospace);
  transition: border-color 0.15s, color 0.15s;
}
@media (min-width: 769px) {
  .doclib-card-action-btn {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    letter-spacing: -0.015em;
  }
}
.doclib-card-action-btn:hover {
  border-color: var(--accent, var(--red));
  color: var(--accent, var(--red));
}
.doclib-card-text-btn-danger {
  color: var(--color-danger, #e06c75);
  border-color: var(--color-danger, #e06c75);
}
.doclib-card-text-btn-danger:hover {
  color: #ff4d4d;
  border-color: #ff4d4d;
}
.doclib-card-expanded-actions > .doclib-card-action-btn {
  font-size: 10px;
  padding: 3px 8px;
}
/* Push the Open/Clone group to the right edge of the action bar — Delete
   anchors the left, primary actions sit opposite. */
.doclib-card-expanded-actions > .doclib-action-group {
  margin-left: auto;
}
.doclib-btn-hint {
  font-weight: normal;
  opacity: 0.35;
  font-size: 8px;
  min-width: 58px;
  padding: 0 8px;
  box-sizing: border-box;
}
@media (max-width: 768px) {
  .doclib-btn-hint { display: none !important; }
}

/* Chat row expand preview — matches the documents-tab expand style. */
.doclib-chat-row { flex-direction: column; align-items: stretch; }
.doclib-chat-row .doclib-card-chevron {
  opacity: 0.3;
  transition: transform 0.2s ease, opacity 0.15s;
  flex-shrink: 0;
}
.doclib-chat-row.doclib-card-expanded .doclib-card-chevron {
  opacity: 0.6;
}
/* Release .memory-item's base max-height: 200px when a chat row is
   expanded. Without this, the row clips at 200px on desktop and the
   preview's messages/actions visually spill past the row's box —
   "colliding" with the next chat in the column. The mobile takeover
   below already handles this via the same override; this rule is the
   desktop-or-any-viewport equivalent. */
.doclib-chat-row.doclib-card-expanded {
  max-height: none;
  overflow: visible;
}
/* When a chat row is expanded, take over the grid the same way documents
   do: hide siblings, hide the admin-card toolbar, and let this row plus
   its preview claim the full available height. .memory-item's default
   max-height: 200px must be overridden or the preview will be tiny. */
/* Mobile-only "card takeover" for an expanded chat — hides siblings,
   hides the admin-card toolbar, and lets this row + its preview claim
   the full available height. On desktop the expanded preview just
   renders inline below the row with its messages constrained by
   max-height + overflow:auto so the user gets a normal scroll. */
@media (max-width: 820px) {
  .doclib-grid:has(.doclib-chat-row.doclib-card-expanded) > .doclib-chat-row:not(.doclib-card-expanded) {
    display: none;
  }
  .admin-card:has(.doclib-chat-row.doclib-card-expanded) > *:not(.doclib-grid) {
    display: none;
  }
  .admin-card:has(.doclib-chat-row.doclib-card-expanded) > .doclib-grid {
    flex: 1 1 auto !important;
    max-height: none !important;
    overflow-y: auto !important;
  }
  .doclib-chat-row.doclib-card-expanded {
    flex: 1 1 auto !important;
    max-height: none !important;
    min-height: 0 !important;
    overflow: hidden !important;
    display: flex !important;
    flex-direction: column !important;
    align-items: stretch !important;
  }
  .doclib-chat-row.doclib-card-expanded .doclib-chat-header {
    flex: 0 0 auto !important;
  }
  .doclib-chat-row.doclib-card-expanded .doclib-chat-preview {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    overflow: hidden !important;
    display: flex !important;
    flex-direction: column !important;
    padding-bottom: 4px !important;
  }
  .doclib-chat-row.doclib-card-expanded .doclib-chat-preview-messages {
    -webkit-overflow-scrolling: touch;
    max-height: none !important;
    flex: 1 1 auto !important;
    min-height: 0 !important;
  }
}
.doclib-chat-preview {
  font-size: 11px;
  padding: 0 4px 6px;
}
.doclib-chat-preview .doclib-chat-open-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 10px;
  padding: 3px 8px;
  border-radius: 4px;
  color: var(--fg-muted);
  border: 1px solid var(--border);
  background: none;
  cursor: pointer;
  font-family: inherit;
  transition: border-color 0.15s, color 0.15s;
}
.doclib-chat-preview .doclib-chat-open-btn:hover {
  border-color: var(--accent, var(--red));
  color: var(--accent, var(--red));
}
.doclib-chat-preview-messages {
  margin-top: 6px;
  max-height: 320px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-right: 4px;
}
/* Desktop expanded state — match the documents-tab behavior: hide
   sibling chats, hide the admin-card toolbar/header, and let the
   expanded row + its messages preview claim the full modal height.
   Chat rows use `memory-item doclib-chat-row` (no `doclib-card` class)
   so the document-tab's global rules at line 12284-12304 skip them —
   these mirror those rules scoped to chat rows. The mobile media query
   above (820px) keeps the same takeover behavior for phones. */
@media (min-width: 821px) {
  .doclib-grid:has(.doclib-chat-row.doclib-card-expanded) > .doclib-chat-row:not(.doclib-card-expanded) {
    display: none;
  }
  .admin-card:has(.doclib-chat-row.doclib-card-expanded) > *:not(.doclib-grid) {
    display: none;
  }
  .admin-card:has(.doclib-chat-row.doclib-card-expanded) > .doclib-grid {
    flex: 1 1 auto !important;
    max-height: none !important;
    overflow: hidden auto !important;
  }
  .doclib-chat-row.doclib-card-expanded {
    flex: 1 1 auto !important;
    max-height: none !important;
    min-height: 0 !important;
    display: flex !important;
    flex-direction: column !important;
  }
  .doclib-chat-row.doclib-card-expanded .doclib-chat-preview {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    display: flex !important;
    flex-direction: column !important;
  }
  .doclib-chat-row.doclib-card-expanded .doclib-chat-preview-messages {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    max-height: none !important;
    overflow-y: auto !important;
  }
}
.doclib-chat-preview-actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 8px;
  padding: 2px 0 2px;
  border-top: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
  margin-top: 2px;
  flex-shrink: 0;
  /* Whole action footer nudged down 5px. */
  position: relative;
  top: 5px;
}
.doclib-chat-delete-btn,
.doclib-chat-archive-btn,
.doclib-chat-restore-btn,
.doclib-chat-discuss-btn,
.doclib-chat-copy-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 10px;
  padding: 3px 8px;
  border-radius: 4px;
  background: none;
  cursor: pointer;
  font-family: inherit;
  transition: border-color 0.15s, color 0.15s;
}
.doclib-chat-archive-btn,
.doclib-chat-restore-btn,
.doclib-chat-discuss-btn,
.doclib-chat-copy-btn {
  color: var(--fg-muted);
  border: 1px solid var(--border);
}
/* Restore sits on the right of the Delete in archive previews. */
.doclib-chat-restore-btn { margin-left: auto; }
.doclib-chat-restore-btn:hover {
  color: var(--accent, var(--red));
  border-color: var(--accent, var(--red));
}
/* Delete + Archive pin to the left of the actions row; the
   `margin-right: auto` on the last left-side button (Archive) pushes
   Copy + Open to the right. Delete sits furthest left. */
.doclib-chat-archive-btn { margin-right: auto; }
.doclib-chat-archive-btn:hover,
.doclib-chat-discuss-btn:hover,
.doclib-chat-copy-btn:hover {
  color: var(--accent, var(--red));
  border-color: var(--accent, var(--red));
}
.doclib-chat-delete-btn {
  color: var(--color-danger, #e06c75);
  border: 1px solid var(--color-danger, #e06c75);
}
.doclib-chat-delete-btn:hover {
  color: #ff4d4d;
  border-color: #ff4d4d;
}
/* When a chat is archived there's no Archive button, so Delete becomes
   the only left-side action and needs the auto margin to push Open right. */
.doclib-chat-preview-actions:not(:has(.doclib-chat-archive-btn)) > .doclib-chat-delete-btn {
  margin-right: auto;
}
/* Hide the "..." menu while the chat card is expanded — archive +
   delete live in the preview footer instead. */
.doclib-chat-row.doclib-card-expanded ._chat-menu { display: none; }
.doclib-chat-preview-actions.doclib-chat-preview-actions-top {
  border-top: none;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
  margin-top: 4px;
  margin-bottom: 6px;
  padding: 0 0 6px;
  justify-content: flex-start;
}
.doclib-chat-preview-messages .doclib-chat-msg {
  margin: 4px 0 10px;
  padding-left: 8px;
  border-left: 2px solid color-mix(in srgb, var(--border) 70%, transparent);
}
.doclib-chat-preview-messages .doclib-chat-msg.user {
  border-left-color: color-mix(in srgb, var(--accent, var(--red)) 60%, transparent);
}
.doclib-chat-preview-messages .doclib-chat-msg-role {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.55;
  margin-bottom: 2px;
}
.doclib-chat-preview-messages .doclib-chat-msg.user .doclib-chat-msg-role {
  color: var(--accent, var(--red));
  opacity: 0.85;
}
.doclib-chat-preview-messages .doclib-chat-msg-body {
  white-space: pre-wrap;
  word-break: break-word;
  opacity: 0.85;
  line-height: 1.45;
}
/* Chat-bubble preview — mirrors the real chat layout. User bubbles hug
   right with an accent tint; assistant bubbles hug left with a neutral
   panel tint and a small model tag at the top. */
.doclib-chat-bubble-row {
  display: flex;
  margin: 6px 0;
}
.doclib-chat-bubble-row.user { justify-content: flex-end; }
.doclib-chat-bubble-row.assistant { justify-content: flex-start; }
.doclib-chat-bubble {
  max-width: 85%;
  padding: 6px 10px 8px;
  border-radius: 14px;
  font-size: 11px;
  line-height: 1.45;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--fg) 4%, transparent);
}
.doclib-chat-bubble-row.user .doclib-chat-bubble {
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 30%, transparent);
  border-bottom-right-radius: 4px;
}
.doclib-chat-bubble-row.assistant .doclib-chat-bubble {
  border-bottom-left-radius: 4px;
}
.doclib-chat-msg-model {
  display: inline-block;
  font-size: 8px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.55;
  margin-bottom: 3px;
  padding: 1px 6px;
  border-radius: 6px;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  color: var(--fg-muted, var(--fg));
}
.doclib-chat-bubble-body { word-break: break-word; }
.doclib-chat-bubble-body p { margin: 4px 0; }
.doclib-chat-bubble-body p:first-child { margin-top: 0; }
.doclib-chat-bubble-body p:last-child { margin-bottom: 0; }
.doclib-chat-bubble-body pre {
  font-size: 10px;
  margin: 4px 0;
  padding: 6px 8px;
  background: color-mix(in srgb, var(--bg) 60%, transparent);
  border-radius: 6px;
  overflow-x: auto;
}
.doclib-chat-bubble-body code {
  font-size: 10px;
  padding: 1px 4px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.doclib-chat-bubble-body pre code { background: none; padding: 0; }
.doclib-chat-bubble-body ul,
.doclib-chat-bubble-body ol {
  margin: 4px 0;
  padding-left: 18px;
}
.doclib-card-collapse-chevron {
  display: none;
  align-items: center;
  opacity: 0.3;
  cursor: pointer;
  flex-shrink: 0;
  margin-left: 4px;
  transition: opacity 0.15s;
}
.doclib-card-collapse-chevron:hover {
  opacity: 0.7;
}
.doclib-card-expanded .doclib-card-collapse-chevron {
  display: inline-flex;
}
/* Expanded card — fills the whole grid */
.doclib-card.doclib-card-expanded {
  flex: 1;
  min-height: 0;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  border: 1px solid var(--border);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: doclib-expand 0.2s ease-out;
}
@keyframes doclib-expand {
  from { opacity: 0.5; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}
.doclib-card.doclib-card-expanded {
  flex-direction: column !important;
  align-items: stretch !important;
  max-height: none !important;
  gap: 4px !important;
}
.doclib-card.doclib-card-expanded > .memory-item-actions { display: none; }
/* Non-preview children of an expanded doc card carry an inline flex:1 from
   the row layout. With the card now flex-column those children would steal
   half the height and shrink the preview — pin them at auto height. */
.doclib-card.doclib-card-expanded > div:not(.doclib-card-preview):not(.doclib-card-header):not(.memory-item-actions):not(.email-card-reader) {
  flex: 0 0 auto !important;
}
/* The email reader IS the scroll container — keep its flex:1 so its
   internal body can claim the rest of the card's height and scroll. */
.doclib-card.doclib-card-expanded > .email-card-reader {
  flex: 1 1 auto !important;
  min-height: 0 !important;
}
.doclib-card.doclib-card-expanded .doclib-card-preview {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto !important;
  overflow-y: auto;
  min-height: 0;
}
.doclib-card.doclib-card-expanded .doclib-card-expanded-actions {
  display: flex;
  flex-shrink: 0;
}

/* Collapsible skills section headers (Your skills / Built-in). */
.skills-section-header {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
  padding: 6px 4px 4px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  opacity: 0.6;
  font-weight: 600;
}
.skills-section-header:hover { opacity: 0.9; }
.skills-section-chevron { transition: transform 0.15s ease; flex-shrink: 0; }
.skills-section-header.collapsed .skills-section-chevron { transform: rotate(-90deg); }
.skills-section-count {
  margin-left: auto;
  opacity: 0.6;
  font-variant-numeric: tabular-nums;
  font-weight: normal;
}
.skill-card-section-hidden { display: none !important; }
/* Warning banner shown when previewing/editing a built-in capability. */
.skill-builtin-warn {
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--color-warning, #f0ad4e);
  background: color-mix(in srgb, var(--color-warning, #f0ad4e) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-warning, #f0ad4e) 35%, transparent);
  border-radius: 6px;
  padding: 6px 9px;
  margin: 0 0 8px;
  flex-shrink: 0;
}
/* Hide the section headers while a card is expanded (full-screen reader). */
.doclib-grid:has(.doclib-card-expanded) > .skills-section-header { display: none; }

/* #skills-list wears both .memory-list and .doclib-grid. doclib-grid's
   max-height:400px would cap the list AND clamp an expanded card — keep
   the memory-list fill-the-modal behaviour instead. */
#skills-list.doclib-grid {
  max-height: none;
  flex: 1;
  min-height: 0;
}
#skills-list.doclib-grid:has(.doclib-card-expanded) {
  overflow: hidden;  /* expanded card owns its own scroll */
}
/* When a skill is expanded, hide the sibling Add-Skill form card so the
   expanded SKILL.md uses the full panel. Otherwise the two admin-cards
   split the height ~50/50 and the expanded skill is stuck in its half
   while the Add form idles in the other. */
.memory-tab-panel[data-memory-panel="skills"]:has(.doclib-card-expanded) > .admin-card:nth-of-type(2) {
  display: none !important;
}

/* Skills cards reuse the doclib expand/footer machinery. The SKILL.md
   preview + the edit textarea need to fill the expanded card and own
   their own scroll, same as a document preview. */
/* Collapsed skill rows used a smaller (0.9em code) title, so the click row
   read slimmer than document/chat/library cards. Give the header a min-height
   so the tap target matches the other library items. */
.skill-card > .skill-card-header {
  min-height: 46px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 6px;
}
.skill-conf-dot {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}
/* Name + description column. Name wraps to 2 lines (skill slugs are long —
   use the space rather than truncating to "check-model-downl…"). */
.skill-card-textcol {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
}
.skill-card-name {
  font-weight: 600;
  font-size: 0.9em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
  line-height: 1.3;
}
.skill-card-desc {
  font-size: 10px;
  opacity: 0.55;
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Right cluster — pills (right-aligned), stats, then the menu/chevron. */
.skill-card-right {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  margin-left: auto;
}
.skill-card-right .memory-cat-badge { flex-shrink: 0; }
.skill-model-pill {
  max-width: 150px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.skill-model-student {
  background: color-mix(in srgb, var(--fg) 10%, transparent);
}
.skill-model-teacher {
  background: color-mix(in srgb, var(--color-warning, #f0ad4e) 24%, transparent);
  color: var(--color-warning, #f0ad4e);
}
.skill-necessity-pill {
  max-width: 150px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.skill-necessity-duplicate {
  background: color-mix(in srgb, var(--color-warning, #f0ad4e) 22%, transparent);
  color: var(--color-warning, #f0ad4e);
}
.skill-necessity-trivial {
  background: color-mix(in srgb, var(--color-danger, #e06c75) 26%, transparent);
  color: var(--color-danger, #e06c75);
  border-color: color-mix(in srgb, var(--color-danger, #e06c75) 38%, transparent);
}
.skill-necessity-irrelevant {
  background: color-mix(in srgb, var(--color-danger, #e06c75) 18%, transparent);
  color: var(--color-danger, #e06c75);
}
.skill-duplicate-keep {
  background: color-mix(in srgb, var(--color-success, #4ade80) 22%, transparent);
  color: var(--color-success, #4ade80);
}
.skill-duplicate-lower {
  background: color-mix(in srgb, var(--color-danger, #e06c75) 14%, transparent);
  color: var(--color-danger, #e06c75);
}
.skill-stats {
  font-size: 9px;
  font-family: monospace;
  white-space: nowrap;
  color: color-mix(in srgb, var(--fg) 45%, transparent);
}
.skill-conf { font-weight: 700; }
.skill-verified,
.skill-teachermark,
.skill-needsmark {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  margin-right: 3px;
}
.skill-verified { color: var(--accent, #4ade80); }
.skill-teachermark { color: var(--color-warning, #f0ad4e); }
.skill-needsmark { color: var(--color-danger, #e06c75); cursor: help; }

/* The card currently being processed by an "Audit now" run glows + pulses so
   it's obvious which one the audit is on. */
@keyframes skill-audit-pulse {
  0%, 100% { box-shadow: 0 0 6px 0 color-mix(in srgb, var(--accent, #4ade80) 55%, transparent); }
  50%      { box-shadow: 0 0 16px 3px color-mix(in srgb, var(--accent, #4ade80) 85%, transparent); }
}
.skill-card.skill-audit-active {
  border-color: var(--accent, #4ade80) !important;
  animation: skill-audit-pulse 1.3s ease-in-out infinite;
}

/* A test is running for this skill — the app's whirlpool spinner is injected
   next to the name from JS (see _setCardRunning) so the collapsed/folded card
   still makes it obvious work is in progress. */
/* Collapsed bar shows the kebab menu; expanded shows an up-chevron to
   collapse. (Toggled by the .doclib-card-expanded class.) */
.skill-kebab-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 4px;
  margin: 0;
  cursor: pointer;
  color: var(--fg);
  opacity: 0.5;
  border-radius: 5px;
  flex-shrink: 0;
  position: relative;
  top: -2px;
}
.skill-kebab-btn:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 10%, transparent); }
.skill-chevron-up { display: none; align-items: center; opacity: 0.5; flex-shrink: 0; }
.skill-card.doclib-card-expanded .skill-kebab-btn { display: none; }
.skill-card.doclib-card-expanded .skill-chevron-up { display: inline-flex; }
/* Kebab dropdown */
.skill-kebab-menu {
  position: fixed;
  z-index: 100002;
  min-width: 150px;
  padding: 4px;
  background: var(--panel, var(--bg));
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.22);
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.skill-kebab-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 9px;
  background: none;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  color: var(--fg);
  font-size: 12px;
  text-align: left;
}
.skill-kebab-item:hover { background: color-mix(in srgb, var(--fg) 10%, transparent); }
.skill-kebab-item.danger { color: var(--color-error, #e55); }
.skill-kebab-item.danger:hover { background: color-mix(in srgb, var(--color-error, #e55) 15%, transparent); }
/* Section labels separating "Your skills" from the read-only "Built-in
   capabilities" list. */
.skills-section-label {
  font-size: 10px;
  opacity: 0.5;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 10px 2px 4px;
  flex-shrink: 0;
}
.skills-section-label:first-child { margin-top: 0; }
/* When a learned skill is expanded the grid hides sibling cards; hide the
   section labels too so they don't orphan above the collapsed list. */
#skills-list:has(.doclib-card-expanded) .skills-section-label { display: none; }
/* Built-in cards are read-only — no expand/hover-pointer affordance. */
.skill-builtin-card { cursor: default; }
.skill-builtin-card:hover { border-color: var(--border); }
.skill-card-preview .skill-md-pre {
  font-family: ui-monospace, 'SF Mono', 'Fira Code', monospace;
  font-size: 11.5px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}
/* Size the expanded skill card to its content (footer sits right under the
   preview) but cap at the modal height so a long SKILL.md scrolls instead
   of overflowing. The inherited `.doclib-card-expanded { flex: 1 }` forced
   the card to fill the whole modal, which left a big empty void between a
   short skill's text and the footer. */
.skill-card.doclib-card-expanded {
  flex: 0 1 auto !important;
  max-height: 100% !important;
}
/* :has()-free expand layout. skills.js adds .skills-has-expanded to the
   skills admin-card when a skill opens, so this works on engines that don't
   support :has() (e.g. older Firefox mobile, where the expand stuck at
   ~50%). Hide everything but the list so the grid fills the card; the
   absolutely-positioned expanded card (mobile) then fills the grid. */
.admin-card.skills-has-expanded > *:not(#skills-list) {
  display: none !important;
}
.admin-card.skills-has-expanded > #skills-list.doclib-grid {
  flex: 1 1 auto !important;
  height: 100% !important;
  min-height: 0 !important;
  overflow: hidden !important;
}
/* Expand animation — a clean opacity fade. The mobile card snaps to a
   full-screen overlay (position:absolute), so any translate/scale on it
   reads as a jarring "explosion"; a pure fade just appears smoothly. (No
   transform also keeps skills.js's height measurement accurate.) */
@keyframes skill-card-expand {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.skill-card.doclib-card-expanded {
  animation: skill-card-expand 0.18s ease-out;
}
/* Switching directly from one expanded card to another: no fade (it would
   show the previous card collapsing through the semi-transparent new one). */
.skill-card.doclib-card-expanded.skill-expand-instant {
  animation: none !important;
}
@media (prefers-reduced-motion: reduce) {
  .skill-card.doclib-card-expanded,
  .skill-card.doclib-card-expanded .doclib-card-preview {
    animation: none !important;
  }
}
/* The preview WRAPPER inherits flex:1 (grow) from the generic doclib expand
   rule — that's what stretched the card and left a void / pushed the footer
   out. Size it to content (shrink + scroll only when long) so the footer
   sits right under the preview and stays fully visible. */
.skill-card.doclib-card-expanded .doclib-card-preview {
  flex: 0 1 auto !important;
  min-height: 0;
  /* The footer lives INSIDE this wrapper. Don't let the wrapper scroll, or
     the footer scrolls off with the content. Keep it clipped; the <pre>
     inside is the scroller, so the footer stays pinned at the bottom. */
  overflow: hidden !important;
}
.skill-card.doclib-card-expanded .skill-md-pre {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
/* Skill footer buttons sit 4px high (shared rule sets top:-4px) — drop them
   back down 4px so they're vertically centered in the skill card footer. */
.skill-card .doclib-card-expanded-actions > .doclib-card-action-btn,
.skill-card .doclib-card-expanded-actions .doclib-action-btn-row > .doclib-card-action-btn {
  top: 0;
}
/* Skills select-mode bulk bar — nudge Cancel / Approve / Delete up 2px. */
#skills-bulk-bar .memory-toolbar-btn {
  position: relative;
  top: -2px;
}
/* ── Audit-all progress panel ── */
.skills-audit-panel {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  margin: 6px 0;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
}
.skills-audit-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.skills-audit-title { font-size: 12px; font-weight: 600; }
.skills-audit-bar { height: 4px; border-radius: 2px; background: color-mix(in srgb, var(--fg) 12%, transparent); margin: 6px 0; overflow: hidden; }
.skills-audit-fill { height: 100%; background: var(--accent, var(--red)); transition: width 0.3s ease; }
.skills-audit-summary { font-size: 11px; opacity: 0.7; margin-bottom: 4px; }
.skills-audit-log {
  max-height: 22vh; overflow-y: auto;
  font-family: ui-monospace, 'SF Mono', 'Fira Code', monospace;
  font-size: 10.5px; line-height: 1.5; opacity: 0.8;
}
/* ── Skill test monitor + AI eval verdict ── */
.skill-test { display: flex; flex-direction: column; gap: 8px; min-height: 0; }
.skill-test-log {
  flex: 1 1 auto;
  min-height: 120px;
  max-height: 48vh;
  overflow-y: auto;
  font-family: ui-monospace, 'SF Mono', 'Fira Code', monospace;
  font-size: 9.5px;
  line-height: 1.45;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  white-space: pre-wrap;
  word-break: break-word;
}
.skill-test-log > div { margin: 1px 0; }
.skill-test-meta  { opacity: 0.5; }
.skill-test-task  { color: var(--accent, var(--red)); font-weight: 500; font-size: 9px; opacity: 0.85; }
.skill-test-round { opacity: 0.6; margin-top: 6px !important; }
.skill-test-tool  { color: var(--accent, var(--red)); opacity: 0.85; }
.skill-test-out   { opacity: 0.7; padding-left: 10px; }
.skill-test-say   { white-space: pre-wrap; }
.skill-test-err   { color: var(--color-danger, #e06c75); }
.skill-eval-head  { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.skill-eval-badge {
  font-size: 10px; font-weight: 700; letter-spacing: 0.03em;
  padding: 2px 8px; border-radius: 10px; flex-shrink: 0;
}
.skill-eval-ok   { background: color-mix(in srgb, var(--color-success, #4ade80) 26%, transparent); color: var(--color-success, #4ade80); }
.skill-eval-warn { background: color-mix(in srgb, var(--color-warning, #f0ad4e) 26%, transparent); color: var(--color-warning, #f0ad4e); }
.skill-eval-bad  { background: color-mix(in srgb, var(--color-danger, #e06c75) 26%, transparent); color: var(--color-danger, #e06c75); }
.skill-eval-unknown { background: color-mix(in srgb, var(--fg) 14%, transparent); }
.skill-eval-summary { font-size: 11px; opacity: 0.85; }
.skill-eval-issues { margin: 6px 0 0; padding-left: 18px; font-size: 11px; opacity: 0.8; }
.skill-eval-issues li { margin: 2px 0; }
.skill-eval-actions { display: flex; gap: 6px; justify-content: flex-end; margin-top: 8px; }
.skill-eval-approve.suggested {
  border-color: var(--color-success, #4ade80) !important;
  color: var(--color-success, #4ade80) !important;
}
/* Already published — the button confirms the state (click to unpublish). */
.skill-eval-approve.is-approved {
  border-color: var(--color-success, #4ade80) !important;
  color: var(--color-success, #4ade80) !important;
  background: color-mix(in srgb, var(--color-success, #4ade80) 14%, transparent) !important;
}
/* Add-Skill form: a "rich placeholder" overlay so only the FIRST word (Title,
   When to use, How, Tags) is accent-colored while the rest stays muted — real
   placeholders can't be partially colored. The native placeholder is a single
   space so :placeholder-shown still toggles the overlay. */
.skill-ph-wrap { position: relative; }
.skill-ph-wrap .skill-hint-input { width: 100%; box-sizing: border-box; }
.skill-rich-ph {
  position: absolute;
  left: 11px; right: 11px; top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  font-size: 12px;
  line-height: 1.2;
  color: color-mix(in srgb, var(--fg) 40%, transparent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Textarea: align the hint to the first line instead of vertical-centering. */
.skill-rich-ph-top { top: 7px; transform: none; }
.skill-rich-ph .k { color: var(--accent, var(--red)); }
/* Hide the overlay once the field has real content. */
.skill-hint-input:not(:placeholder-shown) ~ .skill-rich-ph { display: none; }
.skill-md-editor {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  box-sizing: border-box;
  resize: none;
  font-family: ui-monospace, 'SF Mono', 'Fira Code', monospace;
  font-size: 11.5px;
  line-height: 1.5;
  padding: 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
}
.doclib-empty {
  text-align: center;
  color: color-mix(in srgb, var(--fg) 35%, transparent);
  padding: 32px 16px;
  font-size: 12px;
  font-style: italic;
}
/* Unified loading row across every Library tab (Chats / Documents / Research /
   Archive): one opacity, one size, with the whirlpool spinner next to it. */
.lib-loading-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 28px 16px;
  font-size: 12px;
  color: color-mix(in srgb, var(--fg) 45%, transparent);
}
.doclib-load-more {
  display: block;
  margin: 10px auto 0;
  padding: 6px 16px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg-muted);
  font-size: 11px;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
  flex-shrink: 0;
  position: relative;
  top: 8px;
}
/* Soft dark gradient above the button — blends the list content into the
   load-more strip instead of a hard cutoff. Spans the modal width and
   sits behind/above the button. */
.doclib-load-more::before {
  content: "";
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 100%;
  margin-bottom: 4px;
  width: min(600px, 92vw);
  height: 34px;
  pointer-events: none;
  background: linear-gradient(to top, color-mix(in srgb, #000 24%, transparent), transparent);
}
.doclib-load-more:hover {
  border-color: var(--red);
  color: var(--red);
}

/* Document library toolbar buttons */
.doclib-toolbar-btn {
  background: none;
  border: 1px solid var(--border);
  color: var(--fg-muted);
  font-size: 11px;
  padding: 5px 10px;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.15s;
  white-space: nowrap;
  font-family: inherit;
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.doclib-toolbar-btn:hover {
  color: var(--fg);
  border-color: var(--fg);
}
.doclib-toolbar-btn.active {
  background: color-mix(in srgb, var(--red) 15%, transparent);
  border-color: color-mix(in srgb, var(--red) 40%, transparent);
  color: var(--red);
}
.doclib-toolbar-btn.danger {
  color: var(--color-error, #e55);
}
.doclib-toolbar-btn.danger:hover:not(:disabled) {
  border-color: var(--color-error, #e55);
}
.doclib-toolbar-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Bulk action bar */
.doclib-bulk-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
  border-radius: 6px;
  background: color-mix(in srgb, var(--red) 5%, transparent);
  font-size: 10px;
  margin-bottom: 8px;
}
.doclib-bulk-bar.hidden {
  display: none;
}
.doclib-bulk-check-all {
  display: flex;
  align-items: center;
  gap: 3px;
  cursor: pointer;
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  font-size: 10px;
}
#doclib-selected-count {
  color: color-mix(in srgb, var(--fg) 50%, transparent);
  font-size: 10px;
}
#doclib-bulk-bar .memory-toolbar-btn {
  position: relative;
  top: -3px;
}

/* Custom checkboxes */
.doclib-select-cb,
.doclib-bulk-check-all input {
  -webkit-appearance: none;
  appearance: none;
  width: 13px;
  height: 13px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  cursor: pointer;
  flex-shrink: 0;
  margin: 0;
  position: relative;
  transition: all 0.15s;
}
.doclib-select-cb:hover,
.doclib-bulk-check-all input:hover {
  border-color: var(--red);
}
.doclib-select-cb:checked,
.doclib-bulk-check-all input:checked {
  background: var(--red);
  border-color: var(--red);
}
.doclib-select-cb:checked::after,
.doclib-bulk-check-all input:checked::after {
  content: '';
  position: absolute;
  left: 3px;
  top: 0px;
  width: 4px;
  height: 8px;
  border: solid var(--bg);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}


/* + button outside scroll area (when doc is on right side) */

@media (max-width: 600px) {
  .doclib-toolbar {
    flex-direction: column;
  }
  .doclib-card-session,
  .doclib-card-time {
    display: none;
  }
  .doclib-card-preview {
    max-height: 40vh;
  }
}

/* ── Archive browser ── */
.archive-list {
  grid-template-columns: 1fr !important;
  gap: 4px !important;
}
.archive-row {
  flex-direction: row !important;
}
.archive-row .doclib-card-header {
  flex: 1;
  padding: 8px 10px;
}
.archive-row .doclib-card-title {
  flex: 1;
  min-width: 0;
}
/* Archive column layout */
.archive-header {
  display: flex;
  align-items: center;
  padding: 4px 10px;
  gap: 6px;
  font-size: 9px;
  font-weight: 600;
  opacity: 0.35;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.archive-col-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-left: -4px;
}
.archive-col-msgs {
  width: 40px;
  flex-shrink: 0;
  text-align: left;
  margin-left: -8px;
}
.archive-col-model {
  width: 90px;
  flex-shrink: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.archive-col-time {
  width: 55px;
  flex-shrink: 0;
  text-align: right;
}
.archive-col-menu {
  width: 24px;
  flex-shrink: 0;
}
.archive-menu-btn {
  background: transparent;
  border: none;
  color: var(--fg);
  opacity: 0.3;
  cursor: pointer;
  padding: 4px;
  line-height: 0;
  border-radius: 4px;
  transition: background .15s, opacity .15s;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  margin-left: 4px;
  vertical-align: middle;
  height: 100%;
}
.archive-menu-btn svg {
  display: block;
  margin-top: -6px;
}
.archive-menu-btn:hover {
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  opacity: 1;
}

/* ── Gallery (image library) ── */

.gallery-modal-content {
  width: min(820px, 94vw);
  max-height: 92vh;
  font-size: 12px;
}
/* While the Edit tab is active the editor needs a *definite* height so
   the right-side tool panel (`.ge-right-panel { overflow-y:auto }`)
   scrolls INTERNALLY. Without this the modal only has `max-height`, so
   `.gallery-editor { height:100% }` can't resolve — the editor grows to
   its full content height, overflows the modal body, and the modal-body
   scrollbar that appears can't be wheel-scrolled because the editor's
   `overscroll-behavior:contain` blocks the chain. Pinning a real height
   here makes the panel bounded and scrollable as designed. Scoped to
   the editor view (matched via the container's inline `display:flex`)
   so the Photos/Albums views keep sizing to their content. */
.gallery-modal-content:has(#gallery-editor-container[style*="flex"]) {
  height: 92vh;
}
/* Photo-detail view sizing (issue #314).
   The detail view is rendered as a `position:absolute; inset:0` overlay
   *inside* `.gallery-images-container`, painted over the photo grid. Because
   it's absolutely positioned it can't contribute to the container's height —
   the container (and therefore the overlay's `inset:0` box) collapses to the
   height of the grid sitting behind it. When the library only has a few
   photos that grid is short, so the detail view is crushed: the image is
   clipped and the metadata sidebar (`overflow-y:auto`) is squeezed into a
   tiny, internally-scrolling strip. (With a large library the grid is tall,
   which is why it looked fine in the demo video but cramped for users with
   few photos.)
   Fix: when the detail view is open, hide the grid-view siblings and drop the
   overlay into normal flow. The container — and the window, up to its 92vh
   max-height — then sizes to the detail's own content (image + metadata), so
   nothing is clipped or squeezed regardless of how many photos exist. Scoped
   via the detail element's inline `display:flex` so the grid / albums views
   keep sizing to their own content. Works on both desktop and the mobile
   full-screen sheet. */
#gallery-images-container:has(> #gallery-detail[style*="flex"]) > *:not(#gallery-detail) {
  display: none !important;
}
#gallery-images-container:has(> #gallery-detail[style*="flex"]) > #gallery-detail {
  position: static;
}
/* Containing block for the photo-detail overlay — keeps it inside the body
   so it sits below the modal header and the tab strip instead of covering them. */
.gallery-images-container { position: relative; }
.gallery-modal-content .modal-header h4 {
  font-size: 1rem;
}
.gallery-stats {
  font-size: 10px;
  color: color-mix(in srgb, var(--fg) 50%, transparent);
  margin-bottom: 4px;
}
.gallery-toolbar {
  display: flex;
  gap: 6px;
  margin-bottom: 4px;
  align-items: center;
}
.gallery-toolbar-break { display: none; }
/* Search input + its "↵ enter to tag" hint live in a relative wrapper that
   carries the 2px down-shift, so input and hint move together. */
.gallery-search-wrap { position: relative; flex: 1; min-width: 0; display: flex; top: 2px; }
.gallery-search {
  flex: 1;
  width: 100%;
  padding: 4px 8px;
  padding-right: 78px;   /* room for the enter hint */
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
  font-size: 12px;
  outline: none;
}
.gallery-search-enter-hint {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 10px;
  font-weight: 600;
  color: var(--accent, var(--red));
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s;
  white-space: nowrap;
}
/* Show the hint only once the user has typed something (placeholder gone). */
.gallery-search:not(:placeholder-shown) ~ .gallery-search-enter-hint { opacity: 0.95; }
#gallery-select-btn { position: relative; top: 2px; font-size: 12px; padding: 10px 11px 12px; }
.gallery-search:focus {
  border-color: var(--red);
}
.gallery-model-filter,
.gallery-sort {
  padding: 4px 8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  font: inherit;
  font-size: 11px;
  cursor: pointer;
}
.gallery-tag-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 4px;
}
.gallery-chip {
  height: 28px;
  box-sizing: border-box;
  padding: 0 13px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border-radius: 14px;
  font-size: 12px;
  line-height: 1;
  border: 1px solid var(--border);
  background: none;
  color: var(--fg);
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.gallery-chip:hover {
  border-color: var(--red);
}
.gallery-chip.active {
  background: color-mix(in srgb, var(--red) 15%, transparent);
  border-color: color-mix(in srgb, var(--red) 40%, transparent);
  color: var(--red);
}
/* Album chips row */
.gallery-album-chips {
  display: flex; gap: 4px; flex-wrap: wrap; padding: 0 0 3px;
}
.gallery-chip-fav { color: var(--red); }
.gallery-chip-fav.active { background: color-mix(in srgb, var(--red) 15%, transparent); border-color: var(--red); }
.gallery-chip-add { opacity: 0.5; font-size: 14px; padding: 2px 10px; }
/* Active-album indicator — appears when the Photos grid is filtered to one album. */
.gallery-chip-active-album {
  display: inline-flex; align-items: center; gap: 4px;
  background: color-mix(in srgb, var(--red) 14%, transparent);
  border-color: color-mix(in srgb, var(--red) 45%, transparent);
  position: relative;
  top: 6px;
}
.gallery-chip-clear {
  background: none;
  border: none;
  color: inherit;
  opacity: 0.6;
  cursor: pointer;
  padding: 0;
  margin-left: 1px;
  width: 12px;
  height: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  line-height: 1;
  position: relative;
  top: -4px;
}
.gallery-chip-clear:hover { opacity: 1; }



/* Favorite button on card */
/* Video cards: <video> fills the same slot as <img>, plus a small play badge
   so it's clear the thumbnail represents a video. */
.gallery-card video {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  background: #000;
  display: block;
}
.gallery-card-play {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 36px; height: 36px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  border-radius: 50%;
  pointer-events: none;
  z-index: 1;
}
.gallery-card-play svg { margin-left: 2px; }
.gallery-detail-image video {
  max-width: 100%;
  max-height: 70vh;
  display: block;
}

.gallery-fav-btn {
  position: absolute; top: 0px; right: 4px; z-index: 2;
  background: rgba(0,0,0,0.4); border: none; border-radius: 50%;
  width: 26px; height: 26px; font-size: 14px;
  color: rgba(255,255,255,0.6); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 0.15s;
  padding: 0;
}
.gallery-card:hover .gallery-fav-btn { opacity: 1; }
.gallery-fav-btn.gallery-fav-active { opacity: 1; color: var(--red); }

.gallery-dl-btn {
  position: absolute; top: 0px; left: 4px; z-index: 2;
  background: rgba(0,0,0,0.4); border: none; border-radius: 50%;
  width: 26px; height: 26px;
  color: rgba(255,255,255,0.75); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 0.15s, background 0.15s;
  padding: 0;
}
.gallery-card:hover .gallery-dl-btn { opacity: 1; }
.gallery-dl-btn:hover { background: rgba(0,0,0,0.7); color: #fff; }
@media (hover: none) {
  .gallery-dl-btn { opacity: 0.55; }
}
/* In select mode the per-thumbnail hover buttons (favorite + download) just
   get in the way of picking — hide them so the card is a clean select target.
   `body.gallery-selecting` is the authoritative signal (one class for the
   whole grid) — the per-card .gallery-card-selectable is kept as a backup
   in case the body class is missed. */
body.gallery-selecting .gallery-fav-btn,
body.gallery-selecting .gallery-dl-btn,
.gallery-card-selectable .gallery-fav-btn,
.gallery-card-selectable .gallery-dl-btn {
  display: none !important;
}

/* AI tag chips in detail view */
.gallery-ai-tags { display: flex; gap: 4px; flex-wrap: wrap; }
/* Space the user's tag chips off the "add tag" input below them. */
#gallery-user-tag-chips { margin-bottom: 4px; }
.gallery-ai-chip {
  background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 25%, transparent);
  border-radius: 10px; padding: 3px 8px; font-size: 10px;
  color: var(--fg); opacity: 0.8;
  font: inherit; font-size: 10px;
  cursor: pointer;
  transition: background 0.12s, opacity 0.12s, border-color 0.12s;
}
.gallery-ai-chip:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 50%, transparent);
}
/* AI-generated tags: muted neutral with a tiny sparkle marker so the
   user can tell at a glance these came from the model, not them. */
.gallery-aitag-chip {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border-color: color-mix(in srgb, var(--fg) 18%, transparent);
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.gallery-aitag-chip:hover {
  background: color-mix(in srgb, var(--fg) 12%, transparent);
  border-color: color-mix(in srgb, var(--fg) 35%, transparent);
}
.gallery-aitag-mark {
  font-size: 8px;
  opacity: 0.55;
  color: var(--accent, var(--red));
}
/* User-applied tags keep the accent-colored chip — they're the personal/
   curated tags, so they get the strongest visual weight. */
.gallery-user-chip {
  font-weight: 600;
}
/* × to remove a user tag (appears inside the chip). */
.gallery-tag-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: 5px;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  font-size: 12px;
  line-height: 1;
  opacity: 0.55;
  transition: opacity 0.12s, background 0.12s, color 0.12s;
}
.gallery-user-chip:hover .gallery-tag-x { opacity: 0.85; }
.gallery-tag-x:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--red, #ff5555) 22%, transparent);
  color: var(--red, #ff5555);
}

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 8px;
  max-height: 60vh;
  overflow-y: auto;
  padding: 2px;
  transition: border-color 0.15s, background 0.15s;
}
.gallery-grid.gallery-dragover {
  border: 2px dashed var(--red);
  border-radius: 8px;
  background: color-mix(in srgb, var(--red) 5%, transparent);
}
.gallery-card {
  position: relative;
  aspect-ratio: 1;
  border-radius: 6px;
  overflow: hidden;
  border: 1px solid var(--border);
  cursor: pointer;
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}
/* Upload-affordance tile pinned to the top-left of the Photos grid. Matches
   the Upload album tile in the Albums tab — dashed border, centered icon. */
.gallery-card-upload {
  border-style: dashed;
  background: color-mix(in srgb, var(--fg) 3%, var(--bg));
  opacity: 0.75;
  transition: opacity 0.12s, border-color 0.15s, transform 0.15s;
}
/* The "Start AI tag" button turns into a Cancel control during a tag run. */
.gallery-tag-cancelling {
  color: var(--red) !important;
  border-color: color-mix(in srgb, var(--red) 45%, var(--border)) !important;
}
/* Skeleton/shimmer placeholder tiles shown while the first page of photos loads. */
.gallery-card-skeleton {
  cursor: default;
  background: linear-gradient(100deg,
    color-mix(in srgb, var(--fg) 4%, var(--panel)) 30%,
    color-mix(in srgb, var(--fg) 12%, var(--panel)) 50%,
    color-mix(in srgb, var(--fg) 4%, var(--panel)) 70%);
  background-size: 200% 100%;
  animation: gallery-skeleton-shimmer 1.25s ease-in-out infinite;
}
.gallery-card-skeleton:hover { transform: none; box-shadow: none; border-color: var(--border); }
@keyframes gallery-skeleton-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
/* Chat attachment image: shimmer skeleton + centered whirlpool shown while a
   sent photo uploads / its thumbnail loads (see chatRenderer buildAttachCards). */
.attach-image-skeleton {
  position: relative;
  width: 160px; height: 120px;
  border-radius: 6px;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(100deg,
    color-mix(in srgb, var(--fg) 4%, var(--panel)) 30%,
    color-mix(in srgb, var(--fg) 12%, var(--panel)) 50%,
    color-mix(in srgb, var(--fg) 4%, var(--panel)) 70%);
  background-size: 200% 100%;
  animation: gallery-skeleton-shimmer 1.25s ease-in-out infinite;
}
.attach-image-skeleton > .spinner-whirlpool { margin: 0 !important; }

/* Corner "Aa" button on a chat photo thumbnail — opens the vision/OCR editor
   so the user can correct what the vision model fed to the LLM. */
.attach-image-preview { position: relative; }
.attach-vision-model,
.attach-image-name {
  max-width: 300px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.attach-vision-model {
  font-size: 10px;
  color: var(--accent, var(--red));
  opacity: 0.85;
  margin-top: 3px;
}
.attach-image-name {
  font-size: 10px;
  opacity: 0.5;
  margin-top: 1px;
}
.attach-ocr-btn {
  position: absolute; top: 4px; right: 4px;
  z-index: 5;  /* sit above msg-action overlays so the click lands here */
  height: 22px;
  background: rgba(0,0,0,0.55); color: #fff;
  border: 1px solid rgba(255,255,255,0.2); border-radius: 4px;
  padding: 0 8px 0 6px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  font-size: 11px; font-weight: 500; line-height: 1;
  opacity: 0.75; transition: opacity 0.15s;
}
.attach-ocr-btn:hover { opacity: 1; }
.attach-ocr-btn svg { display: block; }
/* On mobile keep just the (larger) edit icon — the "Caption" label crowds
   the corner of the small chat photo. */
@media (max-width: 768px) {
  .attach-ocr-btn .attach-ocr-label { display: none; }
  .attach-ocr-btn {
    width: 28px; height: 28px;
    padding: 0;
  }
  .attach-ocr-btn svg { width: 16px; height: 16px; }
}

/* Vision/OCR text editor modal — opened from the "Aa" button above. */
.vision-editor-overlay {
  position: fixed; inset: 0; z-index: 9999;
  background: rgba(0,0,0,0.55);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
}
.vision-editor-panel {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
  width: min(560px, 92vw);
  max-height: 88vh;
  display: flex; flex-direction: column; gap: 8px;
  box-shadow: 0 14px 40px rgba(0,0,0,0.4);
}
.vision-editor-title {
  font-size: 13px; font-weight: 600; color: var(--fg);
  display: flex; align-items: center; gap: 6px;
}
.vision-editor-desc { font-size: 11px; opacity: 0.6; line-height: 1.4; }
.vision-editor-text {
  width: 100%; min-height: 180px;
  background: var(--panel); color: var(--fg);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 8px 10px;
  font-family: inherit; font-size: 12px; line-height: 1.5;
  resize: vertical; outline: none;
}
.vision-editor-text:focus { border-color: var(--accent-primary, var(--red)); }
.vision-editor-hint { font-size: 10.5px; opacity: 0.55; line-height: 1.4; }
.vision-editor-actions {
  display: flex; gap: 8px; justify-content: flex-end;
  margin-top: 4px;
}
.vision-editor-btn {
  background: var(--panel); color: var(--fg);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 6px 14px; font-size: 12px; cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
  display: inline-flex; align-items: center; gap: 5px;
}
.vision-editor-btn svg { display: block; }
/* Optical nudge: the button text sits 1px higher than the SVG glyphs
   alongside it. Drop the text down 1px so they line up. */
.vision-editor-btn .vision-btn-label { position: relative; top: 1px; }
.vision-editor-btn:hover { border-color: color-mix(in srgb, var(--fg) 40%, var(--border)); }
.vision-editor-btn-primary {
  background: var(--accent-primary, var(--red));
  color: #fff; border-color: transparent;
}
.vision-editor-btn-primary:disabled { opacity: 0.6; cursor: not-allowed; }

/* Quick full-size lightbox shown when the user taps a chat photo thumbnail. */
.attach-lightbox {
  position: fixed; inset: 0; z-index: 9998;
  background: rgba(0, 0, 0, 0.85);
  display: flex; align-items: center; justify-content: center;
  padding: 16px; cursor: zoom-out;
  animation: attach-lightbox-fade 0.12s ease-out;
}
.attach-lightbox img {
  max-width: 100%; max-height: 100%;
  border-radius: 6px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
  object-fit: contain;
}
.attach-lightbox-err {
  position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%);
  background: var(--bg); color: var(--fg);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 6px 10px; font-size: 12px;
}
@keyframes attach-lightbox-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.gallery-card-upload:hover {
  opacity: 1;
  border-color: var(--red);
  transform: translateY(-1px);
}
.gallery-card-upload-inner {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  color: var(--fg);
}
.gallery-card-upload-label {
  font-size: 12px;
  font-weight: 500;
  opacity: 0.8;
}
.gallery-card:hover {
  border-color: var(--red);
  box-shadow: 0 2px 8px color-mix(in srgb, var(--red) 15%, transparent);
  transform: translateY(-1px);
}
.gallery-select-dot {
  position: absolute; top: 6px; left: 6px; z-index: 2;
  width: 10px; height: 10px; border-radius: 50%; cursor: pointer;
  background: color-mix(in srgb, var(--fg) 30%, transparent);
  border: 1px solid color-mix(in srgb, var(--fg) 50%, transparent);
  transition: background 0.15s;
}
.gallery-select-dot.selected { background: var(--red); border-color: var(--red); }
.gallery-select-dot:hover { background: color-mix(in srgb, var(--red) 50%, transparent); }
/* Strong accent ring on the whole photo when it's selected — inset
   box-shadow (not outline) because outline-offset:-4px inside an
   overflow:hidden rounded card renders sharp-cornered on Firefox.
   box-shadow inset always respects the card's border-radius. */
.gallery-card:has(.gallery-select-dot.selected) {
  box-shadow: inset 0 0 0 7px var(--red);
}
.gallery-select-btn {
  padding: 9px 10px 11px; background: transparent; color: var(--fg);
  border: 1px solid var(--border); border-radius: 6px; cursor: pointer;
  font-size: 11px; font-family: inherit; opacity: 0.6; transition: all 0.15s;
  margin-top: -4px !important;
}
.gallery-select-btn:hover { opacity: 1; }
/* Match the library Select toggle's red tint (.memory-toolbar-btn.active).
   Bumped contrast so the toggled state actually reads on mobile — the prior
   15% red on transparent was nearly invisible against the page background. */
.gallery-select-btn.active {
  background: color-mix(in srgb, var(--red) 28%, transparent);
  color: var(--red);
  border-color: var(--red);
  font-weight: 600;
  opacity: 1;
}
/* Toolbar action buttons sit 2px lower than the generic select-btn so
   they baseline-align with the toolbar's selects/inputs instead of
   floating above them. */
.gallery-toolbar-action {
  margin-top: 0 !important;
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.gallery-bulk-bar {
  display: flex; align-items: center; gap: 8px; padding: 6px 0; font-size: 12px;
  justify-content: flex-end;
}
.gallery-bulk-bar.hidden { display: none; }
.gallery-bulk-all {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; opacity: 0.75; cursor: pointer; margin-right: auto;
}
.gallery-bulk-all input { accent-color: var(--accent, var(--red)); cursor: pointer; }
.gallery-bulk-delete {
  padding: 3px 10px; background: transparent; color: var(--red);
  border: 1px solid var(--red); border-radius: 4px; cursor: pointer;
  font-size: 11px; font-family: inherit;
}
.gallery-bulk-delete:hover { background: var(--red); color: #fff; }
.gallery-bulk-cancel {
  padding: 3px 10px; background: transparent; color: var(--fg);
  border: 1px solid var(--border); border-radius: 4px; cursor: pointer;
  font-size: 11px; font-family: inherit; opacity: 0.6;
}
.gallery-bulk-cancel:hover { opacity: 1; }
.gallery-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.gallery-card-info {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 6px 8px;
  background: linear-gradient(transparent, rgba(0,0,0,0.75));
  color: #fff;
}
.gallery-card-prompt {
  font-size: 10px;
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gallery-card-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
  font-size: 9px;
  opacity: 0.8;
}
.gallery-card-model {
  padding: 1px 5px;
  border-radius: 6px;
  background: rgba(255,255,255,0.15);
}
.gallery-empty {
  text-align: center;
  color: color-mix(in srgb, var(--fg) 35%, transparent);
  padding: 32px 16px;
  font-size: 12px;
  font-style: italic;
}
.gallery-load-more {
  display: block;
  margin: 10px auto 0;
  padding: 6px 16px;
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  cursor: pointer;
  font: inherit;
  font-size: 11px;
  transition: border-color 0.15s, color 0.15s;
}
.gallery-load-more:hover {
  border-color: var(--red);
  color: var(--red);
}

/* Gallery detail overlay */
.gallery-detail {
  position: absolute;
  inset: 0;
  background: var(--panel);
  z-index: 10;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}
.gallery-detail-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 8px 4px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  /* Pin to top so a tall (portrait) photo can't cover the Back / ⋮
     menu when the detail body grows past the visible area. */
  position: sticky;
  top: 0;
  background: var(--panel);
  /* Above the image's rotate/nav buttons (z-index:3) so they scroll cleanly
     behind the header instead of colliding with it. */
  z-index: 5;
  min-height: 0;
}
.gallery-detail-back {
  background: none;
  border: none;
  color: var(--fg);
  cursor: pointer;
  font: inherit;
  font-size: 12px;
  padding: 4px 8px;
  border-radius: 4px;
  position: relative;
  top: -2px;
}
/* Slightly larger header icons (back/edit/heart/⋮) — override the inline 14px. */
.gallery-detail-header svg { width: 16px; height: 16px; }
.gallery-detail-back:hover {
  background: color-mix(in srgb, var(--fg) 10%, transparent);
}
.gallery-detail-action {
  background: none;
  border: 1px solid var(--border);
  color: var(--fg);
  cursor: pointer;
  font: inherit;
  font-size: 11px;
  padding: 3px 8px;
  border-radius: 4px;
  transition: border-color 0.15s;
}
.gallery-detail-action:hover {
  border-color: var(--fg);
}
.gallery-detail-action.danger {
  color: var(--color-error, #e55);
}
.gallery-detail-action.danger:hover {
  border-color: var(--color-error, #e55);
}
/* Overflow menu — replaces the seven individual action buttons that used
   to crowd the right side of the detail header. */
.gallery-detail-menu-wrap { position: relative; }
.gallery-detail-menu-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 26px;
  padding: 0;
  position: relative;
  top: -2px;
}
.gallery-detail-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 150px;
  padding: 3px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 18%, transparent);
  z-index: 12;
  display: flex;
  flex-direction: column;
  gap: 0;
}
.gallery-detail-menu[hidden] { display: none; }
.gallery-detail-menu .dropdown-item-compact {
  width: 100%;
  background: none;
  border: none;
  text-align: left;
  font: inherit;
  /* Tighter than the global default — the photo-detail menu has 8
     items so every pixel of vertical padding adds up. */
  padding: 4px 8px;
  font-size: 11px;
  gap: 8px;
  line-height: 1.2;
}
.gallery-detail-menu .dropdown-item-compact .dropdown-icon,
.gallery-detail-menu .dropdown-item-compact .dropdown-icon svg {
  width: 12px;
  height: 12px;
}
.gallery-detail-body {
  display: flex;
  gap: 16px;
  padding: 12px;
  flex: 1;
  min-height: 0;
}
.gallery-detail-image {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
}
/* Shrink-wraps the actual image so overlay children (heart, face boxes)
   anchor to the IMAGE bounds, not the wider letterboxed wrapper. */
.gallery-detail-img-frame {
  position: relative;
  display: inline-flex;
  max-width: 100%;
  max-height: 80vh;
}
.gallery-detail-image img,
.gallery-detail-image video,
.gallery-detail-img-frame img,
.gallery-detail-img-frame video {
  max-width: 100%;
  max-height: 80vh;
  border-radius: 6px;
  object-fit: contain;
  display: block;
}
.gallery-detail-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: rgba(0, 0, 0, 0.45);
  color: rgba(255, 255, 255, 0.85);
  border: none;
  border-radius: 50%;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s, background 0.15s;
  z-index: 3;
  padding: 0;
}
.gallery-detail-image:hover .gallery-detail-nav { opacity: 0.85; }
.gallery-detail-nav:hover { opacity: 1 !important; background: rgba(0, 0, 0, 0.7); }
.gallery-detail-nav-prev { left: 8px; }
.gallery-detail-nav-next { right: 8px; }
/* Rotate buttons — same pattern as the prev/next arrows but pinned to the
   top corners. Hover-revealed so they don't clutter the image. */
.gallery-detail-rotate {
  position: absolute;
  top: 8px;
  background: rgba(0, 0, 0, 0.45);
  color: rgba(255, 255, 255, 0.85);
  border: none;
  border-radius: 50%;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s, background 0.15s;
  z-index: 3;
  padding: 0;
}
.gallery-detail-image:hover .gallery-detail-rotate { opacity: 0.85; }
.gallery-detail-rotate:hover { opacity: 1 !important; background: rgba(0, 0, 0, 0.7); }
.gallery-detail-rotate-ccw { left: 8px; }
.gallery-detail-rotate-cw  { right: 8px; }
/* Inline heart that lives next to the "Date" label in the sidebar.
   Tiny outline icon by default; fills red when favorited. */
.gallery-detail-date-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
}
.gallery-detail-fav-inline {
  background: none;
  border: none;
  padding: 0;
  /* Nudge up 8px so the heart sits visually above the Date label
     baseline rather than centred on it. */
  margin-top: -8px;
  border-radius: 4px;
  color: var(--fg-muted);
  opacity: 0.6;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  transition: opacity 0.15s, color 0.15s;
}
/* Shrink the SVG so the heart matches the height of the small uppercase
   Date label sitting next to it. */
.gallery-detail-fav-inline svg {
  width: 12px;
  height: 12px;
  display: block;
}
.gallery-detail-fav-inline:hover {
  opacity: 1;
}
.gallery-detail-fav-inline.active {
  opacity: 1;
  color: var(--red);
}
.gallery-detail-nav-disabled {
  opacity: 0 !important;
  pointer-events: none;
}
@media (hover: none) {
  .gallery-detail-nav { opacity: 0.7; }
}
.gallery-detail-sidebar {
  width: 240px;
  flex-shrink: 0;
  /* Vertical scroll for long metadata, but never horizontal — overflow:auto
     alone would let a wide child (e.g. long album name in the select) push
     the sidebar into horizontal-scroll mode. */
  overflow-x: hidden;
  overflow-y: auto;
  min-width: 0;
}
/* Constrain any child that could otherwise stretch the sidebar wider than
   its declared width (selects with long option text, long URLs, etc.). */
.gallery-detail-sidebar select,
.gallery-detail-sidebar input,
.gallery-detail-sidebar .gallery-detail-prompt {
  max-width: 100%;
  box-sizing: border-box;
}
.gallery-detail-section {
  margin-bottom: 12px;
}
.gallery-date-rel {
  opacity: 0.55;
  margin-left: 4px;
  font-size: 11px;
  font-style: italic;
}
.gallery-detail-section label {
  display: block;
  font-size: 10px;
  font-weight: 600;
  opacity: 0.6;
  margin-bottom: 3px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.gallery-detail-section div {
  font-size: 12px;
  word-break: break-word;
}
.gallery-detail-prompt {
  white-space: pre-wrap;
  line-height: 1.4;
}
.gallery-tag-input,
.gallery-detail-name-input {
  width: 100%;
  padding: 5px 8px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  font: inherit;
  font-size: 11px;
  margin-bottom: 4px;
}
.gallery-tag-input:focus,
.gallery-detail-name-input:focus {
  border-color: var(--red);
  outline: none;
}
/* ↵ enter hint inside the Add-a-tag field (accent), replacing the "press Enter"
   placeholder text. */
.gallery-tag-input-wrap { position: relative; }
.gallery-tag-input-wrap .gallery-tag-input { padding-right: 24px; }
.gallery-tag-enter-hint {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  margin-top: -2px;  /* the input has 4px bottom margin; center on the box */
  color: var(--accent, var(--red));
  font-size: 13px;
  line-height: 1;
  pointer-events: none;
  opacity: 0.8;
}
.gallery-detail-name-input { font-size: 13px; font-weight: 500; }
/* ↵ enter hint on the image name field — accent, shown while editing to signal
   that Enter saves. */
.gallery-name-wrap { position: relative; }
.gallery-name-enter {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  margin-top: -2px;
  color: var(--accent, var(--red));
  opacity: 0.85;
  pointer-events: none;
  display: none;
}
.gallery-name-wrap:focus-within .gallery-name-enter { display: inline-flex; }
.gallery-name-wrap:focus-within .gallery-detail-name-input { padding-right: 28px; }
.gallery-tag-save {
  background: none;
  border: 1px solid var(--border);
  color: var(--fg);
  cursor: pointer;
  font: inherit;
  font-size: 10px;
  padding: 3px 10px;
  border-radius: 4px;
  transition: border-color 0.15s, color 0.15s;
}
.gallery-tag-save:hover {
  border-color: var(--red);
  color: var(--red);
}

@media (max-width: 600px) {
  .gallery-grid {
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
    gap: 6px;
  }
  /* Single-row toolbar on mobile: search + Sources + Recent (sort) all share
     one row. The search bar shrinks so the dropdowns fit beside it. */
  .gallery-toolbar {
    flex-wrap: nowrap;
    gap: 4px;
  }
  .gallery-search-wrap {
    flex: 1 1 0;
    min-width: 0;
  }
  .gallery-search {
    /* Drop the desktop right-padding reserved for the "enter to tag" hint —
     hide the hint on mobile (below) so the input can use its full width. */
    padding-right: 8px;
  }
  .gallery-search-enter-hint { display: none; }
  /* Disable the row break so the filters stay inline with search. */
  .gallery-toolbar-break { display: none; }
  .gallery-model-filter,
  .gallery-sort {
    flex: 0 0 auto;
    /* Cap each dropdown so search keeps a usable amount of width. */
    max-width: 100px;
    font-size: 11px;
    padding: 4px 6px;
    /* Nudge down 2px to baseline with the search input (which carries a
       2px down-shift via its wrapper). */
    position: relative;
    top: 2px;
  }
  .gallery-select-btn {
    padding: 6px 8px 8px;
    font-size: 10px;
    flex-shrink: 0;
    /* Push Select to the far right of the single-row toolbar, regardless
       of its DOM position (sits between search and the dropdowns). */
    order: 99;
    margin-left: auto;
  }
  /* Tabs scroll horizontally rather than wrapping when narrow. */
  .gallery-tabs {
    overflow-x: auto;
    flex-wrap: nowrap;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .gallery-tabs::-webkit-scrollbar { display: none; }
  .gallery-tab { flex-shrink: 0; }
  /* Album chips also scroll horizontally so they don't stack. */
  .gallery-album-chips {
    overflow-x: auto;
    flex-wrap: nowrap;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-bottom: 4px;
  }
  .gallery-album-chips::-webkit-scrollbar { display: none; }
  .gallery-chip { flex-shrink: 0; }
  /* Tasks filter chips — same horizontal-swipe behaviour as the gallery
     albums / library chips on mobile: one row, slide-to-see-more. */
  .tasks-activity-filters {
    overflow-x: auto;
    flex-wrap: nowrap !important;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-bottom: 4px;
  }
  .tasks-activity-filters::-webkit-scrollbar { display: none; }
  .tasks-activity-filters > * { flex-shrink: 0; }
  /* Detail view */
  .gallery-detail-body {
    flex-direction: column;
  }
  .gallery-detail-sidebar {
    width: 100%;
  }
  /* Album grid: smaller tile minimum on mobile. */
  .gallery-albums-grid {
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 8px;
  }
}


/* ── Scoreboard table ── */
.scoreboard-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88em;
}
.scoreboard-table th {
  text-align: left;
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  font-weight: 600;
  font-size: 0.85em;
  color: color-mix(in srgb, var(--fg) 55%, transparent);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.scoreboard-table th:not(:first-child) {
  text-align: center;
}
.scoreboard-table td {
  padding: 5px 10px;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent);
  text-align: center;
}
.scoreboard-table td.scoreboard-model {
  text-align: left;
  font-weight: 500;
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.scoreboard-table td.scoreboard-pct {
  font-weight: 600;
  color: var(--red);
}
.scoreboard-table tbody tr:hover {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
}

/* ── Compare search results ── */
.compare-search-results {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.compare-search-result {
  padding: 6px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
}
.compare-search-result:last-child {
  border-bottom: none;
}
.search-result-title {
  color: var(--color-accent);
  text-decoration: none;
  font-weight: 500;
  font-size: 0.9em;
  display: block;
  line-height: 1.3;
}
.search-result-title:hover {
  color: var(--color-link-hover);
  text-decoration: underline;
}
.search-result-snippet {
  color: color-mix(in srgb, var(--fg) 70%, transparent);
  font-size: 0.82em;
  line-height: 1.4;
  margin-top: 2px;
}
.search-result-url {
  color: color-mix(in srgb, var(--fg) 35%, transparent);
  font-size: 0.75em;
  margin-top: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* ── Cookbook Tool ── */

/* ── Cookbook ── */
.cookbook-serve-preset {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  margin-bottom: 3px;
  cursor: pointer;
  transition: background 0.15s;
}
.cookbook-serve-preset:hover { background: color-mix(in srgb, var(--fg) 8%, transparent); }
.cookbook-serve-preset-name {
  font-size: 11px;
  font-weight: 500;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cookbook-serve-preset-meta {
  font-size: 9px;
  font-family: 'Fira Code', monospace;
  color: var(--fg-muted);
  opacity: 0.5;
}
.cookbook-serve-preset-launch {
  background: none;
  border: none;
  color: #50fa7b;
  cursor: pointer;
  font-size: 10px;
  opacity: 0.6;
  padding: 0 2px;
  position: relative;
  top: -4px;
}
.cookbook-serve-preset-launch:hover { opacity: 1; }
.cookbook-serve-preset-rm {
  background: none;
  border: none;
  color: var(--fg-muted);
  cursor: pointer;
  font-size: 10px;
  opacity: 0.3;
  padding: 0 2px;
  position: relative;
  top: -4px;
}
.cookbook-serve-preset-rm:hover { opacity: 0.8; color: var(--red); }
.cookbook-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
  scrollbar-width: thin;
}
#cookbook-modal .modal-content {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
#cookbook-modal .modal-header {
  flex: 0 0 auto;
}
#cookbook-modal .cookbook-body {
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}
#cookbook-modal .cookbook-group {
  min-height: 0;
}
#cookbook-modal .cookbook-group > .admin-card {
  min-height: 0;
  overflow-y: auto !important;
  overflow-x: hidden !important;
}
#cookbook-modal .cookbook-section-body {
  min-height: 0;
}
.cookbook-body::-webkit-scrollbar {
  width: 4px;
}
.cookbook-body::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--fg) 15%, transparent);
  border-radius: 4px;
}
.cookbook-body::-webkit-scrollbar-track {
  background: transparent;
  padding: 4px 0;
}
.cookbook-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
  min-height: 0;
}

/* Cards — match admin-card */
.cookbook-card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.cookbook-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.cookbook-card-header-actions { display: flex; gap: 4px; }
.cookbook-card-title { font-size: 13px; font-weight: 600; }
.cookbook-card-desc {
  font-size: 11px;
  color: color-mix(in srgb, var(--fg) 50%, transparent);
}

/* Fields grid */
.cookbook-fields {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 6px;
  margin-top: 4px;
}
.cookbook-field-label {
  display: flex;
  flex-direction: column;
  font-size: 11px;
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  gap: 3px;
}

/* Inputs — match admin-add-form input */
.cookbook-field-input {
  padding: 5px 8px;
  font-size: 12px;
  font-family: inherit;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 6px;
  outline: none;
}
.cookbook-field-input:focus { border-color: var(--red); }
#hwfit-dl-server, #hwfit-usecase, #hwfit-server-select, #hwfit-cache-server, #serve-sort { height: 28px; width: 88px; }
/* Serve tab — match Documents sizing, but leave room for the active "Cancel"
   label and center it vertically so the descenders don't clip. */
#hwfit-cache-select {
  min-width: 58px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

/* Command preview */
.cookbook-cmd-preview {
  margin: 4px 0 0;
  padding: 8px 10px;
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: 'Fira Code', monospace;
  font-size: 11px;
  white-space: pre-wrap;
  word-break: break-all;
  color: var(--fg);
}

/* Buttons — match admin-btn-sm */
.cookbook-actions { display: flex; gap: 6px; margin-top: 2px; }
.cookbook-btn {
  padding: 4px 10px;
  min-width: 54px;
  text-align: center;
  font-size: 11px;
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
  cursor: pointer;
  transition: all 0.15s;
}
.cookbook-btn:hover {
  background: var(--border);
  border-color: var(--accent);
}
.cookbook-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.cookbook-run-btn {
  background: var(--accent);
  color: var(--panel);
  border-color: var(--accent);
  font-weight: 600;
}
.cookbook-run-btn:hover { opacity: 0.85; }
.cookbook-stop-btn {
  background: var(--color-error);
  border-color: var(--color-error);
}

/* Output */
.cookbook-output-wrap {
  position: relative;
  margin: 0;
}
.cookbook-output-kill {
  position: absolute;
  top: 4px;
  right: 34px;
  width: 22px;
  height: 22px;
  padding: 0;
  background: none;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--fg-muted);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity .15s, color .15s;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cookbook-output-kill:hover { color: var(--color-error, var(--warn)); border-color: var(--border); }
.cookbook-output-wrap:hover .cookbook-output-kill { opacity: 0.7; }
.cookbook-output-wrap .copy-code {
  position: absolute;
  top: 6px;
  right: 6px;
}
.cookbook-output-wrap:hover .copy-code { opacity: 0.7; }
.cookbook-output-pre {
  margin: 0;
  padding: 8px 10px;
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: 'Fira Code', monospace;
  font-size: 11px;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 180px;
  overflow-y: auto;
}
.cookbook-output-error { color: var(--color-error); }

/* Downloaded/cached models */
.hwfit-cached-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  margin: 2px 0;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  cursor: default;
  font-size: 12px;
}
.hwfit-cached-item .hwfit-serve-panel {
  flex-basis: 100%;
  margin-top: 4px;
}
.hwfit-cached-item:hover {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
}
.hwfit-cached-header {
  font-size: 10px;
  opacity: 0.4;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border: none !important;
  background: none !important;
  padding: 2px 8px;
  cursor: default !important;
}
.hwfit-cached-header:hover { background: none !important; }
.hwfit-cached-name {
  font-weight: 600;
  flex-shrink: 0;
}
.hwfit-cached-repo {
  flex: 1;
  color: var(--fg-muted);
  font-size: 11px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.hwfit-cached-size,
.hwfit-cached-files {
  font-size: 10px;
  color: var(--fg-muted);
  white-space: nowrap;
}
.hwfit-cached-serve,
.hwfit-cached-delete {
  flex-shrink: 0;
}
.hwfit-cached-delete {
  opacity: 0.4;
  font-size: 10px;
}
.hwfit-cached-delete:hover {
  opacity: 1;
  color: var(--color-error, var(--warn));
}
.hwfit-cached-badge {
  font-size: 9px;
  padding: 1px 6px;
  border-radius: 3px;
  font-weight: 600;
  white-space: nowrap;
}
.hwfit-cached-ready {
  background: color-mix(in srgb, var(--color-success, #4caf50) 20%, transparent);
  color: var(--color-success, #4caf50);
}
.hwfit-cached-dl {
  background: color-mix(in srgb, var(--color-warning, #f0ad4e) 20%, transparent);
  color: var(--color-warning, #f0ad4e);
}

/* Cookbook notification dot */
#tool-cookbook-btn {
  position: relative;
}
#rail-cookbook {
  position: relative;
}
.cookbook-open-loading {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  margin-left: 2px;
  color: var(--accent, var(--red));
  opacity: 0.9;
}
.cookbook-open-loading .ai-spinner,
.cookbook-open-loading .ai-spinner-whirlpool {
  display: inline-flex !important;
  width: 14px;
  height: 14px;
  margin: 0 !important;
  gap: 0 !important;
}
.cookbook-open-loading canvas {
  display: block;
}
#rail-cookbook .cookbook-open-loading {
  position: absolute;
  right: 2px;
  top: -2px;
  width: 12px;
  height: 12px;
  margin-left: 0;
}
/* The serve-list 'downloading' / 'download stalled' status text sits a hair
   low against the rest of the meta line on mobile — nudge it up 2px. */
@media (max-width: 768px) {
  .cookbook-dl-status {
    position: relative;
    top: -2px;
  }
}
.cookbook-notif-dot {
  width: 6px;
  height: 6px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--color-success, #4caf50);
  /* Drives the breathing glow in the keyframes (matches email's dot). */
  --notif-glow: var(--color-success, #4caf50);
  animation: cookbook-notif-pulse 2s ease-in-out infinite;
}
.cookbook-notif-dot.cookbook-notif-error {
  background: var(--color-error, #f44);
  --notif-glow: var(--color-error, #f44);
  position: relative;
  left: -2px;
  top: -1px;
}
.cookbook-tab-error-dot { --notif-glow: var(--color-error, #f44); }
.rail-notify-error { color: var(--color-error, #f44) !important; }
.cookbook-tab-error-dot {
  display: inline-block; width: 5px; height: 5px; border-radius: 50%;
  background: var(--color-error, #f44); margin-left: 4px; vertical-align: middle;
  animation: cookbook-notif-pulse 2s ease-in-out infinite;
}
@keyframes cookbook-notif-pulse {
  0%, 100% {
    opacity: 1;
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--notif-glow, var(--accent, var(--red))) 60%, transparent);
  }
  50% {
    opacity: 0.85;
    box-shadow: 0 0 6px 2px color-mix(in srgb, var(--notif-glow, var(--accent, var(--red))) 55%, transparent);
  }
}
.cookbook-clear-btn {
  font-size: 10px !important;
  padding: 0 8px !important;
  height: 22px !important;
  border-radius: 6px !important;
  border: 1px solid var(--border) !important;
  color: color-mix(in srgb, var(--fg) 40%, transparent) !important;
  opacity: 1 !important;
  background: none !important;
}
.cookbook-clear-btn:hover {
  color: var(--red) !important;
  border-color: var(--red) !important;
  background: color-mix(in srgb, var(--red) 8%, transparent) !important;
}
.cookbook-bulk-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  margin-bottom: 4px;
  background: color-mix(in srgb, var(--red) 6%, transparent);
  border: 1px solid color-mix(in srgb, var(--red) 20%, transparent);
  border-radius: 6px;
  font-size: 11px;
}
.cookbook-bulk-bar.hidden { display: none; }
.serve-select-cb {
  width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; cursor: pointer;
  background: var(--border); transition: background 0.15s;
  align-self: center;
  position: relative;
  top: -2px;
}
#serve-bulk-bar {
  position: relative;
  top: -6px;
}
#serve-bulk-bar #serve-bulk-cancel {
  top: 0 !important;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 24px;
  line-height: 1;
}
#serve-bulk-bar #serve-bulk-cancel svg {
  top: 0;
}
.serve-select-cb.selected { background: var(--red); }
.serve-select-cb:hover { background: color-mix(in srgb, var(--red) 50%, transparent); }
#hwfit-cache-select.active {
  background: var(--red);
  color: #fff;
  border-color: var(--red);
}
.cookbook-serve-dirs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: center;
}
.cookbook-serve-dir-pill {
  font-size: 10px;
  font-family: 'Fira Code', monospace;
  padding: 2px 8px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border: 1px solid color-mix(in srgb, var(--fg) 10%, transparent);
  color: var(--fg-muted);
  letter-spacing: 0.2px;
}
/* "running" pill on a Serve-tab card when the model has a live serve task. */
.cookbook-serve-running-pill {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 10px;
  font-size: 9px;
  font-weight: 500;
  text-transform: lowercase;
  letter-spacing: 0.3px;
  vertical-align: 2px;
  position: relative;
  top: -1px;
  color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
}
.cookbook-serve-dir-edit {
  font-size: 9px;
  color: var(--fg-muted);
  opacity: 0.4;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.cookbook-serve-dir-edit:hover {
  opacity: 0.7;
  color: var(--accent, var(--red));
}
.cookbook-gpu-group {
  display: flex;
  gap: 2px;
  margin-top: -4px;
}
.cookbook-gpu-btn {
  width: 22px; height: 22px;
  font-size: 10px;
  font-family: 'Fira Code', monospace;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: none;
  color: var(--fg-muted);
  cursor: pointer;
  opacity: 0.4;
  transition: all 0.15s;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cookbook-gpu-btn:hover {
  opacity: 0.7;
  border-color: var(--fg-muted);
}
.cookbook-gpu-btn.active {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  border-color: color-mix(in srgb, var(--fg) 55%, transparent);
  color: var(--fg);
  font-weight: 600;
}
/* Probe annotation states (set by "Free?" button) */
.cookbook-gpu-btn.gpu-free {
  opacity: 1;
  border-color: #4ade80;
  color: #4ade80;
}
.cookbook-gpu-btn.gpu-busy {
  opacity: 0.85;
  border-color: color-mix(in srgb, var(--red) 70%, transparent);
  color: var(--red);
  background: color-mix(in srgb, var(--red) 8%, transparent);
}
.cookbook-gpu-btn.gpu-missing {
  opacity: 0.2;
  text-decoration: line-through;
  cursor: not-allowed;
}
/* Keep both GPU action labels on one line — they wrapped to two rows on
   mobile, which looked broken. */
.cookbook-gpu-probe, .cookbook-gpu-clear { white-space: nowrap; }
/* "Clear Server" lives in the actions row next to "Probe GPUs".
   Inherits .cookbook-btn sizing; this rule just adds the destructive
   red tint on hover so the "this kills processes" cue stays. */
.cookbook-gpu-clear:hover:not(:disabled) {
  color: var(--red);
  border-color: color-mix(in srgb, var(--red) 60%, var(--border));
  background: color-mix(in srgb, var(--red) 8%, transparent);
}
.cookbook-gpu-clear:disabled { opacity: 0.4; cursor: wait; }
.cookbook-gpu-clear:disabled { opacity: 0.4; cursor: wait; }
/* GPU probe popup — per-GPU process list with kill buttons */
.cookbook-gpu-popup {
  /* Fixed positioning (relative to viewport) so we never get pulled into
     a scrolling/transform stacking context from an ancestor. Z-index has
     to clear the cookbook modal (260) and the rest of the high-z UI
     layers (themed-confirm and various overlays sit around 9000-10000). */
  position: fixed;
  z-index: 10010;
  min-width: 280px;
  max-width: 420px;
  background: var(--panel, #1a1a1a);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.4);
  font-family: 'Fira Code', monospace;
  font-size: 11px;
  color: var(--fg);
}
.cookbook-gpu-popup-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  font-weight: 600;
}
.cookbook-gpu-popup-stats {
  flex: 1;
  font-weight: 400;
  color: var(--fg-muted);
  font-size: 10px;
}
.cookbook-gpu-popup-close {
  background: none;
  border: none;
  color: var(--fg-muted);
  cursor: pointer;
  font-size: 14px;
  padding: 0 4px;
}
.cookbook-gpu-popup-close:hover { color: var(--fg); }
.cookbook-gpu-popup-body {
  padding: 4px 0;
  max-height: 320px;
  overflow-y: auto;
}
.cookbook-gpu-popup-empty {
  padding: 10px;
  color: var(--fg-muted);
  font-style: italic;
}
.cookbook-gpu-proc {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
}
.cookbook-gpu-proc:hover { background: color-mix(in srgb, var(--fg) 5%, transparent); }
.cookbook-gpu-proc-info {
  flex: 1;
  display: flex;
  gap: 8px;
  align-items: center;
  min-width: 0;
}
.cookbook-gpu-proc-pid {
  color: var(--fg-muted);
  width: 56px;
  flex-shrink: 0;
}
.cookbook-gpu-proc-name {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.cookbook-gpu-proc-mem {
  color: var(--fg-muted);
  flex-shrink: 0;
}
.cookbook-gpu-proc-actions {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
}
.cookbook-gpu-kill {
  border: 1px solid color-mix(in srgb, var(--red) 60%, transparent);
  background: none;
  color: var(--red);
  border-radius: 3px;
  padding: 2px 8px;
  cursor: pointer;
  font-family: inherit;
  font-size: 10px;
}
.cookbook-gpu-kill[data-sig="KILL"] {
  background: color-mix(in srgb, var(--red) 12%, transparent);
}
.cookbook-gpu-kill:hover:not(:disabled) {
  background: color-mix(in srgb, var(--red) 20%, transparent);
}
.cookbook-gpu-kill:disabled { opacity: 0.4; cursor: wait; }
.cookbook-hf-link {
  font-size: 9px;
  text-decoration: none;
  color: var(--fg-muted);
  opacity: 0.5;
  padding: 1px 5px;
  border: 1px solid color-mix(in srgb, var(--fg) 12%, transparent);
  border-radius: 3px;
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  vertical-align: 1px;
  letter-spacing: 0.3px;
  font-weight: 600;
}
.cookbook-hf-link:hover {
  opacity: 0.8;
  border-color: var(--accent, var(--red));
  color: var(--accent, var(--red));
}

/* Running tab sections */
.cookbook-saved-section,
.cookbook-serve-section,
.cookbook-dl-section {
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 6px;
  overflow: hidden;
}
.cookbook-section-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 10px;
  cursor: pointer;
  user-select: none;
  /* Persistent surface + border so it reads as a clickable bar instead of
     blending into the panel background. */
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  border: 1px solid var(--border);
  border-radius: 6px;
  transition: background 0.15s, border-color 0.15s;
}
.cookbook-section-header:hover {
  background: color-mix(in srgb, var(--fg) 9%, transparent);
  border-color: color-mix(in srgb, var(--fg) 22%, transparent);
}
.cookbook-section-header .cookbook-section-title {
  flex: 1;
}
.cookbook-section-header .cookbook-clear-btn {
  margin-left: 0;
  position: relative;
  top: -3px;
}
/* "Stop all" sits just left of "Clear finished"; it carries the auto margin so
   the pair is pushed together to the right of the section title. */
.cookbook-section-header .cookbook-stop-all-btn {
  margin-left: auto;
  margin-right: 6px;
  position: relative;
  top: -3px;
}
.cookbook-stop-all-btn {
  font-size: 10px !important;
  padding: 0 8px !important;
  height: 22px !important;
  border-radius: 6px !important;
  border: 1px solid var(--border) !important;
  color: color-mix(in srgb, var(--fg) 40%, transparent) !important;
  opacity: 1 !important;
  background: none !important;
}
.cookbook-stop-all-btn:hover {
  color: var(--warn, #f0ad4e) !important;
  border-color: var(--warn, #f0ad4e) !important;
  background: color-mix(in srgb, var(--warn, #f0ad4e) 8%, transparent) !important;
}
.cookbook-section-chevron {
  flex-shrink: 0;
  opacity: 0.6;
  transition: transform 0.15s ease, opacity 0.15s ease;
}
.cookbook-section-header:hover .cookbook-section-chevron { opacity: 1; }
.cookbook-section-body {
  padding: 0;
}
.cookbook-saved-toggle-header {
  font-size: 10px;
  font-weight: 600;
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.3px;
  margin-bottom: 4px;
}
.cookbook-saved-toggle:hover { color: var(--fg); }
.cookbook-saved-item {
  margin: 2px 0;
  border-radius: 4px;
  font-size: 11px;
}
.cookbook-saved-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  border-radius: 4px;
  transition: background 0.08s;
}
.cookbook-saved-header:hover {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
}
.cookbook-saved-item .hwfit-serve-panel {
  margin: 4px 0 4px 6px;
}
.cookbook-saved-host {
  font-size: 10px;
  color: var(--fg-muted);
  font-family: 'Fira Code', monospace;
}
.cookbook-saved-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cookbook-saved-launch {
  font-size: 10px;
  padding: 2px 8px;
}
.cookbook-saved-del {
  background: none;
  border: none;
  color: var(--fg-muted);
  opacity: 0.3;
  cursor: pointer;
  font-size: 11px;
  padding: 0 2px;
}
.cookbook-saved-del:hover {
  opacity: 1;
  color: var(--color-error, var(--warn));
}

/* Serve config panel */
.hwfit-serve-panel {
  margin-top: 6px;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  flex: 1;
  display: flex;
  flex-direction: column;
  position: relative;
}
.cookbook-serve-slots {
  display: flex;
  gap: 3px;
  justify-content: flex-end;
  margin-bottom: 4px;
}
.cookbook-slot-btn {
  min-width: 22px; height: 22px;
  padding: 0 6px;
  font-size: 10px; font-weight: 600;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: none;
  white-space: nowrap;
  max-width: 80px;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--fg-muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0.5;
  transition: all 0.15s;
}
.cookbook-slot-btn:hover { opacity: 0.9; border-color: var(--fg-muted); }
/* Saved-configs split button: "Save" + dropdown-arrow joined into one control,
   but each segment keeps its own style — Save uses the filled accent
   "Done"-button look; the arrow stays a subtle outlined menu trigger. */
.cookbook-saved-split { gap: 0; }
.cookbook-saved-save,
.cookbook-saved-arrow { max-width: none; }
.cookbook-saved-save {
  padding: 0 10px;
  gap: 4px;
  background: var(--red);
  color: #fff;
  border-color: var(--red);
  font-weight: 600;
  opacity: 1;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.cookbook-saved-save:hover {
  background: color-mix(in srgb, var(--red) 80%, white);
  border-color: color-mix(in srgb, var(--red) 80%, white);
  opacity: 1;
}
.cookbook-saved-arrow {
  padding: 0 6px;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-left: none;
}
.cookbook-slot-saved { background: color-mix(in srgb, var(--accent) 10%, transparent); border-color: color-mix(in srgb, var(--accent) 30%, transparent); color: var(--accent); }
.cookbook-slot-saved:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
.cookbook-slot-btn.active { opacity: 1; background: var(--accent); color: #fff; border-color: var(--accent);
}
.cookbook-slot-wrap {
  position: relative;
  display: inline-flex;
}
.cookbook-slot-wrap:hover .cookbook-slot-del { opacity: 1; }
.cookbook-slot-del {
  position: absolute;
  top: -5px;
  right: -5px;
  width: 14px;
  height: 14px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--bg, #1a1a1a);
  color: var(--fg-muted);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s, color 0.15s, border-color 0.15s;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cookbook-slot-del:hover {
  color: #f44;
  border-color: #f44;
}
.cookbook-card-backend { display: none; }
/* Dependencies row — aligned tags */
.cookbook-dep-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel);
}
/* Let the deps list fill the available window height instead of being
   capped at the default .doclib-grid 400-px box. */
[data-backend-group="Dependencies"] #cookbook-deps-list {
  flex: 1;
  max-height: none;
}

/* Settings tab: stack the HF token + Servers cards as distinct blocks
   (matches the Download tab's block-per-section layout). */
.cookbook-settings-stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
  /* Scroll the whole settings panel so the Servers card can grow to hold every
     server (it used to be a cramped internal scroll box that clipped them). */
  overflow-y: auto;
  min-height: 0;
}
.cookbook-settings-stack.hidden { display: none; }
.cookbook-dep-row.cookbook-dep-blocked { opacity: 0.4; }
.cookbook-dep-info { flex: 1; min-width: 0; }
.cookbook-dep-section {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 12px 2px 4px;
}
.cookbook-dep-section-title {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
}
.cookbook-dep-section-note {
  font-size: 10px;
  color: color-mix(in srgb, var(--fg) 50%, transparent);
}
.cookbook-dep-tag {
  font-size: 9px;
  padding: 0 8px;
  border-radius: 4px;
  white-space: nowrap;
  text-align: center;
  min-width: 62px;
  box-sizing: border-box;
  line-height: 1;
  /* Explicit height so the <button> tags (Install / Installed) are the EXACT
     same height as the sibling <span> tags — Firefox gives buttons a taller
     native box otherwise. Mirrors the server-row tag rule (height:24px). */
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.cookbook-dep-target {
  border: 1px solid var(--border);
  color: color-mix(in srgb, var(--fg) 50%, transparent);
}
.cookbook-dep-cat {
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  color: color-mix(in srgb, var(--fg) 60%, transparent);
}
.cookbook-dep-installed {
  background: color-mix(in srgb, var(--green, #50fa7b) 18%, transparent);
  color: var(--green, #50fa7b);
  border: 1px solid color-mix(in srgb, var(--green, #50fa7b) 35%, transparent);
}
.cookbook-dep-na {
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  border: 1px solid color-mix(in srgb, var(--fg) 16%, transparent);
  cursor: help;
}
.cookbook-dep-install {
  background: var(--accent, var(--red));
  color: #fff;
  border: none;
  cursor: pointer;
  font-family: inherit;
  font-weight: 500;
  position: relative;
  top: -3px;
  /* Strip the native button box so it's the same height as the sibling tags
     (Firefox renders <button> taller otherwise); height comes from .cookbook-dep-tag. */
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
}
.cookbook-dep-install:hover { opacity: 0.85; }
/* Installed split button: "Installed" label + separator + ▾ caret; clicking it
   opens the actions menu (Update). Replaces the old ⋮ button. */
.cookbook-dep-installed-btn {
  padding: 0;
  cursor: pointer;
  font-family: inherit;
  overflow: hidden;
  position: relative;
  top: -3px;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
}
.cookbook-dep-installed-btn .cookbook-dep-installed-label { padding: 0 8px; }
.cookbook-dep-installed-btn .cookbook-dep-caret {
  display: inline-flex;
  align-items: center;
  align-self: stretch;
  padding: 0 7px;
  font-size: 12px;
  opacity: 0.85;
  border-left: 1px solid color-mix(in srgb, var(--green, #50fa7b) 35%, transparent);
}
.cookbook-dep-installed-btn:hover { filter: brightness(1.15); }
.hwfit-serve-row {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 6px;
  margin-bottom: 6px;
}
.hwfit-serve-row label {
  font-size: 10px;
  color: var(--fg-muted);
  white-space: nowrap;
  letter-spacing: 0.3px;
}
.hwfit-serve-row label select,
.hwfit-serve-row label input {
  display: block;
  margin-top: 2px;
}
.hwfit-sf {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg);
  font-family: inherit;
  font-size: 12px;
  padding: 0 6px;
  height: 28px;
}
.hwfit-sf[data-field="backend"],
.hwfit-sf[data-field="dtype"],
.hwfit-sf[data-field="tp"] {
  height: 32px;
  box-sizing: border-box;
  width: 100%;
}
.hwfit-sf:focus {
  border-color: var(--accent, var(--red));
  outline: none;
}
.hwfit-sf[type="text"] { width: 100%; }
.hwfit-sf.hwfit-sf-wide { width: 100%; }
.hwfit-sf.hwfit-sf-full { grid-column: 1 / -1; }
.hwfit-serve-checks {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 6px;
}
.hwfit-sf-cb {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--fg-muted);
  cursor: pointer;
  padding: 3px 0;
}
.hwfit-sf-cb:hover { color: var(--fg); }
/* Speculative method + tokens controls — render inline beside the checkbox */
.hwfit-spec-group .hwfit-spec-method,
.hwfit-spec-group .hwfit-spec-tokens {
  height: 20px;
  padding: 0 4px;
  font-size: 11px;
  /* Match the generic .hwfit-sf serve controls: inherit font + 4px radius
     so the Speculative method/token widgets read as the same control
     family as the rest of the panel (they were 'Fira Code'/3px before). */
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--fg);
}
.hwfit-spec-group .hwfit-spec-method { min-width: 110px; }
.hwfit-spec-group .hwfit-spec-tokens { width: 44px; text-align: center; }
/* Themed step buttons replacing the native number-input spinner. */
.hwfit-numstep {
  display: inline-flex;
  align-items: stretch;
  height: 20px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  overflow: hidden;
}
.hwfit-numstep:focus-within { border-color: var(--accent, var(--red)); }
.hwfit-numstep .hwfit-spec-tokens {
  border: none !important;
  border-radius: 0 !important;
  width: 38px;
  height: 100%;
  background: transparent;
  -moz-appearance: textfield;
  appearance: textfield;
  margin: 0;
}
.hwfit-numstep .hwfit-spec-tokens::-webkit-outer-spin-button,
.hwfit-numstep .hwfit-spec-tokens::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.hwfit-numstep-btn {
  width: 16px;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  border: none;
  color: var(--accent, var(--red));
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  opacity: 1;
  transition: background 0.12s, opacity 0.12s, color 0.12s;
}
.hwfit-numstep-btn:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  color: var(--accent, var(--red));
}
.hwfit-numstep-btn:active {
  background: color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
}
.hwfit-numstep-btn:focus { outline: none; }
.hwfit-numstep-btn[data-step="-1"] { border-right: 1px solid var(--border); }
.hwfit-numstep-btn[data-step="1"]  { border-left:  1px solid var(--border); }
.hwfit-spec-group:has(input[type="checkbox"]:not(:checked)) .hwfit-spec-method,
.hwfit-spec-group:has(input[type="checkbox"]:not(:checked)) .hwfit-spec-tokens,
.hwfit-spec-group:has(input[type="checkbox"]:not(:checked)) .hwfit-numstep {
  opacity: 0.45;
  pointer-events: none;
}
/* Custom toggle switch */
.hwfit-sf-cb input[type="checkbox"] {
  -webkit-appearance: none;
  appearance: none;
  width: 28px;
  height: 14px;
  border-radius: 7px;
  background: color-mix(in srgb, var(--fg) 15%, transparent);
  position: relative;
  cursor: pointer;
  margin: 0;
  flex-shrink: 0;
  transition: background 0.2s;
}
.hwfit-sf-cb input[type="checkbox"]::after {
  content: '';
  position: absolute;
  top: 1.5px;
  left: 2px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #000;
  transition: transform 0.2s, background 0.2s;
}
.hwfit-sf-cb input[type="checkbox"]:checked {
  background: var(--accent, var(--red));
}
.hwfit-sf-cb input[type="checkbox"]:checked::after {
  transform: translateX(14px);
  background: #fff;
}
.hwfit-serve-extra {
  margin-bottom: 6px;
}
.hwfit-serve-extra label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 10px;
  color: var(--fg-muted);
  letter-spacing: 0.3px;
}
.hwfit-serve-extra .hwfit-sf {
  width: 100%;
}
.hwfit-serve-cmd {
  margin: 6px 0;
  padding: 8px 10px;
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  border: 1px solid var(--border);
  border-radius: 4px;
  font-family: 'Berkeley Mono', 'SF Mono', 'Fira Code', monospace;
  font-size: 10px;
  white-space: pre-wrap;
  word-break: break-all;
  width: 100%;
  box-sizing: border-box;
  resize: none;
  color: var(--fg);
  line-height: 1.5;
  min-height: 36px;
  overflow: hidden;
}
.hwfit-serve-actions {
  display: flex;
  gap: 6px;
  margin-top: 4px;
  align-items: center;
}
.hwfit-serve-actions .cookbook-btn {
  padding: 5px 14px;
  font-size: 11px;
}
.hwfit-serve-actions-spacer { flex: 1 1 auto; }
.hwfit-serve-launch {
  background: var(--accent-primary, var(--red));
  color: #fff;
  border: 1px solid var(--accent-primary, var(--red));
  border-radius: 4px;
  font-weight: 700;
}
.hwfit-serve-launch:hover {
  opacity: 0.9;
}

/* Task header ⋮ menu button */
.cookbook-task-save-btn {
  background: none;
  border: none;
  color: var(--fg-muted);
  cursor: pointer;
  opacity: 0.3;
  padding: 2px;
  flex-shrink: 0;
  transition: opacity 0.15s;
  position: relative;
  top: -4px;
  transform: scale(1.15);
}
.cookbook-task-save-btn:hover { opacity: 0.8; }
.cookbook-task-edit-btn {
  background: none;
  border: none;
  color: var(--fg-muted);
  cursor: pointer;
  opacity: 0.3;
  padding: 2px;
  flex-shrink: 0;
  transition: opacity 0.15s;
  position: relative;
  top: -4px;
  transform: scale(1.15);
}
.cookbook-task-edit-btn:hover { opacity: 0.8; }
.cookbook-task-menu-btn {
  background: none;
  border: 1px solid transparent;
  color: var(--fg-muted);
  font-size: 16px;
  width: 22px;
  height: 22px;
  cursor: pointer;
  opacity: 0;
  border-radius: 4px;
  position: relative;
  top: -3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.15s;
  flex-shrink: 0;
}
.cookbook-task:hover .cookbook-task-menu-btn { opacity: 0.6; }
.cookbook-task-menu-btn:hover {
  opacity: 1 !important;
  background: color-mix(in srgb, var(--fg) 7%, transparent);
  border-color: var(--border);
  color: var(--fg);
}
@media (max-width: 768px) {
  .cookbook-task .cookbook-task-menu-btn {
    opacity: 0.72;
    width: 32px;
    height: 32px;
    min-width: 32px;
    top: -5px;
  }
  .cookbook-task .cookbook-task-menu-btn:active {
    opacity: 1;
    background: color-mix(in srgb, var(--fg) 9%, transparent);
    border-color: var(--border);
  }
}
/* Same z-index treatment as .cookbook-task-dropdown — cookbook modal's
   auto-stack climbs past low values; popups append to body and need to
   sit above the modal regardless. */
.hwfit-cached-dropdown,
.cookbook-gpu-split-menu,
.cookbook-saved-menu,
.cookbook-dep-menu {
  z-index: 10000;
}

/* Launch-command textarea wrapper — Copy pill floats at the top-right
   corner of the field (chat run-output pattern). */
.hwfit-serve-cmd-wrap {
  position: relative;
}
.hwfit-serve-cmd-wrap .hwfit-serve-cmd {
  /* Just enough breathing room so a cursor at line-end doesn't actually
     touch the Copy icon — text otherwise uses the full width of the box. */
  padding-right: 32px;
}
.hwfit-serve-copy-inline {
  position: absolute;
  top: 4px; right: 4px;
  z-index: 2;
  width: 26px !important;
  height: 26px !important;
  min-width: 26px !important;
  padding: 0 !important;
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  opacity: 0.7;
  transition: opacity 0.12s, color 0.12s;
}
.hwfit-serve-copy-inline:hover { opacity: 1; color: var(--accent-primary, var(--red)); }
.hwfit-serve-copy-inline.copied { opacity: 1; color: var(--color-save-green, #4caf50); }
.hwfit-serve-copy-inline svg { display: block; }

/* Split button: Clear Server (main) + ^ arrow (more actions). The two halves
   are visually joined — left button square on its right edge, arrow square
   on its left edge — so they read as one widget. */
.cookbook-gpu-split { display: inline-flex; flex-shrink: 0; }
.cookbook-gpu-split .cookbook-gpu-split-main {
  border-top-right-radius: 0 !important;
  border-bottom-right-radius: 0 !important;
}
.cookbook-gpu-split .cookbook-gpu-split-arrow {
  border-top-left-radius: 0 !important;
  border-bottom-left-radius: 0 !important;
  border-left-width: 0 !important;
  padding: 0 6px !important;
  min-width: 24px;
}
.cookbook-gpu-split .cookbook-gpu-split-arrow svg { display: block; }

.cookbook-task-dropdown {
  /* Must sit above the cookbook modal (whose auto-stack z-index starts at
     300 and climbs whenever the modal is brought to the front). 1000 was
     not enough — the dropdown rendered behind the modal on mobile. */
  z-index: 10000;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.3);
  min-width: auto;
  width: max-content;
}
.cookbook-task-dropdown .dropdown-item-compact {
  padding: 6px 10px;
  font-size: 12px;
  cursor: pointer;
  border-radius: 4px;
  white-space: nowrap;
}
.cookbook-task-dropdown .dropdown-item-compact:hover {
  background: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent);
}
.cookbook-dropdown-danger:hover {
  color: var(--red) !important;
}
.cookbook-task-retry:hover { color: var(--color-warning, #f0ad4e); }
.cookbook-task-kill:hover { color: var(--color-error, var(--warn)); }

/* Serve "Edit command" modal */
.cookbook-edit-overlay {
  position: fixed; inset: 0; z-index: 10000;
  background: rgba(0,0,0,0.5);
  display: flex; align-items: center; justify-content: center;
}
.cookbook-edit-modal {
  width: min(720px, 92vw);
  background: var(--panel, var(--bg));
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.35);
  display: flex; flex-direction: column; gap: 10px;
}
.cookbook-edit-title {
  font-size: 13px; font-weight: 600; color: var(--fg);
}
.cookbook-edit-textarea {
  width: 100%;
  min-height: 140px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg);
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 12px;
  padding: 8px;
  outline: none;
  resize: vertical;
  white-space: pre;
  overflow: auto;
}
.cookbook-edit-textarea:focus { border-color: var(--accent, var(--fg)); }
.cookbook-edit-actions {
  display: flex; gap: 8px; justify-content: flex-end;
}
.cookbook-edit-save {
  background: color-mix(in srgb, var(--accent) 20%, transparent) !important;
  border-color: var(--accent) !important;
}

/* Running tasks */
/* Each running task is a real card with a left accent stripe coloured by
   status, instead of an invisible divider against bg. */
.cookbook-task {
  margin: 0 0 8px;
  border: 1px solid var(--border);
  border-left: 3px solid color-mix(in srgb, var(--fg) 30%, transparent);
  border-radius: 8px;
  overflow: hidden;
  max-width: 100%;
  min-width: 0;
  background: color-mix(in srgb, var(--fg) 3%, var(--bg));
  transition: border-color 0.15s, background 0.15s;
}
.cookbook-task:hover {
  border-color: color-mix(in srgb, var(--fg) 25%, var(--border));
  background: color-mix(in srgb, var(--fg) 5%, var(--bg));
}
/* Status-driven left stripe via :has() — graceful fallback to neutral. */
.cookbook-task:has(.cookbook-task-running) { border-left-color: var(--green, #50fa7b); }
.cookbook-task:has(.cookbook-task-done)    { border-left-color: var(--green, #50fa7b); }
.cookbook-task:has(.cookbook-task-error)   { border-left-color: var(--color-error, var(--warn, #f87171)); }
.cookbook-task:has(.cookbook-task-queued)  { border-left-color: var(--color-warning, #f0ad4e); }

/* Serve crashed / unreachable — full red frame so a dead server in the
   Running tab is obvious at a glance (mirrors the endpoint health dot). */
.cookbook-task.cookbook-task-unreachable,
.cookbook-task[data-type="serve"][data-status="error"],
.cookbook-task[data-type="serve"][data-status="crashed"] {
  border-color: var(--color-error, #f44);
  border-left-color: var(--color-error, #f44);
  background: color-mix(in srgb, var(--color-error, #f44) 8%, var(--bg));
}

.cookbook-task-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  background: color-mix(in srgb, var(--fg) 7%, transparent);
  font-size: 12px;
  border-bottom: 1px solid color-mix(in srgb, var(--fg) 6%, transparent);
}
.cookbook-task-type {
  text-transform: uppercase;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.5px;
  padding: 1px 5px;
  border-radius: 3px;
  flex-shrink: 0;
  width: 67.3px;
  text-align: center;
}
.cookbook-task-type[data-type="serve"] {
  background: color-mix(in srgb, var(--green, #50fa7b) 16%, transparent);
  color: var(--green, #50fa7b);
}
.cookbook-dl-add-server {
  position: relative;
  top: -3px;
}
.cookbook-task-type[data-type="download"] {
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  color: var(--fg-muted);
}
/* Finished state — overrides the per-type colors so a completed download or
   serve task shows the same green FINISHED chip. */
.cookbook-task-type.cookbook-task-type-done {
  background: color-mix(in srgb, var(--green, #50fa7b) 16%, transparent);
  color: var(--green, #50fa7b);
}
.cookbook-task-backend {
  width: 67.3px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  opacity: 0.5;
}
.cookbook-task-name {
  flex: 1;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cookbook-task-wave {
  font-size: 11px;
  letter-spacing: -1px;
  color: #fff;
  /* Gentle pulse on top of the cycling bars so a running task reads as clearly
     alive — especially when the card is collapsed and the log isn't visible. */
  animation: cookbook-wave-pulse 1.3s ease-in-out infinite;
}
@keyframes cookbook-wave-pulse {
  0%, 100% { opacity: 0.45; }
  50%      { opacity: 1; }
}
.cookbook-task-indicator {
  min-width: 24px;
  text-align: center;
  flex-shrink: 0;
  display: inline-flex;
  justify-content: center;
  align-items: center;
  /* The done/clear pill inside is wider than 24px and would otherwise
     overflow into the edit/save buttons that sit immediately before it on
     a serve task. Push it right so it stops crashing into them on desktop. */
  margin-left: 8px;
}
.cookbook-task-check {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  position: relative;
  top: 0;
  cursor: pointer;
  padding: 1px 6px 1px 4px;
  border-radius: 9px;
  color: var(--red, #ff5555);
  transition: background 0.15s;
}
.cookbook-task-check svg { flex-shrink: 0; }
.cookbook-task-check:hover { background: color-mix(in srgb, var(--red, #ff5555) 18%, transparent); }
/* Terminal task clear pill. */
.cookbook-task-done-label,
.cookbook-task-clear-label {
  font-size: 9px;
  line-height: 1;
  text-transform: lowercase;
}
.cookbook-task-done-label { color: var(--red, #ff5555); }
.cookbook-task-clear-label { display: none; }
.cookbook-task-check-ico { display: none; }
.cookbook-task-clear-ico { display: inline; }
.cookbook-task[data-status="done"] .cookbook-task-check {
  color: var(--green, #50fa7b);
}
.cookbook-task[data-status="done"] .cookbook-task-check:hover {
  background: color-mix(in srgb, var(--green, #50fa7b) 16%, transparent);
}
.cookbook-task[data-status="done"] .cookbook-task-done-label {
  color: var(--green, #50fa7b);
}
.cookbook-task[data-status="done"] .cookbook-task-check-ico { display: inline; }
.cookbook-task[data-status="done"] .cookbook-task-clear-ico { display: none; }
.cookbook-task-start-now {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  position: relative;
  top: -4px;
  cursor: pointer;
  padding: 1px 6px 1px 4px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--fg);
  font-family: inherit;
  font-size: 9px;
  line-height: 1;
  text-transform: lowercase;
  white-space: nowrap;
  transition: background 0.15s;
}
.cookbook-task-start-now svg {
  flex-shrink: 0;
  position: relative;
  top: 0;
}
.cookbook-task-start-now:hover {
  background: color-mix(in srgb, var(--fg) 12%, transparent);
}
/* "Serve" button on a finished download — green pill matching the "running" /
   finished badge (it sits next to the green FINISHED chip + check). */
.cookbook-task-serve-btn {
  font-size: 9px;
  font-weight: 600;
  padding: 1px 6px;
  border: none;
  border-radius: 3px;
  line-height: 16px;
  flex-shrink: 0;
  cursor: pointer;
  font-family: inherit;
  background: color-mix(in srgb, var(--green, #50fa7b) 20%, transparent);
  color: var(--green, #50fa7b);
  position: relative;
  top: -2px;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
}
.cookbook-task-serve-btn:hover { background: color-mix(in srgb, var(--green, #50fa7b) 32%, transparent); }
.cookbook-task-sub {
  padding: 1px 10px 4px;
  line-height: 1;
  display: flex;
  align-items: center;
  gap: 10px;
}
.cookbook-task-session {
  font-size: 9px;
  color: var(--fg-muted);
  font-family: 'Fira Code', monospace;
  opacity: 0.35;
  letter-spacing: 0.3px;
}
.cookbook-task-server {
  font-size: 9px;
  color: var(--fg-muted);
  font-family: 'Fira Code', monospace;
  opacity: 0.4;
}
.cookbook-task-uptime {
  font-size: 9px;
  color: var(--fg-muted);
  font-family: 'Fira Code', monospace;
  opacity: 0.4;
}
.cookbook-task-status {
  font-size: 9px;
  padding: 1px 6px;
  border-radius: 3px;
  font-weight: 600;
  text-align: left;
  flex-shrink: 0;
  line-height: 16px;
}
.cookbook-task-running { background: color-mix(in srgb, var(--green, #50fa7b) 20%, transparent); color: var(--green, #50fa7b); }
/* Stopping: same pill treatment as "running" but orange. */
.cookbook-task-stopping { background: color-mix(in srgb, var(--orange, #ffb86c) 22%, transparent); color: var(--orange, #ffb86c); }
.cookbook-task-done { background: color-mix(in srgb, var(--green) 15%, transparent); color: var(--green); }
.cookbook-task-error { background: color-mix(in srgb, var(--color-error, var(--warn)) 20%, transparent); color: var(--color-error, var(--warn)); }
/* Crashed: same filled-pill treatment as "running" but red, with a red border. */
.cookbook-task-crashed { background: color-mix(in srgb, var(--red, #ff5555) 16%, transparent); color: var(--red, #ff5555); border: 1px solid var(--red, #ff5555); padding: 0 5px; }
.cookbook-task-stopped { background: color-mix(in srgb, var(--color-warning, #f0ad4e) 22%, transparent); color: var(--color-warning, #f0ad4e); }
.cookbook-task-queued { background: color-mix(in srgb, var(--color-warning, #f0ad4e) 15%, transparent); color: var(--color-warning, #f0ad4e); }
/* Stopped / crashed servers get an orange surround instead of the heavier
   red — communicates "this isn't running, here's the last command you used,
   relaunch when ready" without screaming "error". Color stays normal so the
   command text remains easy to copy. */
.cookbook-task[data-status="stopped"],
.cookbook-task[data-status="error"],
.cookbook-task[data-status="crashed"] {
  border-color: color-mix(in srgb, var(--color-warning, #f0ad4e) 55%, var(--border));
  background: color-mix(in srgb, var(--color-warning, #f0ad4e) 6%, var(--bg));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-warning, #f0ad4e) 18%, transparent);
}
.cookbook-task[data-status="stopped"] .cookbook-output-pre,
.cookbook-task[data-status="error"]   .cookbook-output-pre,
.cookbook-task[data-status="crashed"] .cookbook-output-pre {
  border-top-color: color-mix(in srgb, var(--color-warning, #f0ad4e) 35%, var(--border));
}
.cookbook-task-retry,
.cookbook-task .cookbook-output-wrap { margin: 0; }
.cookbook-task .cookbook-output-pre {
  border-radius: 0;
  border-top: 1px solid color-mix(in srgb, var(--fg) 8%, transparent);
  max-height: 150px;
  max-width: 100%;
  overflow-x: hidden;
  box-sizing: border-box;
  background: color-mix(in srgb, var(--fg) 5%, var(--bg));
}
.cookbook-task .cookbook-output-pre:empty {
  display: none;
}
.cookbook-task-collapsed {
  display: none !important;
}
.cookbook-task-header {
  cursor: pointer;
}

/* Env bar — match admin-card */
.cookbook-env-bar {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
}
.cookbook-env-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.cookbook-env-row > .cookbook-field-label {
  flex: 1 1 140px;
  min-width: 120px;
}
.cookbook-env-row .cookbook-field-input {
  width: 100%;
  box-sizing: border-box;
}
.cookbook-extra-label { grid-column: 1 / -1; }

/* Tabs — match library tabs */
.cookbook-tabs {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: 8px;
}
/* Mobile: every modal tab strip should swipe horizontally instead of wrapping
   or getting cut off. Hide the scrollbar (still pannable via touch / drag). */
@media (max-width: 768px) {
  .cookbook-tabs,
  .memory-tabs,
  .admin-tabs,
  .lib-tabs,
  .gallery-tabs,
  .preset-tabs {
    flex-wrap: nowrap !important;
    overflow-x: auto !important;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }
  .cookbook-tabs::-webkit-scrollbar,
  .memory-tabs::-webkit-scrollbar,
  .admin-tabs::-webkit-scrollbar,
  .lib-tabs::-webkit-scrollbar,
  .gallery-tabs::-webkit-scrollbar,
  .preset-tabs::-webkit-scrollbar {
    display: none;
  }
  .cookbook-tabs > *,
  .memory-tabs > *,
  .admin-tabs > *,
  .lib-tabs > *,
  .gallery-tabs > *,
  .preset-tabs > * {
    flex-shrink: 0;
  }
}
.cookbook-tab {
  /* `background: none` isn't enough on Windows — Chrome/Edge fall back to the
     OS native button background (dark gray / black under dark mode) when
     no explicit color + appearance reset is applied. Force transparent +
     appearance:none so the tab inherits the modal's panel color. */
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  background-color: transparent;
  padding: 6px 14px;
  font-size: 12px;
  font-family: inherit;
  color: var(--fg-muted);
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  transition: color 0.1s, border-color 0.1s;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.cookbook-tab:hover {
  color: var(--fg);
}
.cookbook-tab.active {
  color: var(--accent, var(--red));
  border-bottom-color: var(--accent, var(--red));
}
/* Mobile: hide tab icons so labels don't wrap to a new row */
@media (max-width: 640px) {
  .cookbook-tab svg {
    display: none;
  }
  .cookbook-tab {
    padding: 6px 10px;
    font-size: 13px;
  }
  /* Mobile cookbook text: matched to the calendar/library modals so
     cookbook doesn't read noticeably larger than the rest of the app.
     Inputs stay at 16px specifically — anything smaller triggers iOS
     Safari's auto-zoom on focus, which yanks the layout. */
  .cookbook-body {
    font-size: 13px;
  }
  .cookbook-body h2 { font-size: 14px; }
  .cookbook-body .memory-desc,
  .cookbook-body .doclib-desc { font-size: 12px; }
  .cookbook-body .memory-item-title { font-size: 13px; }
  .cookbook-body .memory-item-meta { font-size: 11px !important; }
  .cookbook-body .hwfit-sf,
  .cookbook-body .cookbook-field-input,
  .cookbook-body .cookbook-dl-repo { font-size: 16px; }
  .cookbook-body .memory-toolbar-btn { font-size: 12px; }
}

/* Mobile cookbook sizing — kept in line with calendar/library modals. */
@media (max-width: 768px) {
  /* The Speculative control (checkbox + method dropdown + token stepper)
     is too wide for a phone — the stepper ran off the right edge of the
     modal. Let the group wrap onto its own line, take full width, and
     shrink the method dropdown so the +/− stepper stays on-screen. */
  .hwfit-spec-group {
    flex-wrap: wrap;
    flex-basis: 100%;
    row-gap: 4px;
  }
  .hwfit-spec-group .hwfit-spec-method { min-width: 0; flex: 1 1 auto; }
  .hwfit-numstep { flex: 0 0 auto; }
  .cookbook-card-title { font-size: 13px; }
  .cookbook-card-desc { font-size: 12px; }
  .cookbook-field-label { font-size: 12px; }
  .cookbook-field-input { font-size: 16px; padding: 7px 10px; }
  /* Dropdown pickers (Dependencies / Download tabs) don't need the 16px
     no-zoom-on-focus trick that text inputs do, and 16px reads oversized next
     to the Serve tab's 11px selects. Shrink just the selects to match. */
  select.cookbook-field-input { font-size: 13px !important; padding: 5px 8px; }
  /* Repo input + model search read oversized at 16px; bring their text and
     placeholder hints down to match the rest of the cookbook. */
  .cookbook-dl-repo, .hwfit-search { font-size: 13px !important; }
  .cookbook-cmd-preview { font-size: 12px; padding: 8px 10px; }
  .cookbook-btn { font-size: 12px; padding: 6px 12px; }
  .cookbook-tab { font-size: 13px; }
  .cookbook-task-header { font-size: 13px; padding: 6px 10px; }
  .cookbook-task-name { font-size: 13px; }
  .cookbook-task-sub { font-size: 11px; }
  .cookbook-task-status {
    font-size: 10px;
    /* Vertical alignment of the phase/loading badge against the task title. */
    position: relative;
    top: 2px;
  }
  /* Keep the ascii wave aligned with the badge (both 2px down). */
  .cookbook-task-wave {
    position: relative;
    top: 2px;
  }
  /* Session id (serve-…) + uptime sub-line down 1px. */
  .cookbook-task-session,
  .cookbook-task-uptime {
    position: relative;
    top: 1px;
  }
  /* Rest of the header row down 2px to match the status badge + wave: the
     model title, the serve/download type tag, and the edit/save/menu icons. */
  .cookbook-task-type,
  .cookbook-task-name,
  .cookbook-task-edit-btn,
  .cookbook-task-save-btn {
    position: relative;
    top: 2px;
  }
  /* The ⋮ menu sits higher than the rest of the row. */
  .cookbook-task-menu-btn {
    position: relative;
    top: -2px;
    /* Mobile has no hover — the base rule's opacity:0 left the ⋮ button
       invisible AND effectively unclickable. Always show + give a proper
       touch target. */
    opacity: 0.7 !important;
    width: 32px !important;
    height: 32px !important;
    font-size: 18px !important;
  }
  /* The copied-confirmation checkmark sits 2px higher than the copy icon. */
  .cookbook-output-copy.copied svg {
    position: relative;
    top: 2px;
  }
  .cookbook-section-title { font-size: 12px; }
  .cookbook-settings-label { font-size: 12px; }
  .cookbook-settings-hint { font-size: 11px; }
  .cookbook-settings-input { font-size: 16px; }
  /* Settings stack is flex:1 + overflow:hidden with no inner scroll, so on a
     short mobile viewport its lower half gets clipped. Let it scroll. */
  .cookbook-settings-stack { overflow-y: auto !important; -webkit-overflow-scrolling: touch; }
  /* The Servers card is flex:1 + its own overflow-y:auto — a nested scroll that
     collapses and crops its list inside the now-scrolling stack. Let both cards
     take natural height and scroll the whole stack as one instead. */
  .cookbook-settings-stack > .admin-card {
    flex: 0 0 auto !important;
    overflow: visible !important;
  }
  .cookbook-dl-repo { font-size: 16px; }
  .cookbook-dl-btn { font-size: 14px; }
  .cookbook-serve-preset-name { font-size: 14px; }
  .cookbook-serve-preset-meta { font-size: 12px; }
  .cookbook-saved-name { font-size: 14px; }
  .cookbook-saved-host { font-size: 12px; }
  .cookbook-slot-btn { font-size: 13px; }
  /* The serve-panel "Save" split button reads larger than the surrounding
     controls (it's also bold) — knock it down so it matches the row. */
  .cookbook-saved-save,
  .cookbook-saved-arrow { font-size: 11px; }
  .cookbook-output-pre { font-size: 12px; }
  .cookbook-dep-tag { font-size: 12px; }
  .cookbook-checkbox-label { font-size: 13px; }
  .cookbook-gpu-btn { font-size: 13px; }
}

/* Slider — range + text value */
.cookbook-slider-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
}
.cookbook-slider {
  flex: 1;
  min-width: 0;
  height: 6px;
  margin: 8px 0;
  padding: 0;
  -webkit-appearance: none;
  appearance: none;
  background: linear-gradient(to right, var(--accent, var(--red)) 0%, var(--accent, var(--red)) 50%, color-mix(in srgb, var(--fg) 12%, transparent) 50%);
  border-radius: 4px;
  outline: none;
  cursor: pointer;
  transition: opacity 0.1s;
}
.cookbook-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--fg);
  border: 2px solid var(--panel);
  box-shadow: 0 1px 4px rgba(0,0,0,0.3);
  cursor: pointer;
  transition: transform 0.1s, box-shadow 0.1s;
}
.cookbook-slider:hover::-webkit-slider-thumb {
  transform: scale(1.15);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent, var(--red)) 20%, transparent);
}
.cookbook-slider:active::-webkit-slider-thumb {
  transform: scale(1.25);
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent, var(--red)) 25%, transparent);
}
.cookbook-slider::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--fg);
  border: 2px solid var(--panel);
  box-shadow: 0 1px 4px rgba(0,0,0,0.3);
  cursor: pointer;
}
.cookbook-slider::-moz-range-progress {
  background: var(--accent, var(--red));
  height: 6px;
  border-radius: 4px;
}
.cookbook-slider::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 4px;
}
.cookbook-slider::-moz-range-track {
  height: 6px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--fg) 12%, transparent);
}
.cookbook-slider-value {
  width: 64px !important;
  flex-shrink: 0;
  text-align: center;
  font-size: 11px;
}

/* Toggle switches — match admin-switch */
.cookbook-checkbox-row {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  padding: 4px 0;
}
.cookbook-checkbox-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  cursor: pointer;
  user-select: none;
}
.cookbook-checkbox-label:hover { color: var(--fg); }
.cookbook-checkbox {
  appearance: none;
  -webkit-appearance: none;
  width: 30px;
  height: 16px;
  background: color-mix(in srgb, var(--fg) 15%, transparent);
  border-radius: 8px;
  position: relative;
  cursor: pointer;
  transition: background 0.08s;
  flex-shrink: 0;
}
.cookbook-checkbox::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--panel);
  box-shadow: 0 1px 2px rgba(0,0,0,0.25);
  transition: transform 0.08s;
}
.cookbook-checkbox:checked {
  background: var(--red);
}
.cookbook-checkbox:checked::after {
  transform: translateX(14px);
}

/* GPU toggle grid */
.cookbook-gpu-row {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 8px;
}
.cookbook-gpu-label {
  font-size: 11px;
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  margin-right: 4px;
  white-space: nowrap;
}
.cookbook-gpu-btn {
  width: 26px;
  height: 26px;
  padding: 0;
  font-size: 11px;
  font-family: inherit;
  font-weight: 600;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: color-mix(in srgb, var(--fg) 40%, transparent);
  cursor: pointer;
  transition: all 0.15s;
}
.cookbook-gpu-btn:hover {
  color: var(--fg);
  border-color: var(--red);
  background: color-mix(in srgb, var(--fg) 4%, transparent);
}
.cookbook-gpu-btn.active {
  background: var(--red);
  color: var(--panel);
  border-color: var(--red);
}

/* Instance / Save buttons */
.cookbook-add-instance-btn,
.cookbook-save-btn {
  background: transparent;
  border: 1px dashed color-mix(in srgb, var(--accent) 50%, transparent);
  color: color-mix(in srgb, var(--fg) 70%, transparent);
  font-size: 11px;
}
.cookbook-add-instance-btn:hover,
.cookbook-save-btn:hover {
  border-color: var(--accent);
  color: var(--fg);
}
.cookbook-remove-instance-btn {
  background: transparent;
  border: none;
  color: color-mix(in srgb, var(--fg) 40%, transparent);
  font-size: 12px;
  cursor: pointer;
  padding: 0 4px;
  line-height: 1;
}
.cookbook-remove-instance-btn:hover { color: var(--red); }

/* Preset chips */
/* Saved card styling */
.cookbook-saved-card .cookbook-card-desc {
  font-size: 11px;
  color: color-mix(in srgb, var(--fg) 50%, transparent);
}
.cookbook-delete-preset-btn {
  color: color-mix(in srgb, var(--fg) 50%, transparent);
  font-size: 12px;
}
.cookbook-delete-preset-btn:hover {
  color: var(--red);
}

/* Sliders span full row for alignment */
.cookbook-field-slider {
  grid-column: 1 / -1;
}

/* Kill button */
.cookbook-kill-btn {
  border-color: var(--color-error);
  color: var(--color-error);
  background: transparent;
}
.cookbook-kill-btn:hover {
  background: var(--color-error);
  color: var(--panel);
  border-color: var(--color-error);
}

/* Error diagnosis banner */
.cookbook-diagnosis {
  margin-top: 6px;
  padding: 10px 12px;
  background: color-mix(in srgb, var(--color-error) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-error) 30%, transparent);
  border-radius: 6px;
}
.cookbook-diag-header {
  display: flex;
  align-items: center;
  gap: 7px;
  position: relative;
  top: -4px;
  margin-bottom: -4px;
}
.cookbook-diag-fold {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0;
  min-height: 0;
  border: 0;
  background: transparent;
  color: var(--color-error);
  font: inherit;
  font-size: 11px;
  font-weight: 700;
  cursor: pointer;
  margin-right: auto;
}
.cookbook-diag-fold:hover {
  background: transparent;
  color: var(--color-error);
  opacity: 0.85;
}
.cookbook-diag-chevron {
  display: inline-block;
  width: 10px;
  font-size: 10px;
}
.cookbook-diag-copy {
  border: 0;
  background: transparent;
  color: var(--fg-muted);
  padding: 0 2px;
  width: 18px;
  height: 18px;
  min-height: 18px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.cookbook-diag-copy:hover {
  background: transparent;
  color: var(--fg);
}
.cookbook-diag-copy.copied {
  color: var(--green, #50fa7b);
}
.cookbook-diag-copy svg {
  display: block;
}
.cookbook-diag-dismiss {
  border: 0;
  background: transparent;
  color: var(--fg-muted);
  padding: 0;
  width: 16px;
  height: 18px;
  min-height: 18px;
  line-height: 16px;
  font-size: 13px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  top: -2px;
}
.cookbook-diag-dismiss:hover {
  background: transparent;
  color: var(--color-error);
}
.cookbook-diag-body {
  margin-top: 7px;
}
.cookbook-diag-message {
  font-size: 12px;
  font-weight: 600;
  color: var(--color-error);
  margin-bottom: 4px;
  margin-left: 2px;
  user-select: text;
}
.cookbook-diag-suggestion {
  font-size: 11px;
  line-height: 1.35;
  color: var(--fg-muted);
  margin-bottom: 8px;
  margin-left: 2px;
  user-select: text;
}
.cookbook-diag-fixes {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.cookbook-diag-actions {
  position: relative;
  display: inline-flex;
}
.cookbook-diag-action-trigger {
  font-size: 11px;
  padding: 4px 10px;
  min-height: 24px;
  background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--color-error) 40%, transparent);
  color: var(--fg);
}
.cookbook-diag-action-trigger:hover {
  border-color: var(--color-error);
  background: color-mix(in srgb, var(--color-error) 12%, transparent);
}
.cookbook-diag-menu {
  position: absolute;
  left: 0;
  top: calc(100% + 4px);
  min-width: 180px;
  z-index: 80;
}
.cookbook-diag-menu button {
  width: 100%;
  justify-content: flex-start;
  text-align: left;
  white-space: nowrap;
}
.cookbook-diag-btn {
  font-size: 11px;
  padding: 4px 10px;
  background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--color-error) 40%, transparent);
  color: var(--fg);
}
.cookbook-diag-btn:hover {
  border-color: var(--color-error);
  background: color-mix(in srgb, var(--color-error) 12%, transparent);
}

/* ── What Fits? (hardware model fitting tab in cookbook) ── */
.cookbook-group.hidden { display: none !important; }

/* Section titles */
.cookbook-section-title {
  font-size: 12px;
  font-weight: 600;
  color: var(--fg);
  opacity: 0.7;
  margin: 10px 0 4px;
}
.cookbook-section-title:first-child { margin-top: 0; }

/* Download input */
.cookbook-dl-input {
  display: flex;
  gap: 6px;
  margin-bottom: 4px;
  align-items: stretch;
}
.cookbook-dl-repo {
  flex: 1;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg);
  font-family: 'Fira Code', monospace;
  font-size: 12px;
  padding: 5px 8px;
}
.cookbook-dl-repo:focus {
  border-color: var(--accent, var(--red));
  outline: none;
}
.cookbook-dl-repo::placeholder {
  color: var(--fg-muted);
  opacity: 0.5;
  font-family: inherit;
}
.cookbook-dl-btn {
  background: var(--accent, var(--red));
  color: #fff;
  border: none;
  border-radius: 4px;
  padding: 0 14px;
  height: 28px;
  font-size: 11px;
  font-family: inherit;
  cursor: pointer;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  margin-top: -4px;
}
.cookbook-dl-btn:hover {
  opacity: 0.9;
}

/* HF link in search panel */
.hwfit-panel-hf-link {
  font-size: 10px;
  color: var(--red);
  text-decoration: none;
  margin-left: auto;
  padding: 2px 6px;
  border: 1px solid color-mix(in srgb, var(--red) 40%, transparent);
  border-radius: 3px;
  transition: all 0.15s;
}
.hwfit-panel-hf-link:hover {
  color: #fff;
  background: var(--red);
  border-color: var(--red);
}

/* Add Server collapsible */
.cookbook-server-details {
  margin: 4px 0 6px;
}
.cookbook-server-toggle {
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 4px;
  user-select: none;
  list-style: none;
  padding: 4px 6px;
  border-radius: 4px;
  font-size: 11px;
  color: var(--fg-muted);
  transition: background 0.1s, color 0.1s;
}
.cookbook-server-toggle::-webkit-details-marker { display: none; }
.cookbook-server-toggle:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 5%, transparent); }
.cookbook-server-toggle svg {
  opacity: 0.5;
  transition: transform 0.2s;
}
.cookbook-server-details[open] .cookbook-server-toggle svg {
  transform: rotate(45deg);
}
.cookbook-settings-content {
  padding: 6px 0;
}
.cookbook-settings-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-bottom: 8px;
}
.cookbook-settings-label {
  font-size: 10px;
  color: var(--fg-muted);
}
.cookbook-settings-hint {
  opacity: 0.5;
  font-weight: 400;
}
.cookbook-settings-input {
  width: 100%;
  font-size: 11px;
  padding: 4px 6px;
}
.cookbook-settings-subtitle {
  font-size: 10px;
  font-weight: 600;
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.3px;
  margin: 8px 0 4px;
  display: flex;
  align-items: center;
  gap: 6px;
}
.cookbook-server-add {
  background: none;
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--fg-muted);
  font-size: 13px;
  width: 16px;
  height: 16px;
  position: relative;
  top: -3px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  line-height: 0;
  padding-bottom: 3px;
  line-height: 16px;
  text-align: center;
  transition: color 0.1s, border-color 0.1s;
}
.cookbook-server-add:hover {
  color: var(--fg);
  border-color: var(--fg);
}
.cookbook-server-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: center;
}
.cookbook-srv-status {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #777;
  transition: background 0.2s, box-shadow 0.2s, transform 0.15s;
  cursor: pointer;
}
.cookbook-srv-status:hover { transform: scale(1.3); }
.cookbook-srv-status.testing {
  background: var(--color-warning, #f0ad4e);
  animation: cookbook-srv-pulse 0.9s ease-in-out infinite;
}
.cookbook-srv-status.ok {
  background: var(--color-success, #50fa7b);
  /* Soft outer ring + breathing halo so active servers glow like LEDs. */
  box-shadow:
    0 0 0 2px color-mix(in srgb, var(--color-success, #50fa7b) 25%, transparent),
    0 0 10px 2px color-mix(in srgb, var(--color-success, #50fa7b) 55%, transparent);
  animation: cookbook-srv-glow-ok 2.4s ease-in-out infinite;
}
.cookbook-srv-status.fail {
  background: var(--red, #e06c75);
  box-shadow:
    0 0 0 2px color-mix(in srgb, var(--red, #e06c75) 25%, transparent),
    0 0 10px 2px color-mix(in srgb, var(--red, #e06c75) 55%, transparent);
}
@keyframes cookbook-srv-pulse {
  0%, 100% { opacity: 0.45; }
  50% { opacity: 1; }
}
@keyframes cookbook-srv-glow-ok {
  0%, 100% {
    box-shadow:
      0 0 0 2px color-mix(in srgb, var(--color-success, #50fa7b) 22%, transparent),
      0 0 8px 1px color-mix(in srgb, var(--color-success, #50fa7b) 45%, transparent);
  }
  50% {
    box-shadow:
      0 0 0 3px color-mix(in srgb, var(--color-success, #50fa7b) 38%, transparent),
      0 0 14px 3px color-mix(in srgb, var(--color-success, #50fa7b) 75%, transparent);
  }
}
.cookbook-server-row input.hwfit-sf,
.cookbook-server-row select.hwfit-sf {
  font-size: 11px;
  padding: 0 6px;
  margin: 0;
  height: 24px;
  min-height: 24px;
  max-height: 24px;
  line-height: 24px;
  box-sizing: border-box;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--fg);
  font-family: inherit;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
}
.cookbook-server-row select.hwfit-sf {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5'%3E%3Cpath d='M0 0l4 5 4-5z' fill='%23999'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 6px center;
  padding-right: 18px;
}
/* Each server is its own card block (mirrors the Running tab's task cards):
   full border + accent left-rail + rounded corners + subtle fill, spaced. */
.cookbook-server-entry {
  margin-bottom: 8px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-left: 3px solid color-mix(in srgb, var(--fg) 30%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--fg) 3%, var(--bg));
  box-sizing: border-box;
}
.cookbook-server-entry:last-child { margin-bottom: 0; }
/* Keep the row inside the card on narrow screens — the fixed-width host/path
   inputs would otherwise overflow the card's background to the right. */
@media (max-width: 768px) {
  .cookbook-server-row .cookbook-srv-host,
  .cookbook-server-row .cookbook-srv-path { flex: 1 1 100% !important; width: auto !important; min-width: 0 !important; }
}
.cookbook-server-row .cookbook-srv-name { width: 60px; flex-shrink: 0; flex-grow: 0; }
.cookbook-server-row .cookbook-srv-host { flex: 1; min-width: 100px; }
.cookbook-server-row .cookbook-srv-host[readonly] { opacity: 0.4; cursor: default; }
.cookbook-server-row .cookbook-srv-port { width: 40px; flex-shrink: 0; flex-grow: 0; }
.cookbook-server-row .cookbook-srv-env { width: 65px; flex-shrink: 0; flex-grow: 0; }
.cookbook-server-row .cookbook-srv-path { flex: 1; min-width: 80px; }
/* Normalize every control on a server row to the same 24-px height — with
   many servers the small per-control height drift was very visible. */
.cookbook-server-row > * {
  height: 24px;
  box-sizing: border-box;
  flex-shrink: 0;
}
.cookbook-server-row > input.hwfit-sf,
.cookbook-server-row > select.hwfit-sf,
.cookbook-server-row > button,
.cookbook-server-row .cookbook-srv-actions > button {
  height: 24px;
  line-height: 22px;
  padding: 0 8px;
  font-size: 11px;
  box-sizing: border-box;
}
.cookbook-server-row .cookbook-srv-actions > .close-btn { width: 24px; padding: 0; line-height: 22px; }
.cookbook-server-row .cookbook-dep-tag { display: inline-flex; align-items: center; height: 24px; padding: 0 6px; line-height: 1; }
.cookbook-server-row .cookbook-srv-status { width: 8px; height: 8px; align-self: center; }
.cookbook-server-rm {
  width: 22px;
  height: 22px;
  font-size: 10px;
  opacity: 0.55;
  border: 1px solid var(--border);
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: opacity 0.12s, background 0.12s, color 0.12s, border-color 0.12s;
}
.cookbook-server-rm:hover {
  opacity: 1;
  background: var(--red);
  color: #fff;
  border-color: var(--red);
}
/* Labelled "Delete server" variant (lives in the Model Directory row) — override
   the 22x22 icon box with a normal text button. */
.cookbook-server-rm-btn {
  width: auto;
  height: auto;
  padding: 2px 8px;
  font-size: 10px;
  font-family: inherit;
  background: none;
  color: var(--red);
  border-color: color-mix(in srgb, var(--red) 40%, var(--border));
  white-space: nowrap;
  position: relative;
  top: -3px;
}
/* The "+" glyph in the Servers-header "+ Add" button, nudged up 1px (scoped so
   the shared calendar +New pill is unaffected). */
#cookbook-server-add .cal-add-plus { position: relative; top: -1px; }
/* Save button on a new server entry — same shape as Delete, accent-colored;
   turns green once saved. */
.cookbook-server-save-btn {
  width: auto;
  padding: 2px 8px;
  font-size: 10px;
  font-family: inherit;
  cursor: pointer;
  background: none;
  color: var(--accent, var(--red));
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 45%, var(--border));
  border-radius: 4px;
  white-space: nowrap;
}
.cookbook-server-save-btn:hover { background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent); }
/* Cancel (discard a new server) — same shape/size as Save, muted. */
.cookbook-server-cancel-btn {
  width: auto;
  padding: 2px 8px;
  font-size: 10px;
  font-family: inherit;
  cursor: pointer;
  background: none;
  color: var(--fg-muted, var(--fg));
  border: 1px solid var(--border);
  border-radius: 4px;
  white-space: nowrap;
}
.cookbook-server-cancel-btn:hover { color: var(--fg); border-color: var(--fg); }
.cookbook-server-save-btn.saved {
  color: var(--green, #50fa7b);
  border-color: color-mix(in srgb, var(--green, #50fa7b) 45%, var(--border));
  cursor: default;
}
.cookbook-path-row {
  display: flex;
  gap: 4px;
  margin-bottom: 3px;
  align-items: center;
}
.cookbook-path-input {
  flex: 1;
}
.cookbook-path-add {
  background: none;
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--fg-muted);
  font-size: 12px;
  width: 18px;
  height: 18px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  line-height: 1;
  transition: color 0.1s, border-color 0.1s;
}
.cookbook-path-add:hover {
  color: var(--fg);
  border-color: var(--fg);
}

/* Server selector in search toolbar */
.hwfit-server-select {
  min-width: 70px;
}
/* GPU toggle buttons */
.hwfit-gpu-toggles {
  display: flex;
  gap: 2px;
  align-items: center;
  position: relative;
  top: -3px;
}
.hwfit-gpu-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg-muted);
  font-size: 10px;
  padding: 0 5px;
  cursor: pointer;
  min-width: 20px;
  height: 28px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.1s, color 0.1s, border-color 0.1s;
  font-family: inherit;
}
.hwfit-gpu-btn:hover {
  color: var(--fg);
  border-color: var(--fg);
}
.hwfit-gpu-btn.active {
  background: var(--accent, var(--red));
  color: #fff;
  border-color: var(--accent, var(--red));
}
/* Pool selector for heterogeneous GPU boxes — sits left of the RAM/GPU buttons */
.hwfit-gpu-group {
  background: var(--bg-elev, var(--bg));
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg);
  font-size: 10px;
  height: 28px;
  padding: 0 4px;
  cursor: pointer;
  box-sizing: border-box;
  font-family: inherit;
  max-width: 190px;
}
.hwfit-gpu-group:hover { border-color: var(--fg); }
/* Brief highlight on the serve command box when a saved config is loaded, so
   the click clearly registers (loading is otherwise silent). */
.cookbook-cmd-flash {
  animation: cookbookCmdFlash 0.6s ease;
}
@keyframes cookbookCmdFlash {
  0%   { box-shadow: 0 0 0 0 var(--accent, var(--red)); border-color: var(--accent, var(--red)); }
  30%  { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, var(--red)) 40%, transparent); border-color: var(--accent, var(--red)); }
  100% { box-shadow: 0 0 0 0 transparent; }
}
/* "Confirmed working" tick on a saved serve config (auto-saved once its endpoint registered) */
.cookbook-saved-confirmed {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  line-height: 0;
}
.hwfit-container { display: flex; flex-direction: column; gap: 8px; }
.hwfit-toolbar {
  display: flex; gap: 4px; align-items: center; flex-wrap: wrap;
}
.hwfit-toolbar select,
.hwfit-toolbar input {
  height: 28px;
  padding: 0 6px;
  font-size: 11px;
  font-family: inherit;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  box-sizing: border-box;
}
.hwfit-toolbar select:focus,
.hwfit-toolbar input:focus { outline: none; border-color: var(--red); }
.hwfit-toolbar .hwfit-server-select { min-width: 70px; flex-shrink: 0; }
.hwfit-toolbar .hwfit-usecase { min-width: 70px; flex-shrink: 0; }
.hwfit-toolbar .hwfit-quant { min-width: 50px; flex-shrink: 0; }
.hwfit-toolbar .hwfit-search { flex: 1; min-width: 80px; }
.hwfit-help-chip {
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--fg) 22%, transparent);
  color: color-mix(in srgb, var(--fg) 55%, transparent);
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  cursor: help;
  position: relative;
  top: -1px;
  margin-left: -1px;
}
.hwfit-help-chip:hover {
  color: var(--fg);
  border-color: color-mix(in srgb, var(--fg) 45%, transparent);
  background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.hwfit-help-chip-inline {
  margin-left: -2px;
  margin-right: 0;
}
.hwfit-ctx-control {
  height: 28px;
  min-width: 134px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0 7px;
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg-muted);
  background: var(--bg);
  font-size: 10px;
  box-sizing: border-box;
}
.hwfit-ctx-control span {
  text-transform: uppercase;
  letter-spacing: 0.3px;
  opacity: 0.75;
}
.hwfit-ctx-control input[type="range"] {
  width: 54px;
  min-width: 54px;
  height: 16px;
  padding: 0;
  border: 0;
  background: transparent;
  accent-color: var(--accent, var(--red));
}
.hwfit-ctx-control output {
  min-width: 28px;
  text-align: right;
  color: var(--fg);
  font-weight: 600;
}
.hwfit-server-toggle { flex-shrink: 0; font-size: 10px !important; padding: 3px 8px !important; white-space: nowrap; }
.hwfit-toolbar .hwfit-host { width: 110px; flex-shrink: 0; }
.hwfit-env-row { gap: 6px; flex-wrap: wrap; }
.hwfit-env-row .hwfit-envtype { width: auto; min-width: 70px; flex-shrink: 0; }
.hwfit-env-row .hwfit-envpath { flex: 1; min-width: 100px; }
.hwfit-env-row .hwfit-gpus { width: 90px; flex-shrink: 0; }
.hwfit-hw {
  display: flex; flex-wrap: wrap; column-gap: 4px; row-gap: 7px; padding: 4px 0;
}
.hwfit-hw-chip {
  font-size: 10px; padding: 0 8px; border-radius: 6px;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  color: var(--fg); opacity: 0.7; white-space: nowrap;
  border: 0;
  font-family: inherit;
  line-height: 1;
  height: 17px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  /* Cap chip width so a long label (e.g. heterogeneous GPU group
     "1× RTX 4090 + 1× RTX 3060") wraps to the next row instead of
     overflowing the modal. Full text stays in the tooltip. */
  max-width: 100%;
}
.hwfit-hw-chip-toggle {
  /* Allow the chip body to truncate with an ellipsis when the chip
     itself is capped at its container's width. Without this, the
     toggle button keeps its intrinsic width and pushes the × button
     off-screen on narrow viewports. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hwfit-hw-chip button,
.hwfit-hw-chip-dismiss,
.hwfit-hw-chip-manual,
.hwfit-hw-chip-toggle,
.hwfit-hw-chip-x {
  appearance: none;
  -webkit-appearance: none;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  padding: 0;
  /* Inherit the chip's line-height so a slightly larger × glyph
     doesn't push past the 17px chip height (which was clipping the
     text vertically in the multi-button layout). */
  line-height: inherit;
}
.hwfit-chip-x {
  display: inline-block;
  transform: translateY(-1px);
}
.hwfit-hw-chip-dismiss { cursor: pointer; }
.hwfit-hw-chip-dismiss:hover { opacity: 1; }
/* Two-part chip: text body toggles dim on click, × removes the chip.
   Visual minimal — just a slightly bigger × so it's easier to hit. */
.hwfit-hw-chip-toggle {
  cursor: pointer;
  /* `!important` because the earlier `.hwfit-hw-chip button { font:
     inherit }` reset (same specificity, defined later in source)
     was winning over the plain rule and leaving the manual chip's
     button at the browser-default ~13px. Forcing it locally so the
     manual chip's text matches every other chip's text. */
  font-size: 10px !important;
  line-height: 1 !important;
  transform: translateY(-3px);
}
.hwfit-hw-chip-x {
  cursor: pointer;
  /* `!important` for the same cascade reason as the toggle button —
     the `.hwfit-hw-chip button { font: inherit }` reset was leaving
     the regular chips' × inheriting the chip's 10px while the
     manual chip's × landed on the browser default (~13px), making
     manual look bigger. Lock every × at 13px. */
  font-size: 13px !important;
  line-height: 1 !important;
  transform: translateY(-5px);
}
.hwfit-hw-chip-x:hover { opacity: 1; }
.hwfit-hw-chip-off {
  /* Ghosted: drop the pill background so it reads as "off" rather than
     "selected but faded". Lower text opacity matches the muted feel. */
  background: transparent !important;
  opacity: 0.4;
}
.hwfit-hw-chip-manual {
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 45%, transparent);
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
  color: var(--accent, var(--red));
  opacity: 1;
  cursor: pointer;
  font-family: inherit;
  margin-inline: 3px;
  padding-left: 7px;
  padding-right: 7px;
}
.hwfit-hw-chip-manual:hover {
  background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
}
.hwfit-hw-manual-btn {
  min-width: 42px;
}
.hwfit-manual-panel {
  display: flex;
  gap: 4px;
  align-items: center;
  flex-wrap: wrap;
  width: 100%;
  margin-top: 2px;
}
.hwfit-manual-panel.hidden { display: none; }
.hwfit-manual-panel label {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 9px;
  color: var(--fg-muted);
}
.hwfit-manual-panel select,
.hwfit-manual-panel input,
.hwfit-manual-panel button {
  height: 24px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  border-radius: 4px;
  font: inherit;
  font-size: 10px;
  padding: 0 6px;
  box-sizing: border-box;
}
.hwfit-manual-panel input { width: 72px; }
.hwfit-manual-panel button { cursor: pointer; }
.hwfit-manual-panel .hwfit-hw-manual-save,
.hwfit-manual-panel .hwfit-hw-manual-clear {
  /* -3 (was -2) — 1px more up to optically align with the labeled
     inputs to their left. */
  transform: translateY(-3px);
}
.hwfit-manual-panel button:hover { border-color: var(--fg); }
/* GPU driver error (e.g. NVML version mismatch) — stands out from the muted
   chips and hints there's a real problem, with the full message on hover. */
.hwfit-hw-chip-error {
  background: color-mix(in srgb, var(--red) 16%, transparent);
  color: var(--red); opacity: 1; cursor: help;
  border: 1px solid color-mix(in srgb, var(--red) 40%, transparent);
}
.hwfit-list {
  max-height: 52vh; overflow-y: auto; display: flex; flex-direction: column; gap: 2px;
}
.hwfit-loading {
  display: flex; align-items: center; justify-content: center;
  color: var(--fg-muted); padding: 16px 0; font-size: 12px;
}
.hwfit-row {
  display: flex; align-items: center; gap: 6px; padding: 5px 8px;
  border-radius: 6px; cursor: pointer; font-size: 11px;
  transition: background 0.1s;
}
.hwfit-row:hover { background: color-mix(in srgb, var(--fg) 6%, transparent); }
/* Already-downloaded rows: dim slightly so attention falls on undownloaded
   options. The green dot stays bright as the "this is local" cue. */
.hwfit-row:has(.hwfit-dl-dot) { opacity: 0.55; }
.hwfit-row:has(.hwfit-dl-dot):hover { opacity: 0.9; }
.hwfit-row:has(.hwfit-dl-dot) .hwfit-dl-dot { opacity: 1; }
.hwfit-header {
  cursor: default; position: sticky; top: 0; z-index: 1;
  background: var(--panel); border-bottom: 1px solid var(--border);
  padding: 4px 8px; font-weight: 600;
}
.hwfit-header:hover { background: var(--panel); }
.hwfit-header .hwfit-col { font-size: 9px; color: var(--fg-muted); text-transform: uppercase; letter-spacing: 0.3px; }
.hwfit-header .hwfit-name { font-size: 9px; }
.hwfit-sortable { cursor: pointer; user-select: none; }
.hwfit-sortable:hover { color: var(--fg) !important; }
.hwfit-sort-active { color: var(--red) !important; }
.hwfit-col {
  flex-shrink: 0; white-space: nowrap; text-align: left;
  font-size: 9px; color: var(--fg-muted);
}
.hwfit-fit { width: 52px; font-weight: 700; text-transform: uppercase; }
.hwfit-name {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  font-weight: 500; font-size: 11px; color: var(--fg);
}
.hwfit-c-params { width: 42px; }
.hwfit-c-quant  { width: 52px; }
.hwfit-c-vram   { width: 42px; }
.hwfit-c-ctx    { width: 32px; }
.hwfit-c-speed  { width: 44px; }
.hwfit-c-score  { width: 40px; font-weight: 700; font-size: 11px; color: var(--fg); }
.hwfit-c-mode   { width: 72px; }
.hwfit-moe {
  display: inline-block; padding: 0 4px; border-radius: 4px; margin-left: 4px;
  background: color-mix(in srgb, var(--red) 15%, transparent);
  color: var(--red); font-weight: 600; font-size: 8px;
}
.hwfit-sort, .hwfit-quant { width: auto; min-width: 70px; flex-shrink: 0; }
.hwfit-row-active { background: color-mix(in srgb, var(--red) 8%, transparent); }

/* ── Inline action panel (expands below a model row) ── */
.hwfit-action-panel {
  border: 1px solid var(--border); border-left: 3px solid var(--red);
  border-radius: 0 6px 6px 6px; background: var(--panel);
  padding: 10px 12px; margin: 2px 0 6px; font-size: 11px;
  display: flex; flex-direction: column; gap: 8px;
  animation: hwfit-panel-in 0.15s ease-out;
}
@keyframes hwfit-panel-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}
.hwfit-panel-header {
  display: flex; align-items: center; gap: 8px; min-width: 0;
}
.hwfit-panel-model {
  font-size: 11px; font-weight: 600; color: var(--fg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0;
}
.hwfit-panel-badge {
  font-size: 9px; padding: 2px 8px; border-radius: 10px;
  background: color-mix(in srgb, var(--red) 12%, transparent);
  color: var(--red); font-weight: 600; flex-shrink: 0;
}
.hwfit-panel-fields {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
}
.hwfit-panel-fields label, .hwfit-adv-grid label {
  display: flex; align-items: center; gap: 4px; font-size: 10px; color: var(--fg-muted); white-space: nowrap;
}
.hwfit-panel-fields input, .hwfit-panel-fields select,
.hwfit-adv-grid input, .hwfit-adv-grid select {
  background: var(--bg); border: 1px solid var(--border); border-radius: 4px;
  color: var(--fg); font-size: 11px; padding: 3px 6px; font-family: inherit;
  outline: none; transition: border-color 0.15s;
}
.hwfit-panel-fields input:focus, .hwfit-adv-grid input:focus,
.hwfit-panel-fields select:focus, .hwfit-adv-grid select:focus {
  border-color: var(--red);
}
.hwfit-panel-fields input[type="text"] { width: 60px; }
.hwfit-adv-grid input[type="text"] { width: 80px; }
.hwfit-adv-grid {
  display: flex; flex-wrap: wrap; gap: 6px 12px; padding: 6px 0;
}
.hwfit-cb { cursor: pointer; }
.hwfit-cb input[type="checkbox"] { margin: 0 2px 0 0; }
.hwfit-panel-advanced, .hwfit-panel-settings {
  font-size: 10px;
}
.hwfit-panel-advanced summary, .hwfit-panel-settings summary {
  cursor: pointer; color: var(--fg-muted); font-size: 10px;
  user-select: none; padding: 2px 0;
}
.hwfit-panel-advanced summary:hover, .hwfit-panel-settings summary:hover { color: var(--fg); }
.hwfit-panel-cmd {
  font-family: 'Berkeley Mono', 'SF Mono', 'Fira Code', monospace;
  font-size: 10px; padding: 6px 8px; border-radius: 4px;
  background: var(--bg); border: 1px solid var(--border);
  white-space: pre-wrap; word-break: break-all; max-height: 60px; overflow-y: auto;
  color: var(--fg-muted);
}
.hwfit-panel-actions {
  display: flex; gap: 4px; flex-wrap: wrap;
}
.hwfit-panel-note {
  font-size: 10px;
  line-height: 1.35;
  color: var(--fg-muted);
  background: color-mix(in srgb, var(--yellow, #f1fa8c) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--yellow, #f1fa8c) 18%, var(--border));
  border-radius: 4px;
  padding: 5px 7px;
}

/* ── Saved presets ── */
.hwfit-preset {
  display: flex; align-items: center; gap: 8px; padding: 6px 10px;
  border: 1px solid var(--border); border-radius: 6px; font-size: 11px;
  transition: border-color 0.15s;
}
.hwfit-preset:hover { border-color: var(--red); }
.hwfit-preset-name { font-weight: 600; color: var(--fg); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hwfit-preset-model { font-size: 9px; color: var(--fg-muted); max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hwfit-preset-backend { font-size: 9px; padding: 1px 6px; border-radius: 8px; background: color-mix(in srgb, var(--fg) 8%, transparent); color: var(--fg-muted); }

@media (max-width: 600px) {
  .hwfit-c-ctx, .hwfit-c-speed, .hwfit-c-mode { display: none; }
  .hwfit-panel-fields { flex-direction: column; align-items: stretch; }
  .hwfit-panel-fields input[type="text"] { width: 100%; }
  .hwfit-preset-model { display: none; }
}

/* ===== Settings Modal Layout ===== */
.settings-modal-content {
  width: min(720px, 92vw);
  max-height: 85vh;
  padding: 0;
  container-type: inline-size;
  container-name: settings-modal;
}

/* Issue #208 — anchor the Settings window to the TOP of the chat area instead
   of vertically centering it. The base .modal uses `align-items:center`, so a
   centered window grows and shrinks around its own midpoint when you switch
   between tabs whose content differs in height (Add Models vs. Shortcuts,
   etc.). That makes the in-modal nav rail — and the whole window — appear to
   jump up and down between pages. Pinning the top edge keeps the nav rail and
   surrounding layout visually stable; the panel only ever grows downward.
   Desktop only: on mobile the panel is a full-height bottom sheet that is
   already top-stable, and a margin there would push it past the viewport. The
   drag/dock code clears this margin (sets inline margin:0) the moment a window
   is dragged, so moving the window still works exactly as before. */
@media (min-width: 769px) {
  #settings-modal { align-items: flex-start; }
  #settings-modal .settings-modal-content { margin-top: 7vh; }
}

.settings-modal-content .modal-header {
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}

.settings-layout {
  display: flex;
  min-height: 400px;
  max-height: calc(85vh - 60px);
}

.settings-modal-content[style*="height"],
#settings-modal.modal-right-docked .settings-modal-content {
  overflow: hidden;
}

.settings-modal-content[style*="height"] .settings-layout,
#settings-modal.modal-right-docked .settings-layout {
  flex: 1 1 0;
  min-height: 0;
  max-height: none;
}

.settings-modal-content[style*="height"] .settings-panels,
#settings-modal.modal-right-docked .settings-panels,
.settings-modal-content[style*="height"] .settings-sidebar,
#settings-modal.modal-right-docked .settings-sidebar {
  min-height: 0;
}

.settings-sidebar {
  width: 160px;
  flex-shrink: 0;
  border-right: 1px solid var(--border);
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: color-mix(in srgb, var(--fg) 2%, transparent);
}

.settings-sidebar-divider { height: 1px; background: var(--border); margin: 8px 12px; }
.settings-sidebar-label {
  font-size: 9px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
  opacity: 0.35;
  padding: 4px 12px 2px;
}
.settings-nav-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: none;
  background: none;
  color: var(--color-muted);
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  border-radius: 6px;
  transition: all 0.1s;
  text-align: left;
  white-space: nowrap;
}

.settings-nav-item:hover {
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  color: var(--fg);
}

.settings-nav-item.active {
  background: color-mix(in srgb, var(--red) 12%, transparent);
  color: var(--red);
}

.settings-nav-item svg {
  flex-shrink: 0;
  opacity: 0.7;
}

.settings-nav-item.active svg {
  opacity: 1;
}

.settings-sidebar-divider { height: 1px; background: var(--border); margin: 8px 12px; }

/* Keyboard shortcuts */
.shortcut-category {
  font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;
  opacity: 0.4; padding: 8px 0 4px; margin-top: 4px;
}
.shortcut-category:first-child { margin-top: 0; padding-top: 0; }
.shortcut-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 5px 0; border-bottom: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
}
.shortcut-row:last-child { border-bottom: none; }
.shortcut-row.shortcut-conflict { background: color-mix(in srgb, var(--warn) 6%, transparent); border-radius: 4px; padding: 5px 6px; }
.shortcut-label { font-size: 12px; display: flex; align-items: center; gap: 6px; }
.shortcut-icon { display: inline-flex; opacity: 0.5; flex-shrink: 0; }
.shortcut-warn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; border-radius: 50%; background: var(--warn); color: #fff;
  font-size: 9px; font-weight: 700; margin-left: 4px;
}
.shortcut-controls { display: flex; align-items: center; gap: 4px; }
/* Inline hint shown while rebinding a shortcut ("press a key" → "↵ Enter
   to save"). Subtle so it doesn't compete with the key caps. */
.shortcut-hint {
  font-size: 10px;
  opacity: 0.6;
  color: var(--accent, var(--red));
  white-space: nowrap;
  margin-right: 2px;
}
.shortcut-hint[hidden] { display: none; }
.shortcut-key {
  font-family: inherit; font-size: 0; padding: 2px 4px;
  background: transparent; border: none; border-radius: 4px;
  color: var(--fg); cursor: pointer; display: flex; align-items: center; gap: 2px;
  transition: all 0.15s;
}
.shortcut-key:hover { background: color-mix(in srgb, var(--accent, #cc6a3a) 8%, transparent); }
/* Unbound shortcut — show a dashed "Set" placeholder instead of keycaps. */
.shortcut-key-unset { font-size: 10px; }
.shortcut-unset {
  font-size: 10px;
  padding: 2px 8px;
  border: 1px dashed var(--border);
  border-radius: 4px;
  color: color-mix(in srgb, var(--fg) 45%, transparent);
}
.shortcut-key-unset:hover .shortcut-unset { border-color: var(--accent, var(--red)); color: var(--fg); }
.shortcut-key kbd {
  display: inline-block; font-family: inherit; font-size: 10px;
  padding: 2px 6px; min-width: 20px; text-align: center;
  /* Highlight kbd chips in the theme accent so they stand out from
     normal text and clearly mark "this is a key you press". */
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, var(--bg));
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 40%, var(--border));
  color: var(--accent, var(--red));
  border-radius: 3px;
  box-shadow: 0 1px 0 color-mix(in srgb, var(--accent, var(--red)) 25%, transparent);
  line-height: 1.4;
  font-weight: 600;
}
.shortcut-key.listening {
  background: color-mix(in srgb, var(--accent, #cc6a3a) 10%, transparent);
  animation: shortcut-pulse 1s infinite;
}
.shortcut-key.listening kbd { border-color: var(--accent, #cc6a3a); }
.shortcut-action-btn {
  width: 24px; height: 24px; border-radius: 4px; border: 1px solid var(--border);
  background: transparent; color: var(--fg); cursor: pointer; font-size: 13px;
  display: flex; align-items: center; justify-content: center; transition: all 0.15s;
}
.shortcut-action-btn:hover { border-color: var(--accent, #cc6a3a); background: color-mix(in srgb, var(--accent, #cc6a3a) 10%, var(--bg)); }
.shortcut-action-btn.is-reset { opacity: 0.5; }
.shortcut-action-btn.is-reset:hover { opacity: 1; }
@keyframes shortcut-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.6; }
}

.settings-panels {
  flex: 1;
  overflow-y: auto;
  padding: 16px 20px;
  min-width: 0;
}
.settings-appearance-panel {
  flex-direction: column;
}
.settings-appearance-panel:not(.hidden) {
  display: flex;
}
.settings-appearance-panel > .admin-card:nth-of-type(1) { order: 3; }
.settings-appearance-panel > .admin-card:nth-of-type(2) { order: 1; }
.settings-appearance-panel > .admin-card:nth-of-type(3) { order: 2; }
.settings-appearance-panel > .admin-card:nth-of-type(n+4) { order: 4; }

/* Mobile: stack tabs on top */
@media (max-width: 600px) {
  .settings-layout {
    flex-direction: column;
  }
  .settings-sidebar {
    width: auto;
    flex-direction: row;
    border-right: none;
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    padding: 6px;
  }
  .settings-nav-item {
    padding: 6px 10px;
    font-size: 11px;
  }
}

/* Snapped/narrow Settings window: move the tab rail to the top. Viewport media
   alone misses desktop right-half snapping, where the modal is narrow but the
   browser window is not. */
@container settings-modal (max-width: 620px) {
  .settings-layout {
    flex-direction: column;
  }
  .settings-sidebar {
    width: auto;
    max-height: none;
    flex-direction: row;
    border-right: none;
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    overflow-y: hidden;
    padding: 6px;
    scrollbar-width: none;
  }
  .settings-sidebar::-webkit-scrollbar {
    display: none;
  }
  .settings-sidebar-divider,
  .settings-sidebar-label {
    display: none;
  }
  .settings-nav-item {
    flex: 0 0 auto;
    padding: 6px 10px;
    font-size: 11px;
  }
  .settings-panels {
    padding: 12px 14px;
  }
}

/* ── Entrance Animations ── */

/* Welcome name — left-to-right wipe with a touch of horizontal stretch.
   Fires on initial render via the .welcome-name rule; restarts on Nobody⇄
   Odysseus toggle via JS reflow trick. */
.welcome-name {
  transform-origin: left center;
  animation: welcome-name-reveal 0.55s cubic-bezier(0.34, 1.32, 0.55, 1) both;
}
/* Hold the welcome-screen entrance animations until the app has finished its
   initial load (fonts loaded + layout settled). Running them mid-load made the
   splash jump/flicker as the page reflowed ("haywire"). Until body.welcome-ready
   is set by JS, the splash sits in its final, static state (no flash, since the
   non-animated state IS the resting state); adding the class then plays the
   entrance once, cleanly. */
body:not(.welcome-ready) #welcome-screen,
body:not(.welcome-ready) .welcome-name {
  animation: none !important;
}
/* Hold the splash invisible (the entrance's start state) until ready, so when
   the animation is released it fades/wipes in smoothly instead of flashing
   from fully-visible to the animation's hidden first frame. */
body:not(.welcome-ready) #welcome-screen {
  opacity: 0;
}
@keyframes welcome-name-reveal {
  from {
    clip-path: inset(0 100% 0 0);
    transform: scaleX(0.96);
    opacity: 0.5;
  }
  to {
    clip-path: inset(0 0 0 0);
    transform: scaleX(1);
    opacity: 1;
  }
}

@keyframes welcome-enter {
  from {
    opacity: 0;
    transform: translate(-50%, -45%);
  }
  to {
    opacity: 1;
    transform: translate(-50%, -50%);
  }
}

@keyframes msg-enter {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes picker-roll-up {
  from {
    opacity: 0;
    transform: scaleY(0.4) translateY(8px);
  }
  to {
    opacity: 1;
    transform: scaleY(1) translateY(0);
  }
}
@keyframes picker-roll-down {
  from {
    opacity: 1;
    transform: scaleY(1) translateY(0);
  }
  to {
    opacity: 0;
    transform: scaleY(0.4) translateY(8px);
  }
}

@keyframes modal-enter {
  from {
    opacity: 0;
    transform: scale(0.95) translateY(8px);
  }
  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}
@keyframes modal-exit {
  from {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
  to {
    opacity: 0;
    transform: scale(0.97) translateY(6px);
  }
}
.modal-content.modal-closing {
  animation: modal-exit 0.18s ease-in both;
}

@keyframes cookbook-modal-enter {
  0% {
    opacity: 0;
    transform: translateY(12px) scale(0.94);
    filter: saturate(0.85);
  }
  65% {
    opacity: 1;
    transform: translateY(-2px) scale(1.012);
    filter: saturate(1.05);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
    filter: none;
  }
}
#cookbook-modal .modal-content.cookbook-modal-entering {
  animation: cookbook-modal-enter 0.28s cubic-bezier(0.22, 1.35, 0.36, 1) both;
}
/* Mobile: real bottom-sheet slide-up — the existing 12px translate is
   too subtle on a phone, the modal effectively just appeared. */
@media (max-width: 768px) {
  #cookbook-modal .modal-content.cookbook-modal-entering {
    animation: cookbook-modal-enter-mobile 0.32s cubic-bezier(0.22, 1, 0.36, 1) both;
  }
}
@keyframes cookbook-modal-enter-mobile {
  0%   { opacity: 0; transform: translateY(100%); }
  100% { opacity: 1; transform: translateY(0); }
}
#cookbook-modal .modal-content:not(.cookbook-modal-entering):not(.modal-closing) {
  animation: none;
}
#cookbook-modal .modal-content.modal-closing {
  animation: modal-exit 0.18s cubic-bezier(0.4, 0, 1, 1) both;
}

/* Per-token streaming fade — new words materialize */
.token-new {
  animation: token-fade 0.4s ease-out both;
}

@keyframes token-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Disable entrance animations when loading chat history (bulk render) */
.chat-history.no-animate .msg {
  animation: none;
}

/* Prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
  .msg,
  .modal-content,
  #welcome-screen,
  .toast,
  .token-new {
    animation: none !important;
    transition: none !important;
  }
}

/* ── Tasks ── */
.tasks-modal-content { max-width: 600px; width: min(600px, 92vw); background: var(--bg); font-size: 12px; }

/* Tasks tabs reuse the .memory-tab look. The Brain window's tab bar is
   full-bleed (its underline spans the whole modal width). The Tasks bar is a
   direct child of .modal-content (padding:10px), so cancel that padding with
   negative side margins to span edge-to-edge, then re-inset the tabs by 10px
   so they line up with the rest of the modal content — matching the Brain bar. */
.tasks-modal-content .tasks-tabs {
  margin: -2px -10px 8px;
  padding: 0 10px;
}

/* Activity log — compact by default. Click the row to expand body+actions. */
.task-log-row {
  --cat-hue: 220;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 4px 10px;
  margin-bottom: 3px;
  background: color-mix(in srgb, var(--fg) 2%, transparent);
  position: relative;
  cursor: pointer;
  transition: padding .12s ease;
}
.task-log-row:hover {
  background: color-mix(in srgb, var(--fg) 4%, transparent);
}
.task-log-row.expanded { padding: 8px 10px 6px; }
.task-log-row-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  margin-bottom: 0;
}
.task-log-row.expanded .task-log-row-head { margin-bottom: 4px; }
/* Collapsed: body + footer hidden. Expanded: visible. */
.task-log-row:not(.expanded):not(.is-skipped) .task-log-row-body,
.task-log-row:not(.expanded):not(.is-skipped) .task-log-row-actions,
.task-log-row:not(.expanded):not(.is-skipped) .task-log-prompt {
  display: none;
}
.task-log-name {
  font-weight: 600;
  /* Pull saturation from the per-row hue but mix with the foreground so the
     title still reads in dark mode. Lightness stays adaptive. */
  color: hsl(var(--cat-hue) 60% 60%);
}
.task-log-task-icon {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  margin-right: -3px;
}
.task-log-task-icon svg {
  top: 0 !important;
  opacity: 0.46 !important;
}
.task-log-row-head .task-ai-mark {
  top: 0;
  margin-left: -4px;
}
.task-log-repeat {
  font-size: 10px;
  font-weight: 500;
  line-height: 1;
  color: color-mix(in srgb, var(--fg) 62%, transparent);
  border-radius: 999px;
  padding: 1px 0;
  white-space: nowrap;
}
@media (prefers-color-scheme: light) {
  .task-log-name { color: hsl(var(--cat-hue) 65% 38%); }
}
/* Per-account prefix in fan-out results — e.g. "[Default] No recent emails"
   becomes a compact accent chip + plain message. Makes multi-account activity
   rows readable instead of a bracket soup. */
/* Running / queued status line in the Activity tab — whirlpool + label. */
.task-log-running {
  display: inline-flex;
  align-items: center;
  gap: 0;
  font-size: 11px;
  opacity: 0.75;
}
.task-log-running-label { font-style: normal; }

/* New right-side placement: "Running <whirlpool>" sits where the timestamp
   normally would, on the head row's right edge. */
.task-log-running-inline {
  display: inline-flex;
  align-items: center;
  gap: 0;
  font-size: 11px;
  opacity: 0.75;
}
.task-log-running-inline .task-log-running-label { font-weight: 500; }
.task-log-running-elapsed {
  margin-left: 6px;
  opacity: 0.6;
  font-variant-numeric: tabular-nums;
}
.task-log-force-run {
  border: 0;
  background: color-mix(in srgb, var(--fg) 7%, transparent);
  box-shadow: none;
  color: inherit;
  opacity: .82;
  margin-left: 7px;
  padding: 1px 6px 1px 4px;
  min-height: 16px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  font-family: inherit;
  font-size: 10px;
  line-height: 1;
  cursor: pointer;
  position: relative;
  top: -1px;
}
.task-log-force-run svg {
  display: block;
  flex-shrink: 0;
}
.task-log-force-run:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--green, #50fa7b) 16%, transparent);
  color: var(--green, #50fa7b);
}
.task-log-stop {
  border: 0;
  background: transparent;
  color: inherit;
  opacity: .72;
  padding: 0;
  margin-left: 6px;
  width: 12px;
  height: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  position: relative;
  top: -3px;
}
.task-log-stop:hover {
  opacity: 1;
  color: var(--red, #f87171);
}

/* Slim single-line row for skipped (noop) runs — body/actions stripped, font
   shrunk, opacity dropped. Distinguishes "task ran but had nothing to do"
   from a "real" entry without flooding the feed visually. */
.task-log-row.is-skipped {
  padding: 4px 8px;
  opacity: 0.45;
  font-size: 11px;
  background: transparent;
}
.task-log-row.is-skipped .task-log-row-head { padding: 0; }
.task-log-row.is-skipped .task-log-name { font-weight: 500; }
.task-log-row.is-skipped .task-log-skipped-reason {
  flex: 1 1 auto;
  margin-left: 6px;
  font-style: italic;
  opacity: 0.85;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.task-log-row.is-skipped:hover { opacity: 0.7; }

.task-log-account-tag {
  display: inline-block;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 1px 7px;
  margin-right: 4px;
  border-radius: 10px;
  color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
  vertical-align: 1px;
}
.task-log-time {
  opacity: 0.5;
  font-variant-numeric: tabular-nums;
}
.task-log-status {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
  background: color-mix(in srgb, var(--fg) 30%, transparent);
}
.task-log-status-ok    { background: #4ade80; box-shadow: 0 0 6px #4ade80, 0 0 3px #4ade80; }
.task-log-status-error { background: var(--red, #f87171); box-shadow: 0 0 6px var(--red, #f87171), 0 0 3px var(--red, #f87171); }
.task-log-status-info  { background: color-mix(in srgb, var(--fg) 25%, transparent); }
.task-log-status-queued  { background: #fbbf24; box-shadow: 0 0 0 2px color-mix(in srgb, #fbbf24 30%, transparent); }
.task-log-status-running { background: #60a5fa; animation: task-log-pulse 1.4s ease-in-out infinite; }
.task-log-status-skipped { background: color-mix(in srgb, var(--fg) 20%, transparent); }
.task-log-status-aborted { background: color-mix(in srgb, var(--fg) 30%, transparent); border: 1px dashed color-mix(in srgb, var(--fg) 50%, transparent); }
@keyframes task-log-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, #60a5fa 60%, transparent); }
  50%      { box-shadow: 0 0 0 4px color-mix(in srgb, #60a5fa 0%, transparent); }
}
.task-log-row-body {
  font-size: 12px;
  line-height: 1.5;
  color: color-mix(in srgb, var(--fg) 85%, transparent);
  overflow-wrap: anywhere;
  word-break: break-word;
}
.task-log-row-body p { margin: 0 0 6px 0; }
.task-log-row-body p:last-child { margin-bottom: 0; }
.task-log-row-body pre {
  margin: 4px 0;
  padding: 6px 8px;
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  border-radius: 4px;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 11px;
  max-height: 180px;
  overflow: auto;
}
.task-log-row-body code {
  font-size: 11px;
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  padding: 1px 4px;
  border-radius: 3px;
}
.task-log-row-body ul, .task-log-row-body ol {
  margin: 4px 0;
  padding-left: 20px;
}
.task-log-row-body li { margin: 2px 0; }
/* Markdown headings inside a task result — without these, ## headings
   render at browser-default huge sizes and blow out the cramped row. */
.task-log-row-body h1,
.task-log-row-body h2,
.task-log-row-body h3,
.task-log-row-body h4 {
  margin: 10px 0 4px;
  line-height: 1.3;
  font-weight: 650;
  color: var(--fg);
}
.task-log-row-body h1 { font-size: 14px; }
.task-log-row-body h2 { font-size: 13px; }
.task-log-row-body h3,
.task-log-row-body h4 { font-size: 12px; opacity: 0.9; }
.task-log-row-body h1:first-child,
.task-log-row-body h2:first-child,
.task-log-row-body h3:first-child { margin-top: 0; }
.task-log-row-body hr {
  border: none;
  border-top: 1px solid color-mix(in srgb, var(--fg) 12%, transparent);
  margin: 8px 0;
}
.task-log-row-body blockquote {
  margin: 4px 0;
  padding: 2px 0 2px 10px;
  border-left: 2px solid color-mix(in srgb, var(--fg) 20%, transparent);
  opacity: 0.85;
}
.task-log-row-body a { color: var(--accent, #60a5fa); text-decoration: none; }
.task-log-row-body a:hover { text-decoration: underline; }
.task-log-row-body strong { font-weight: 650; color: var(--fg); }
/* Email-summary tables — render compactly instead of default browser ugliness. */
.task-log-row-body table {
  border-collapse: collapse;
  width: 100%;
  margin: 6px 0;
  font-size: 11px;
}
.task-log-row-body th,
.task-log-row-body td {
  border: 1px solid color-mix(in srgb, var(--fg) 10%, transparent);
  padding: 3px 6px;
  text-align: left;
  vertical-align: top;
}
.task-log-row-body th {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  font-weight: 600;
}
.task-log-row-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 4px;
}
.task-log-open-chat,
.task-log-open-report,
.task-log-copy,
.task-log-clear-cache,
.task-log-run-again {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  background: none;
  border: 1px solid color-mix(in srgb, var(--fg) 14%, transparent);
  border-radius: 5px;
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  font-family: inherit;
  font-size: 10px;
  padding: 1px 6px;
  cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
  line-height: 1.4;
}
.task-log-open-chat:hover,
.task-log-open-report:hover,
.task-log-copy:hover,
.task-log-clear-cache:hover,
.task-log-run-again:hover {
  color: var(--fg);
  border-color: color-mix(in srgb, var(--fg) 30%, transparent);
  background: color-mix(in srgb, var(--fg) 5%, transparent);
}
.task-log-row-actions > .task-log-open-chat,
.task-log-row-actions > .task-log-copy {
  margin-left: auto;
}
.task-log-clear-cache svg {
  position: relative;
  top: 0;
}
/* Activity filter chips — toggle-out model: ON by default (solid),
   click to toggle OFF (dimmed + strikethrough) to hide that group. */
.tasks-af-chip {
  font-size: 11px;
  padding: 3px 10px;
  border: 1px solid color-mix(in srgb, var(--fg) 16%, transparent);
  border-radius: 12px;
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  color: var(--fg);
  cursor: pointer;
  font-family: inherit;
  transition: opacity .12s, background .12s, border-color .12s;
}
.tasks-af-chip:hover { border-color: color-mix(in srgb, var(--fg) 32%, transparent); }
.tasks-af-chip.off {
  opacity: 0.4;
  text-decoration: line-through;
  background: transparent;
}
.tasks-af-chip.active {
  border-color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  opacity: 1;
}
.tasks-af-chip-error { border-color: color-mix(in srgb, var(--red, #f87171) 45%, transparent); }
.tasks-af-chip-error:not(.off) { color: var(--red, #f87171); }

/* "Open in Deep Research" is now a regular clickable chat link (a
   `#research-<id>` markdown anchor in the assistant's message), rendered as
   an a.chat-link. Nudge it 4px right so it sits slightly inset. */
a.chat-link[href^="#research-"] {
  margin-left: 4px;
}
.task-log-row.is-long .task-log-row-body {
  max-height: 8.5em;
  overflow: hidden;
  position: relative;
  mask-image: linear-gradient(to bottom, #000 70%, transparent);
  -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent);
}
.task-log-row.is-long.expanded .task-log-row-body {
  max-height: none;
  overflow: visible;
  mask-image: none;
  -webkit-mask-image: none;
}
.task-log-row-toggle {
  margin-top: 6px;
  background: none;
  border: none;
  font-family: inherit;
  font-size: 11px;
  cursor: pointer;
  padding: 2px 0;
  color: transparent;          /* hide raw "Show more" text */
}
.task-log-row-toggle::before {
  color: color-mix(in srgb, var(--accent, var(--fg)) 80%, transparent);
  opacity: 0.8;
}
.task-log-row:not(.expanded) .task-log-row-toggle::before { content: 'Show more'; }
.task-log-row.expanded .task-log-row-toggle::before { content: 'Show less'; }
.task-log-row-toggle:hover::before { opacity: 1; }
.task-log-prompt {
  margin-top: 6px;
  font-size: 11px;
}
.task-log-prompt summary {
  cursor: pointer;
  opacity: 0.5;
  user-select: none;
}
.task-log-prompt summary:hover { opacity: 0.8; }
.task-log-prompt pre {
  margin: 4px 0 0;
  padding: 6px 8px;
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  border-radius: 4px;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 11px;
  opacity: 0.75;
  max-height: 200px;
  overflow: auto;
}

.task-card {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 12px;
  margin-bottom: 0;
  background: color-mix(in srgb, var(--fg) 2%, transparent);
  min-height: 47px;
  box-sizing: border-box;
  align-items: center;
}
/* When expanded, height grows to fit the detail panel. */
.task-card.expanded { align-items: flex-start; }
/* Nudge the card text up (dot/menu keep their own offsets); the leading icon
   and built-in tag ride a bit higher to sit on the title's cap line. */
.task-card .memory-item-title,
.task-card .memory-item-meta { position: relative; top: -4px; }
/* Always show the per-card ⋮ kebab on task cards (the default opacity:0 +
   hover-reveal is unreliable on touch and easy to miss on desktop).
   pointer-events / z-index belt-and-braces against any sibling overlay,
   margin reset undoes the global `.modal-body button { margin-top:6px }`
   that was punting the hit-target down. */
.task-card .memory-item-actions {
  opacity: 0.55;
  pointer-events: auto;
  position: relative;
  z-index: 5;
}
.task-card:hover .memory-item-actions,
.task-card .memory-item-actions:hover { opacity: 1; }
.task-card .memory-item-actions .memory-item-btn {
  margin: 0 !important;
  pointer-events: auto;
  position: relative;
  z-index: 5;
  width: 28px;
  min-width: 28px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Make the SVG inside the kebab transparent to pointer events so the click
   always lands on the BUTTON itself (some browsers / nested SVG events lose
   the click when hitting the inner glyph). */
.task-card .memory-item-actions .memory-item-btn svg { pointer-events: none; }
.task-card .task-builtin-badge { position: relative; top: -4px; }
.task-ai-mark {
  flex: 0 0 auto;
  color: var(--accent, var(--red));
  opacity: 0.78;
  position: relative;
  top: -4px;
}
/* Per-card select checkbox rides up to the title line. The "All" checkbox is
   #tasks-select-all (not .memory-select-cb), so it stays put. */
.task-card .memory-select-cb { position: relative; top: -4px; }
/* Bigger ⋮ dropdown on mobile (its buttons carry inline styles → !important). */
@media (max-width: 768px) {
  .task-dropdown { min-width: 160px !important; padding: 6px !important; }
  .task-dropdown button { font-size: 13px !important; padding: 10px 12px !important; gap: 10px !important; }
  .task-dropdown button svg { width: 15px !important; height: 15px !important; }
  .task-card .task-status-badge {
    padding-left: 5px;
    padding-right: 5px;
    gap: 2px;
    letter-spacing: 0.15px;
  }
  .task-card .task-state-badge {
    width: 20px;
    min-width: 20px;
    height: 20px;
    min-height: 20px;
    padding-left: 0;
    padding-right: 0;
    box-sizing: border-box;
    justify-content: center;
  }
  .task-card .task-state-badge .task-state-label {
    display: none;
  }
  .task-card .task-card-run-btn {
    margin-right: 1px !important;
    top: 0;
  }
}

.task-card-header {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
}

.task-card-name {
  font-size: 12px;
  font-weight: 600;
}

.task-card-schedule {
  font-size: 10.5px;
  opacity: 0.5;
  margin-left: auto;
}

.task-card-output {
  font-size: 10px;
  opacity: 0.45;
}

.task-card-prompt {
  font-size: 11px;
  opacity: 0.55;
  margin: 4px 0;
  line-height: 1.4;
}

.task-card-meta {
  font-size: 10.5px;
  opacity: 0.45;
  margin-bottom: 6px;
}

.task-card-actions {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.task-btn {
  font-family: inherit;
  font-size: 11px;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: none;
  color: var(--fg);
  cursor: pointer;
  opacity: 0.7;
  transition: opacity 0.15s, background 0.15s;
}
.task-btn:hover { opacity: 1; }
.task-btn:disabled { opacity: 0.3; cursor: not-allowed; }

.task-btn-primary {
  background: var(--red);
  color: #fff;
  border-color: transparent;
  opacity: 1;
}
.task-btn-primary:hover { opacity: 0.85; }

.task-btn-danger { color: var(--red); border-color: color-mix(in srgb, var(--red) 30%, transparent); }
.task-btn-danger:hover { opacity: 1; border-color: var(--red); }

.tasks-clock {
  font-size: 10px;
  opacity: 0.35;
  text-align: center;
  padding: 6px 0 8px;
}

.task-preset-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 8px;
}
.task-preset-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 12px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg); cursor: pointer; transition: all 0.15s; text-align: left;
  color: var(--fg);
}
.task-preset-card:hover {
  border-color: var(--accent, #cc6a3a); background: color-mix(in srgb, var(--accent, #cc6a3a) 8%, var(--bg));
}
.task-preset-icon { font-size: 20px; margin-bottom: 2px; }
.task-preset-label { font-size: 12px; font-weight: 600; }
.task-preset-desc { font-size: 10px; opacity: 0.5; line-height: 1.3; }

.task-form { display: flex; flex-direction: column; gap: 4px; }
.task-form-label {
  font-size: 11px;
  opacity: 0.55;
  font-weight: 500;
  display: block;
  /* Consistent rhythm: 8px above each label (section gap), 4px below to the
     field — same spacing for Name/Prompt/Trigger/Output/Model/Chain. */
  margin: 8px 0 4px;
}
.task-form-input {
  font-family: inherit;
  font-size: 11px;
  padding: 6px 8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  transition: border-color 0.15s;
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  min-width: 0;
}
/* A <select>'s closed width otherwise grows to its widest option (the long
   action descriptions), overflowing the modal — clip it to the field width. */
select.task-form-input { text-overflow: ellipsis; }
.task-form-input:focus {
  outline: none;
  border-color: var(--red);
}
.task-form-textarea { resize: vertical; min-height: 60px; }
.task-form-actions { display: flex; gap: 6px; justify-content: flex-end; margin-top: 8px; }

.task-form-toggle {
  display: flex; gap: 4px;
}
.task-toggle-btn {
  flex: 1; padding: 4px 8px; font-size: 10px; font-family: inherit;
  border: 1px solid var(--border); border-radius: 6px; background: transparent;
  color: var(--fg); opacity: 0.4; cursor: pointer; transition: all 0.15s;
}
.task-toggle-btn:hover { opacity: 0.7; border-color: var(--fg); }
.task-toggle-btn.active {
  opacity: 1; border-color: var(--red); color: var(--red);
  background: color-mix(in srgb, var(--red) 10%, transparent);
}

.task-time-picker, .task-date-picker {
  display: flex;
  align-items: center;
  gap: 4px;
}
.task-time-select, .task-date-select {
  width: auto;
  min-width: 48px;
  font-size: 11px;
}
.task-time-sep { opacity: 0.4; font-size: 11px; }

.task-history-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.task-runs-list { display: flex; flex-direction: column; gap: 6px; overflow-y: auto; }
.task-run-item {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
}
.task-run-item-header {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
}
.task-run-time { margin-left: auto; font-size: 10px; opacity: 0.45; }
.task-run-result {
  font-size: 11px;
  opacity: 0.6;
  margin-top: 4px;
  line-height: 1.4;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ── Settings Form Utilities ── */

.settings-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.settings-col {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.settings-label {
  font-size: 12px;
  min-width: 70px;
}
.settings-select {
  flex: 1;
  /* Allow the select to shrink below its longest option's width inside a flex
     row — without this a long model name (e.g. the Vision picker) overflowed
     the card off-screen on mobile. */
  min-width: 0;
  max-width: 100%;
  padding: 5px 8px;
  font-family: inherit;
  font-size: 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  transition: border-color 0.15s;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: textfield;
  outline: none;
}
input.settings-select::-webkit-outer-spin-button,
input.settings-select::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
input.settings-select::placeholder { color: color-mix(in srgb, var(--fg) 35%, transparent); }
.settings-select:focus {
  outline: none;
  border-color: var(--red);
}

/* Default-chat fallback chain editor. Each row mirrors the primary
   endpoint/model selectors, indented under them to read as a chain. */
.settings-fallbacks {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.settings-fallbacks:not(:empty) { margin-top: 2px; }
.settings-fallback-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-left: 12px;
  border-left: 2px solid color-mix(in srgb, var(--fg) 12%, transparent);
}
.settings-fallback-num {
  font-size: 11px;
  opacity: 0.4;
  min-width: 14px;
  text-align: right;
}
.settings-fallback-row .settings-select { flex: 1; min-width: 0; }
.settings-fallback-remove {
  flex-shrink: 0;
  margin-right: 4px;
  width: 22px;
  height: 22px;
  line-height: 1;
  font-size: 15px;
  /* Nudge the × glyph 5px left within the button (button size unchanged). */
  text-indent: -5px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: transparent;
  color: color-mix(in srgb, var(--fg) 55%, transparent);
  cursor: pointer;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
  position: relative;
  top: -6px;
}
.settings-fallback-remove:hover {
  border-color: var(--red);
  color: var(--red);
  background: color-mix(in srgb, var(--red) 10%, transparent);
}
.settings-fallback-add {
  align-self: flex-start;
  margin-top: 2px;
  font-size: 11px;
  padding: 3px 9px;
  border: 1px dashed var(--border);
  border-radius: 6px;
  background: transparent;
  color: color-mix(in srgb, var(--fg) 65%, transparent);
  cursor: pointer;
  transition: border-color 0.12s, color 0.12s;
}
.settings-fallback-add:hover {
  border-color: var(--red);
  color: var(--red);
}
.settings-input {
  flex: 1;
  padding: 5px 8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  font-family: inherit;
  font-size: 12px;
  transition: border-color 0.15s;
}
/* Hide the native number-input spinner arrows (e.g. SMTP/IMAP Port) — they
   render unstyled and ugly. Field still accepts only numbers. */
.settings-input[type="number"]::-webkit-outer-spin-button,
.settings-input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.settings-input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}
.settings-input:focus {
  outline: none;
  border-color: var(--red);
}
/* ── Contacts manager (Settings → Integrations → CardDAV) ── */
.contacts-add-row {
  display: flex;
  gap: 6px;
  margin-bottom: 8px;
  align-items: center;
}
.contacts-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 360px;
  overflow-y: auto;
}
.contact-row {
  padding: 6px 4px;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 35%, transparent);
}
.contact-row:last-child { border-bottom: none; }
.contact-row-edit { margin-top: 4px; }

/* ── Sort Dropdown ── */

.sort-dropdown {
  position: absolute;
  right: 0;
  top: 100%;
  z-index: 1000;
  min-width: 120px !important;
  width: max-content;
  padding: 4px !important;
  margin-top: 4px;
  background: var(--panel) !important;
  border: 1px solid var(--border) !important;
  border-radius: 8px !important;
  box-shadow: 0 4px 16px rgba(0,0,0,0.4) !important;
  backdrop-filter: none !important;
}
.sort-dropdown-item {
  cursor: pointer;
  padding: 6px 8px !important;
  font-size: 11px !important;
  border-radius: 6px !important;
  border-bottom: none !important;
  white-space: nowrap;
  transition: background 0.1s;
}
.sort-dropdown-item:hover {
  background: color-mix(in srgb, var(--accent) 10%, transparent) !important;
}
.sort-dropdown-sep {
  border-top: 1px solid var(--border);
  margin-top: 2px;
  padding-top: 6px;
}

/* Mobile: bigger taps for the chats sort/funnel menu and the select-mode
   bulk-action bar. Also drop the "Rearrange" item — it's a touch-finicky
   feature better left to desktop. */
@media (max-width: 768px) {
  /* Nudge the funnel/sort button right on mobile so it doesn't hug the
     chevron next to it. */
  #session-sort-btn { transform: translateX(11px); }
  #session-sort-dropdown.sort-dropdown {
    min-width: 200px !important;
    padding: 6px !important;
  }
  #session-sort-dropdown .sort-dropdown-item {
    padding: 12px 14px !important;
    font-size: 14px !important;
  }
  #session-rearrange-toggle { display: none !important; }

  /* Select-mode bar — make Archive / Delete / Cancel buttons + the
     Select-all toggle finger-sized. */
  .session-bulk-bar {
    padding: 8px 10px;
    font-size: 14px;
    gap: 10px;
  }
  .session-bulk-btn {
    padding: 10px;
    min-width: 44px;
    min-height: 44px;
  }
  .session-bulk-btn svg { width: 20px; height: 20px; }
  #session-select-all-dot { font-size: 22px !important; padding: 6px; }
  #session-select-all-label { font-size: 14px !important; padding: 4px; }
}


/* ── Admin: Built-in tools ── */
.admin-tool-category {
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 6px;
  overflow: hidden;
  transition: border-color 0.15s;
}
.admin-tool-category:hover {
  border-color: color-mix(in srgb, var(--fg) 20%, var(--border));
}
.admin-tool-cat-header {
  font-size: 12px;
  font-weight: 500;
  padding: 8px 10px;
  transition: background 0.15s;
}
.admin-tool-cat-header:hover {
  background: color-mix(in srgb, var(--fg) 4%, transparent);
}
.admin-tool-cat-body {
  border-top: 1px solid var(--border);
  padding: 4px 0;
}
.admin-tool-cat-body.hidden {
  display: none;
}
.admin-tool-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  padding-right: 28px; /* align toggles with header toggle (chevron 12px + gap 6px + padding) */
  transition: background 0.08s;
}
.admin-tool-row:hover {
  background: color-mix(in srgb, var(--fg) 4%, transparent);
}
.admin-tool-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.admin-tool-name {
  font-size: 11px;
  font-weight: 500;
}
.admin-tool-desc {
  font-size: 10px;
  opacity: 0.4;
}
.admin-tool-ctx {
  font-size: 9px;
  opacity: 0.3;
  flex-shrink: 0;
  min-width: 35px;
  text-align: right;
  font-family: 'Berkeley Mono', 'SF Mono', 'Fira Code', monospace;
}

/* Cookbook serve param hints */
.hwfit-hint {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  color: var(--fg);
  font-size: 8px;
  font-weight: 600;
  opacity: 0.35;
  cursor: help;
  margin-left: 2px;
  vertical-align: middle;
  transition: opacity 0.1s;
}
.hwfit-hint:hover {
  opacity: 0.8;
  background: color-mix(in srgb, var(--accent) 15%, transparent);
}
.hwfit-dl-dot {
  color: var(--green);
  font-size: 8px;
  margin-left: 4px;
  opacity: 0.7;
}
.hwfit-parser-tag {
  font-size: 9px;
  opacity: 0.4;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  padding: 1px 5px;
  border-radius: 3px;
  margin-left: 4px;
  font-family: 'Berkeley Mono', 'SF Mono', 'Fira Code', monospace;
}

/* Cookbook output copy — show only on hover */
.cookbook-output-wrap .cookbook-output-copy {
  opacity: 0;
  transition: opacity 0.1s;
}
.cookbook-output-wrap:hover .cookbook-output-copy {
  opacity: 0.5;
}
.cookbook-output-wrap .cookbook-output-copy:hover {
  opacity: 1;
}
#cookbook-dl-btn-search {
  position: relative;
  top: -4px;
  padding-top: 0;
  padding-bottom: 0;
  height: 28px;
}

/* Cached model menu button */
.hwfit-cached-menu-btn {
  background: none;
  border: none;
  color: var(--fg);
  font-size: 16px;
  cursor: pointer;
  opacity: 0;
  padding: 2px 4px;
  border-radius: 4px;
  transition: opacity 0.1s;
  flex-shrink: 0;
  position: relative;
  top: -2px;
}
.memory-item:hover .hwfit-cached-menu-btn,
.hwfit-cached-item:hover .hwfit-cached-menu-btn { opacity: 0.4; }
.hwfit-cached-menu-btn:hover { opacity: 1 !important; }
.hwfit-cached-menu-btn svg { pointer-events: none; }
@media (max-width: 768px) {
  #cookbook-modal .hwfit-cached-menu-btn {
    opacity: 0.72 !important;
    width: 32px;
    height: 32px;
    min-width: 32px;
    padding: 0;
    justify-content: center;
    top: -4px;
  }
  #cookbook-modal .hwfit-cached-menu-btn:active {
    opacity: 1 !important;
    background: color-mix(in srgb, var(--fg) 9%, transparent);
  }
}

/* Quick run button */
.cookbook-run-btn {
  background: var(--accent, var(--red)) !important;
  color: var(--panel) !important;
  border-color: var(--accent, var(--red)) !important;
  font-weight: 600;
}
.cookbook-run-btn:hover {
  opacity: 0.9;
}
#hwfit-cache-scan {
  position: relative;
  top: 1px;
  width: 51px;
}
#serve-search {
  height: 32px;
}
#cookbook-dl-btn {
  position: relative;
  top: -4px;
}

/* Cookbook model directory tags */
.cookbook-modeldir-tag {
  font-size: 11px;
  padding: 3px 8px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  color: var(--fg);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: 'Berkeley Mono', 'SF Mono', monospace;
}
.cookbook-modeldir-default {
  opacity: 0.5;
}
/* Default-server radio-check in a Settings server title (same look as the
   model-dir download target). Active = accent-tinted. */
.cookbook-srv-default {
  cursor: pointer;
  opacity: 0.35;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  line-height: 1;
  transition: opacity 0.12s, color 0.12s;
}
.cookbook-srv-default:hover { opacity: 0.8; }
.cookbook-srv-default.active { opacity: 1; color: var(--accent, var(--red)); }
.cookbook-srv-default-label { font-size: 10px; font-weight: 600; letter-spacing: 0.02em; }
/* Download-target toggle inside a model-dir tag */
.cookbook-modeldir-dl {
  cursor: pointer;
  opacity: 0.35;
  display: inline-flex;
  align-items: center;
  line-height: 0;
  transition: opacity 0.12s, color 0.12s;
}
.cookbook-modeldir-dl:hover { opacity: 0.8; }
.cookbook-modeldir-dl.active { opacity: 1; color: var(--accent, var(--red)); }
/* The tag currently flagged as the download target — clearly highlighted so
   it's obvious where downloads land. */
.cookbook-modeldir-target {
  opacity: 1;
  color: var(--accent, var(--red));
  font-weight: 600;
  background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent, var(--red)) 60%, transparent);
}
.cookbook-modeldir-rm {
  cursor: pointer;
  opacity: 0.4;
  font-size: 10px;
}
.cookbook-modeldir-rm:hover {
  opacity: 1;
}
.cookbook-modeldir-add {
  background: none;
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg);
  font-size: 10px;
  font-weight: 500;
  height: 19.5px;
  box-sizing: border-box;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  position: relative;
  top: -3px;
  padding: 0 9px;
  transition: border-color 0.12s, background 0.12s;
}
.cookbook-modeldir-add:hover { border-color: var(--accent, var(--red)); background: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent); }

/* Cookbook serve optimizations */
.hwfit-serve-opts {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0;
}
.hwfit-apply-opts {
  font-size: 11px !important;
  min-width: auto !important;
  white-space: nowrap;
}
.hwfit-opts-desc {
  font-size: 9px;
  opacity: 0.4;
  font-style: italic;
}

/* Library modal tabs */
.lib-tabs,
.admin-tabs {
  display: flex;
  gap: 0;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--border);
}
.lib-tab,
.admin-tab {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--fg-muted);
  font-size: 12px;
  font-family: inherit;
  padding: 6px 14px;
  cursor: pointer;
  transition: color 0.1s, border-color 0.1s;
}
.lib-tab:hover,
.admin-tab:hover { color: var(--fg); }
.lib-tab.active,
.admin-tab.active {
  color: var(--accent, var(--red));
  border-bottom-color: var(--accent, var(--red));
}

/* Cookbook tab count badge */
.cookbook-tab-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--accent, var(--red)) 20%, transparent);
  color: var(--accent, var(--red));
  font-size: 9px;
  font-weight: 700;
  margin-left: 4px;
  line-height: 1;
}

/* ── Generate Visual Report button (deep research) ── */
.view-report-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 12px;
  padding: 8px 16px;
  border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--red) 10%, transparent);
  color: var(--fg);
  font-family: inherit;
  font-size: 0.88em;
  font-weight: 500;
  transition: background 0.15s ease;
  cursor: pointer;
}
.view-report-btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--red) 18%, transparent);
}
.view-report-btn svg {
  flex-shrink: 0;
}
.report-btn-wrap {
  margin-top: 12px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.view-report-btn.chat-about-btn {
  border-color: color-mix(in srgb, var(--fg) 22%, transparent);
  background: color-mix(in srgb, var(--fg) 6%, transparent);
}
.view-report-btn.chat-about-btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--fg) 12%, transparent);
}
.view-report-btn:disabled {
  opacity: 0.6;
  cursor: progress;
}
.report-spinner {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.88em;
  color: var(--fg);
  opacity: 0.7;
}
.report-spinner-text {
  font-weight: 500;
}
.report-spinner-dots::after {
  content: '';
  animation: report-dots 1.4s steps(4, end) infinite;
}
@keyframes report-dots {
  0%   { content: ''; }
  25%  { content: '.'; }
  50%  { content: '..'; }
  75%  { content: '...'; }
  100% { content: ''; }
}

/* ── Continue research hint ────────────────────────── */
.continue-research-wrap {
  margin-top: 8px;
}
.continue-research-hint {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78em;
  color: var(--fg-dim, var(--fg));
  opacity: 0.5;
  padding: 4px 0;
}
.continue-research-hint svg {
  flex-shrink: 0;
  opacity: 0.6;
}

/* ── Research reconnect & timer ────────────────────── */
.msg.research-reconnect {
  border-left: 3px solid var(--red);
  background: color-mix(in srgb, var(--red) 5%, transparent);
}
.research-timer {
  font-size: 0.8em;
  opacity: 0.6;
  margin-top: 4px;
  font-family: monospace;
  color: var(--fg-dim, var(--fg));
}

/* ── Research synapse visualization ───────────────────────────────
   Live SVG graph of an in-flight deep-research run. The query is the
   central node; sub-questions branch off per round; sources are leaf
   nodes that pop in as they're captured. Pulses indicate activity. */
.research-synapse {
  margin: 6px 0 4px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background:
    radial-gradient(ellipse at center, color-mix(in srgb, var(--accent, var(--red)) 10%, transparent) 0%, transparent 70%),
    color-mix(in srgb, var(--panel) 50%, var(--bg));
  overflow: hidden;
}
.research-synapse .rs-stage {
  height: 200px;
  position: relative;
}
.research-synapse-compact .rs-stage { height: 130px; }
.research-synapse-compact .rs-meta { padding: 4px 8px 5px; font-size: 10px; }
.research-synapse-compact .rs-label-sub { font-size: 8px; }
.research-synapse svg {
  display: block;
  width: 100%;
  height: 100%;
}
.research-synapse .rs-edge {
  stroke: var(--border);
  stroke-width: 1.2;
  fill: none;
  opacity: 0.55;
}
.research-synapse .rs-edge.rs-edge-firing {
  stroke: var(--accent, var(--red));
  stroke-width: 2;
  opacity: 1;
  filter: drop-shadow(0 0 4px var(--accent, var(--red)));
  animation: rs-fire 1.1s ease-out;
}
@keyframes rs-fire {
  0%   { stroke-dasharray: 4 200; stroke-dashoffset: 200; opacity: 0; }
  20%  { opacity: 1; }
  100% { stroke-dasharray: 200 4; stroke-dashoffset: -200; opacity: 0.55; }
}
.research-synapse .rs-node {
  fill: var(--bg);
  stroke: var(--accent, var(--red));
  stroke-width: 1.5;
  transition: all 0.3s ease;
  transform-box: fill-box;
  transform-origin: center;
}
.research-synapse .rs-node-root {
  fill: var(--accent, var(--red));
  stroke: var(--accent, var(--red));
}
/* Sub & leaf nodes stay inside the accent palette so the whole graph
   reads as one organism. Leaves are softer/lower-contrast dots that
   float around their sub — not green-on-stem (which read as palm fronds). */
.research-synapse .rs-node-sub {
  stroke: color-mix(in srgb, var(--accent, var(--red)) 70%, var(--fg));
}
.research-synapse .rs-node-leaf {
  stroke: color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
  fill: color-mix(in srgb, var(--accent, var(--red)) 22%, var(--bg));
}
.research-synapse .rs-node-new {
  animation: rs-pop 0.6s ease-out;
}
@keyframes rs-pop {
  0%   { transform: scale(0); opacity: 0; }
  60%  { transform: scale(1.25); opacity: 1; }
  100% { opacity: 1; }
}
.research-synapse .rs-pulse {
  fill: var(--accent, var(--red));
  opacity: 0;
  animation: rs-pulse 2.6s ease-out infinite;
  transform-box: fill-box;
  transform-origin: center;
}
@keyframes rs-pulse {
  0%   { transform: scale(1); opacity: 0.65; }
  100% { transform: scale(5); opacity: 0; }
}
.research-synapse .rs-label {
  fill: var(--fg);
  font-size: 10px;
  font-family: ui-monospace, "JetBrains Mono", monospace;
  pointer-events: none;
  opacity: 0.85;
}
.research-synapse .rs-label-sub {
  font-size: 9px;
  opacity: 0.7;
}
.research-synapse .rs-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  padding: 6px 10px 8px;
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11px;
  color: var(--fg-dim, var(--fg));
  opacity: 0.85;
  border-top: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
}
.research-synapse .rs-meta .rs-status {
  color: var(--accent, var(--red));
  font-weight: 600;
}
.research-synapse .rs-meta b {
  color: var(--fg);
  font-weight: 600;
}
.research-synapse .rs-meta .rs-sep { opacity: 0.4; }
.research-synapse.rs-complete .rs-pulse { animation: none; opacity: 0; }
.research-synapse.rs-complete .rs-node-root {
  fill: var(--color-success, #4caf50);
  stroke: var(--color-success, #4caf50);
}
.research-synapse.rs-complete .rs-meta .rs-status { color: var(--color-success, #4caf50); }
.research-synapse.rs-error .rs-meta .rs-status { color: var(--red); }

/* ── Raw findings items (inside sources-style box) ── */
.finding-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.finding-item .source-link {
  border-radius: 6px 6px 0 0;
}
.finding-summary {
  padding: 6px 8px 8px;
  font-size: 0.82em;
  line-height: 1.5;
  color: var(--fg-dim, var(--fg));
  opacity: 0.85;
  border-bottom: 1px solid color-mix(in srgb, var(--fg) 6%, transparent);
  margin-bottom: 4px;
  white-space: pre-wrap;
  word-break: break-word;
}
.finding-item:last-child .finding-summary {
  border-bottom: none;
  margin-bottom: 0;
}

/* ═══════════════════════════════════════════════════════════
   Gallery Editor
   ═══════════════════════════════════════════════════════════ */

/* Tab bar */
.gallery-tabs {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--border);
  padding: 0 16px;
  background: var(--panel);
}
.gallery-tab {
  padding: 8px 18px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--fg);
  opacity: 0.6;
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  transition: opacity 0.15s, border-color 0.15s;
}
.gallery-tab:hover { opacity: 0.85; }
.gallery-tab.active {
  opacity: 1;
  border-bottom-color: var(--red);
}
/* Icon + label layout inside each tab. */
.gallery-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.gallery-tab-icon {
  display: inline-flex;
  align-items: center;
  opacity: 0.85;
}
.gallery-tab.active .gallery-tab-icon { opacity: 1; }
/* Close × on the Edit tab — appears on hover. */
.gallery-tab-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  margin-left: 2px;
  border-radius: 4px;
  opacity: 0;
  transition: opacity 0.12s, background 0.12s;
  /* Use a slightly larger glyph at a larger line-height so the × sits
     visually centered — the U+00D7 character has uneven em-box metrics. */
  font-size: 16px;
  line-height: 16px;
  font-weight: 500;
  cursor: pointer;
  padding-bottom: 2px;
  box-sizing: border-box;
}
.gallery-tab:hover .gallery-tab-close,
.gallery-tab.active .gallery-tab-close { opacity: 0.65; }
/* Hide the close × entirely on the Edit tab when there's no edit open. */
.gallery-tab[data-tab="editor"]:not(.has-edit) .gallery-tab-close { display: none !important; }
.gallery-tab-close:hover { opacity: 1 !important; background: color-mix(in srgb, var(--red) 25%, transparent); color: var(--red); }
/* Inline rename input shown when the Edit tab is double-clicked. */
.gallery-tab-rename-input {
  background: var(--bg);
  border: 1px solid var(--red);
  border-radius: 3px;
  color: var(--fg);
  font: inherit;
  font-size: 13px;
  padding: 1px 4px;
  width: 140px;
  outline: none;
}

/* Albums tab — grid of album cards with cover thumbnails. */
.gallery-albums-container {
  padding: 12px 4px;
  max-height: 70vh;
  overflow-y: auto;
  border: 2px dashed transparent;
  border-radius: 8px;
  transition: border-color 0.15s, background 0.15s;
}
.gallery-settings-container {
  padding: 8px 4px;
  max-height: 72vh;
  overflow-y: auto;
}
.gallery-albums-container.gallery-dragover {
  border-color: var(--red);
  background: color-mix(in srgb, var(--red) 5%, transparent);
}
.gallery-albums-empty {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 48px 16px;
  opacity: 0.7;
  font-size: 13px;
}
.gallery-albums-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
}
.gallery-album-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  cursor: pointer;
  transition: transform 0.12s, border-color 0.12s, box-shadow 0.12s;
}
.gallery-album-card:hover {
  transform: translateY(-2px);
  border-color: var(--red);
  box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 12%, transparent);
}
.gallery-album-menu-btn {
  position: absolute;
  top: 6px; right: 6px;
  width: 24px; height: 24px;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  color: var(--fg);
  border: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
  border-radius: 6px;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s, background 0.12s;
  z-index: 2;
  backdrop-filter: blur(4px);
  /* Plain "…" text — the previous SVG was rendering as a flat black
     block on some themes when its currentColor didn't contrast. */
  font-size: 16px;
  line-height: 1;
  font-weight: 600;
  padding: 0 0 4px;
}
.gallery-album-card:hover .gallery-album-menu-btn,
.gallery-album-menu-btn:focus-visible { opacity: 1; }
.gallery-album-menu-btn:hover { background: var(--bg); }
.gallery-album-menu-pop {
  position: absolute;
  top: 34px; right: 6px;
  min-width: 140px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 18%, transparent);
  display: flex; flex-direction: column;
  padding: 4px;
  z-index: 3;
}
.gallery-album-menu-pop[hidden] { display: none; }
.gallery-album-cover {
  aspect-ratio: 4 / 3;
  background: color-mix(in srgb, var(--fg) 5%, var(--bg));
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.gallery-album-cover img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.gallery-album-placeholder {
  opacity: 0.4;
  font-size: 40px;
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
}
.gallery-album-info {
  padding: 8px 10px;
  display: flex; flex-direction: column; gap: 2px;
}
.gallery-album-name {
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.gallery-album-count {
  font-size: 11px;
  opacity: 0.55;
}
.gallery-album-card-add {
  border-style: dashed;
  opacity: 0.7;
}
.gallery-album-card-add:hover { opacity: 1; }

/* Edit-tab empty state — shown before any image is loaded. */
.gallery-editor-landing {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px;
  padding: 64px 16px;
  flex: 1;
  text-align: center;
  color: var(--fg);
}
.gallery-editor-landing h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
}
/* "vision model" link in the AI-tagging description → opens Settings. */
.ge-vision-link {
  color: var(--accent, var(--red));
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.ge-vision-link:hover { opacity: 0.8; }
.ge-alpha-tag {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  vertical-align: middle;
  padding: 1px 5px;
  border-radius: 4px;
  color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--accent, var(--red)) 15%, transparent);
  position: relative;
  top: -1px;
}
.gallery-editor-landing p {
  margin: 0;
  opacity: 0.6;
  font-size: 13px;
  max-width: 320px;
}
.gallery-editor-landing-actions {
  display: flex; gap: 10px;
  margin-top: 8px;
}
/* Bigger primary action buttons specifically inside the editor landing —
   the gallery-wide .gallery-select-btn is a compact toolbar style which is
   too small for the empty-state hero. */
.gallery-editor-landing-actions .gallery-select-btn {
  padding: 7px 22px 17px;
  font-size: 13px;
  opacity: 0.85;
}
.gallery-editor-landing-actions .gallery-select-btn:hover { opacity: 1; }
/* Template picker — native <select> so it renders reliably across browsers
   without fighting our custom flex layouts. */
.gallery-editor-template-label {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  margin-top: 18px;
  font-size: 11px;
  opacity: 0.55;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.gallery-editor-template-select {
  min-width: 240px;
  padding: 8px 12px;
  background: var(--panel);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
  font-size: 13px;
  text-transform: none;
  letter-spacing: normal;
  cursor: pointer;
  opacity: 1;
}
.gallery-editor-template-select:hover { border-color: var(--red); }
.gallery-editor-template-select:focus { outline: none; border-color: var(--red); }
/* Saved-drafts grid on the editor landing. Surfaces every persisted
   in-progress project so the user can resume without re-opening the
   original photo. Each card: thumbnail + name + last-saved hint + ×. */
.gallery-editor-drafts {
  width: min(720px, 92%);
  margin: 28px auto 0;   /* centered */
  padding-top: 18px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.gallery-editor-drafts {
  position: relative;
}
.gallery-editor-drafts-loading {
  position: absolute;
  inset: 30px 0 0 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--panel);
  border-radius: 6px;
  min-height: 80px;
}
.gallery-editor-drafts-header {
  display: flex;
  align-items: center;
  justify-content: center;   /* center the title / search / select */
  gap: 8px;
  margin-bottom: 8px;
}
/* In select mode the bulk bar drops in below the header — pull it up. */
#gallery-editor-drafts-bulk { margin-top: -12px; }
.gallery-editor-drafts-title {
  margin: 0;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  opacity: 0.55;
  font-weight: 600;
  text-align: left;
  flex-shrink: 0;
}
.gallery-editor-drafts-search {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 280px;
  height: 26px;          /* same thickness as the Select button */
  box-sizing: border-box;
  padding: 0 8px;
  font-size: 12px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
}
.gallery-editor-drafts-search:focus { outline: none; border-color: var(--red); }
/* The shared .gallery-select-btn has a -4px top margin + tall asymmetric padding
   meant for the main gallery toolbar; in this header it makes the button a
   different height / offset from the search box. Normalize so it lines up. */
.gallery-editor-drafts-header .gallery-select-btn {
  margin-top: 0 !important;
  height: 26px;          /* match the search bar */
  box-sizing: border-box;
  padding: 0 10px;
  font-size: 12px;
  line-height: 1;
  flex-shrink: 0;
}
#gallery-editor-drafts-select { margin-left: auto; flex-shrink: 0; }
#gallery-editor-drafts-select.active {
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  border-color: var(--accent, var(--red));
  color: var(--accent, var(--red));
}
.gallery-editor-draft-card.selected {
  outline: 2px solid var(--accent, var(--red));
  outline-offset: -2px;
}
.gallery-editor-draft-card.select-mode { cursor: pointer; }
/* Graceful exit when a project card is deleted — fade + shrink before re-render. */
.gallery-editor-draft-card.gallery-draft-removing {
  opacity: 0;
  transform: scale(0.92);
  transition: opacity 0.24s ease, transform 0.24s ease;
  pointer-events: none;
}
/* Draft select dot uses the standard small .gallery-select-dot look (same as the
   photo grid) — no oversized white-ring checkbox. */
.gallery-editor-drafts-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
}
.gallery-editor-draft-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: color-mix(in srgb, var(--panel) 70%, transparent);
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s, transform 0.15s;
  text-align: left;
}
.gallery-editor-draft-card:hover {
  border-color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--panel) 90%, transparent);
}
.gallery-editor-draft-card:focus-visible {
  outline: 2px solid var(--accent, var(--red));
  outline-offset: 2px;
}
.gallery-editor-draft-thumb {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 4px;
  background: var(--bg);
  display: block;
}
.gallery-editor-draft-thumb-empty {
  background: repeating-linear-gradient(
    45deg,
    color-mix(in srgb, var(--fg) 4%, transparent),
    color-mix(in srgb, var(--fg) 4%, transparent) 6px,
    transparent 6px,
    transparent 12px
  );
}
.gallery-editor-draft-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.gallery-editor-draft-name {
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gallery-editor-draft-meta {
  font-size: 10px;
  opacity: 0.55;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gallery-editor-draft-delete {
  position: absolute;
  top: -2px;
  right: 8px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 14px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s, background 0.12s;
}
.gallery-editor-draft-card:hover .gallery-editor-draft-delete { opacity: 1; }
.gallery-editor-draft-delete:hover { background: var(--red); }
/* Editor layout */
.gallery-editor {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  height: 100%;
  overflow: hidden;
  /* Prevent the editor's own scroll/touch from chaining out to the
     gallery modal or the page body — critical on mobile so touching
     the canvas doesn't slide the whole page. */
  overscroll-behavior: contain;
  touch-action: pan-y;
}
/* Pin the topbar to the top of the editor so its action buttons remain
   visible if the gallery modal body itself ends up scrolling. */
.ge-topbar { position: sticky; top: 0; z-index: 5; }
.ge-topbar.ge-topbar-menu-open { z-index: 10006; }

/* Top bar */
.ge-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 10px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  gap: 8px;
}
.ge-topbar-left, .ge-topbar-right {
  display: flex;
  align-items: center;
  gap: 4px;
}
/* "ALPHA" badge — flags the editor as in-development. */
.ge-alpha-badge {
  flex-shrink: 0;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.1em;
  line-height: 1;
  padding: 3px 6px;
  margin-right: 4px;
  border-radius: 4px;
  color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 40%, transparent);
  text-transform: uppercase;
  user-select: none;
  cursor: default;
}
@media (max-width: 768px) {
  /* Keep the toolbar tight on mobile — badge stays but shrinks. */
  .ge-alpha-badge { font-size: 8px; padding: 2px 5px; margin-right: 2px; }
}
.ge-diffusion-status {
  font-size: 11px;
  cursor: pointer;
  padding: 2px 8px;
  border-radius: 4px;
  transition: all 0.15s;
  white-space: nowrap;
}
.ge-diffusion-status.online {
  color: var(--color-success, #4ade80);
}
.ge-diffusion-status.offline {
  color: var(--color-error, #ef4444);
  opacity: 0.7;
}
.ge-diffusion-status.offline:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--color-error) 10%, transparent);
}
.ge-topbar-fill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--accent, var(--red));
  border-color: color-mix(in srgb, var(--accent, var(--red)) 50%, var(--border));
  background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
}
.ge-topbar-fill:hover {
  background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
  border-color: var(--accent, var(--red));
}
/* Mask-color swatch in the topbar — small label + circular swatch so
   the user can pick a contrasting overlay colour from anywhere. */
.ge-topbar-mask-color-wrap {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
}
.ge-topbar-mask-color-label {
  font-size: 10px;
  opacity: 0.6;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.ge-topbar-mask-color {
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
}
.ge-topbar-mask-color.cp-swatch-input {
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  border-radius: 50%;
  padding: 0;
}
.ge-topbar-sep {
  width: 1px;
  height: 16px;
  background: var(--border);
  margin: 0 4px;
}

/* Editor body (toolbar + canvas + panel) */
.ge-editor-body {
  display: flex;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  position: relative;
}
.gallery-editor-container {
  flex: 1;
  min-height: 0;
  display: none;
}

/* Toolbar (left) */
.ge-toolbar {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* Asymmetric horizontal padding shifts the whole column 4 px left —
     buttons, hover backgrounds, active highlight pills, and section
     separators all move together. */
  padding: 8px 8px 8px 0;
  background: var(--panel);
  border-right: 1px solid var(--border);
  width: 56px;
  flex-shrink: 0;
}
.ge-tool-sep {
  font-size: 8px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-muted);
  text-align: center;
  padding: 8px 0 4px;
  border-top: 1px solid var(--border);
  margin-top: 4px;
  opacity: 0.6;
  flex-shrink: 0;
}
/* All tool buttons share a fixed height so the column reads as a clean
   grid. Long labels (e.g. "Remove BG") truncate with ellipsis instead of
   wrapping to two lines and making one button taller than the others. */
.ge-tool-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 0 2px;
  height: 42px;
  flex-shrink: 0;
  border: none;
  background: none;
  color: var(--fg);
  opacity: 0.6;
  cursor: pointer;
  border-radius: 6px;
  transition: background 0.15s, opacity 0.15s;
}
.ge-tool-btn .ge-tool-label {
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ge-tool-btn { position: relative; }
/* Hover background is now a pseudo so we can shift it 2 px left
   independently from the button's own bounds (which contain the icon
   and label at their already-shifted positions). */
.ge-tool-btn:hover { opacity: 0.85; }
.ge-tool-btn:hover::after {
  content: '';
  position: absolute;
  inset: 0 2px 0 -2px;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  border-radius: 6px;
  z-index: -1;
  pointer-events: none;
}
.ge-tool-btn.active {
  opacity: 1;
  color: var(--red);
  background: none;
  /* Padding stays the same as inactive buttons so the icon stays
     horizontally aligned with its neighbors. The taller highlight is
     drawn by ::before so it can extend up/down without pushing the
     button or shifting the icon. */
}
.ge-tool-btn.active::before {
  content: '';
  position: absolute;
  /* Highlight pill shifted 2 px left of the button bounds so it sits
     under the icon + label (which are also nudged left). Same total
     width as before — just slid. */
  inset: 0 2px 0 -2px;
  background: color-mix(in srgb, var(--red) 18%, transparent);
  border-radius: 6px;
  z-index: 0;
  pointer-events: none;
}
/* Tool button contents stay above the active highlight pseudo. Also
   shifted an additional 2 px left so the icon + label sit further inside
   the highlight pill (which still anchors to the button bounds). */
.ge-tool-btn > * { position: relative; z-index: 1; left: -2px; }
.ge-tool-icon { font-size: 18px; line-height: 1; }
.ge-tool-label { font-size: 9px; line-height: 1.25; }
/* AI badge — same ✦ glyph the Enhance tool uses, pinned to the top-
   left of any tool button marked `ai: true`. Same color as the icon
   itself (inherits foreground) so it reads as part of the tool, not
   a flag. */
.ge-tool-ai {
  position: absolute !important;
  top: 1px;
  z-index: 2;
  color: inherit;
  opacity: 0.7;
  pointer-events: none;
  font-size: 11px;
  line-height: 1;
  font-weight: 700;
  left: 3px !important;
}
.ge-tool-btn.is-ai:hover .ge-tool-ai { opacity: 0.95; }
.ge-tool-btn.is-ai.active .ge-tool-ai { opacity: 1; }
/* Inline ✦ marker used on AI action buttons (Generate, Remove, etc.)
   so they read as AI-backed at a glance — same glyph as .ge-tool-ai
   in the toolbar, just sitting inline next to the label. */
.ge-btn-ai-mark {
  display: inline-block;
  font-size: 11px;
  line-height: 1;
  font-weight: 700;
  opacity: 0.75;
  margin-right: 1px;
}
.ge-btn-ai:hover .ge-btn-ai-mark { opacity: 1; }

/* Per-tool clear-selection badge — shows a tiny X in the top-right of
   the Lasso / Wand button when each holds a selection. Click clears
   the selection without switching tools (handler stops propagation). */
.ge-tool-clear {
  position: absolute !important;
  top: 3px;
  right: 3px;
  width: 14px;
  height: 14px;
  display: none;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  color: #fff;
  background: var(--red);
  cursor: pointer;
  opacity: 0.95;
  z-index: 2;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--red) 70%, transparent),
              0 1px 4px color-mix(in srgb, var(--red) 50%, transparent);
  transition: opacity 0.12s, transform 0.12s, box-shadow 0.12s;
  left: auto !important;
}
.ge-tool-btn.has-selection .ge-tool-clear { display: inline-flex; }
.ge-tool-clear:hover {
  opacity: 1;
  transform: scale(1.15);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--red) 80%, transparent),
              0 2px 6px color-mix(in srgb, var(--red) 60%, transparent);
}

/* Aspect-ratio placeholder shown while a draft is loading. */
.ge-canvas-placeholder {
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  border: 1px dashed color-mix(in srgb, var(--fg) 20%, transparent);
  border-radius: 4px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1;
}
/* Canvas area (center) */
.ge-canvas-area {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: auto;
  background: var(--bg);
  position: relative;
  min-width: 0;
  /* Stop touch interactions inside the editor from dragging the whole
     page (or the gallery modal). Touches on the canvas are owned by the
     editor's drawing logic — no native pan/zoom. */
  overscroll-behavior: contain;
}
.ge-main-canvas {
  touch-action: none;
}
.ge-wand-loading {
  position: absolute;
  inset: 0;
  z-index: 25;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--bg) 30%, transparent);
  pointer-events: none;
}
/* Loading overlay shown while a large image is decoding — centered
   whirlpool + "Loading" caption over a translucent dim. */
.ge-loading-overlay {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  pointer-events: none;
}
/* Full-editor cover (project load): above the toolbar/panel and near-opaque so
   the top toolbar/old content doesn't peek through while loading. */
.ge-loading-overlay-full {
  z-index: 200;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  pointer-events: auto;
}
.ge-loading-overlay .ge-loading-inner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border-radius: 10px;
  background: var(--panel);
  border: 1px solid var(--border);
  box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 18%, transparent);
}
.ge-loading-overlay .ge-loading-text {
  font-size: 12px;
  opacity: 0.8;
}

/* Shortcuts cheatsheet (toggled by `?` or the keyboard icon in the top bar). */
#ge-shortcuts-overlay {
  position: absolute;
  inset: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
}
#ge-shortcuts-overlay[hidden] { display: none; }
.ge-shortcuts-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 18px 12px;
  box-shadow: 0 8px 30px color-mix(in srgb, var(--fg) 25%, transparent);
  width: min(720px, 92vw);
  max-height: 86vh;
  overflow-y: auto;
  color: var(--fg);
}
.ge-shortcuts-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
  font-weight: 600;
  font-size: 13px;
}
.ge-shortcuts-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px 24px;
}
@media (min-width: 720px) {
  .ge-shortcuts-grid { grid-template-columns: repeat(4, 1fr); }
}
.ge-shortcuts-col h5 {
  margin: 0 0 6px;
  font-size: 11px;
  opacity: 0.6;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
}
/* Each shortcut row: key chips + description. The key chips are usually
   chained with "+" so we use a small horizontal gap between siblings,
   plus a larger left margin on the trailing text via the kbd:last-of-type
   sibling rule — keeps "Ctrl+Shift+D Deselect" readable instead of glued. */
.ge-shortcuts-col > div {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
  padding: 4px 0;
  opacity: 0.85;
  line-height: 1.5;
}
/* Push the description text away from the last kbd by adding margin
   to any text node that follows a kbd. Flex+wrap handles wrap on narrow. */
.ge-shortcuts-col > div kbd + kbd { margin-left: 0; }
.ge-shortcuts-col > div kbd:last-of-type { margin-right: 6px; }
.ge-shortcuts-card kbd,
#ge-shortcuts-popover kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px;
  font-weight: 600;
  padding: 2px 6px;
  border: 1px solid color-mix(in srgb, var(--accent, #cc6a3a) 55%, transparent);
  border-bottom-width: 2px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--accent, #cc6a3a) 14%, transparent);
  color: var(--accent, #cc6a3a);
  min-width: 18px;
  line-height: 1.4;
}
.ge-shortcuts-foot {
  margin-top: 12px;
  font-size: 11px;
  opacity: 0.55;
  text-align: center;
}
.ge-main-canvas {
  /* Cursor is managed entirely by JS so each tool can show its own
     icon (move arrow, circle overlay, etc.) without CSS fighting it. */
  image-rendering: pixelated;
  box-shadow: 0 2px 12px rgba(0,0,0,0.3);
  background: repeating-conic-gradient(#808080 0% 25%, #a0a0a0 0% 50%) 50% / 16px 16px;
}

/* Transform-overlay canvas — sits over the main canvas with extra
   margin so resize / rotation handles can render OUTSIDE the image
   bounds. Pointer events disabled so it doesn't intercept clicks. */
.ge-transform-overlay {
  position: absolute;
  image-rendering: pixelated;
  pointer-events: none;
  z-index: 5;
}

/* Right panel */
.ge-right-panel {
  width: 200px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  border-left: 1px solid var(--border);
  background: var(--panel);
  /* The whole panel scrolls so every tool control is reachable; the
     layers-actions row uses position:sticky to stay pinned at the
     bottom of the viewport while scrolling. */
  overflow-y: auto;
  overflow-x: hidden;
}

/* Controls */
.ge-controls {
  flex: 0 0 auto;
  padding: 10px 10px 6px;
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* Brush controls (Color + Size) — give the two rows breathing room
   between them, since neither sits inside a separator-bordered section. */
#ge-brush-controls {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Floating Transform popup — horizontal layout, draggable from
   anywhere on the popup (matches the FX / adjust popups). Defaults
   to the right side of the editor (over the layers panel). */
/* Match the .ge-adj-popup layout convention: icon + title + [_][×]
   header bar, then the body. Drag from the header (same as FX popups). */
.ge-transform-popup {
  position: absolute;
  z-index: 11;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 0 0 8px;
  background: color-mix(in srgb, var(--panel) 96%, transparent);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
  backdrop-filter: blur(6px);
  user-select: none;
}
.ge-transform-popup.ge-transform-popup-dragging .ge-transform-popup-head { cursor: grabbing; }
.ge-transform-popup-head {
  /* Inherits .ge-adj-head styling — flex row, icon + title + buttons,
     bottom border, grab cursor — with the title nudged down 2px for
     visual balance and explicit horizontal padding so the icon doesn't
     hug the left edge. */
  display: flex !important;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  padding-top: 10px;
  touch-action: none;
  cursor: grab;
}
.ge-transform-popup-head:active { cursor: grabbing; }
/* Force the title to take all middle space so the [_][×] cluster
   stays pinned to the right edge. */
.ge-transform-popup-head .ge-adj-title { flex: 1 1 auto !important; }
.ge-transform-popup-head .ge-head-btns { margin-left: auto !important; }
.ge-transform-popup-head .ge-adj-title { position: relative; top: 2px; }
/* Icon nudged 4px lower than the title baseline so it reads as the
   row's anchor rather than floating above it. */
.ge-transform-popup-head .ge-adj-icon { position: relative; top: 4px; }
/* Mobile-only: shift the head content UP to match the rest of the
   editor's mobile popup styling. */
@media (max-width: 820px) {
  .ge-transform-popup-head .ge-adj-title { top: -2px; }
  .ge-transform-popup-head .ge-adj-icon { top: 0; }
}
/* Make sure the head-buttons cluster sits hard-right with a small
   gap, matching the FX-popup convention. */
.ge-transform-popup-head .ge-head-btns { margin-left: auto; }
.ge-transform-min-hint {
  display: inline-flex;
  align-items: center;
  opacity: 0.45;
  margin-right: -2px;
  line-height: 1;
  color: var(--fg-muted);
}
/* Small "Merge" text label next to each merge / flatten button so the
   icons read at a glance instead of needing the title-tooltip. */
.ge-merge-label {
  font-size: 10px;
  margin-left: 4px;
  opacity: 0.75;
}
.ge-transform-popup-body {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
}
.ge-transform-popup-minimised .ge-transform-popup-body,
.ge-transform-popup-minimised .ge-transform-popup-hint {
  display: none;
}
.ge-transform-popup-hint { padding: 0 12px; }
.ge-transform-field {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  position: relative;
}
.ge-transform-field label {
  font-size: 11px;
  font-weight: 600;
  opacity: 0.65;
  min-width: 12px;
  text-align: right;
}
/* Hide the native browser spin-buttons entirely — we render our own
   themed ▲/▼ via .ge-transform-spin (see below). */
.ge-transform-popup-input {
  width: 76px;
  height: 24px;
  padding: 0 18px 0 6px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  font: inherit;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  text-align: right;
  cursor: text;
  -moz-appearance: textfield;
  box-sizing: border-box;
}
.ge-transform-popup-input::-webkit-outer-spin-button,
.ge-transform-popup-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  appearance: none;
  margin: 0;
  display: none;
}
/* Rotation field reserves extra room on the right for both the °
   suffix AND the custom spinner so they don't sit on top of each other.
   The suffix renders inside that reserved zone. */
.ge-transform-popup-input-rot { width: 76px; padding-right: 30px; }
.ge-transform-popup-input:focus { outline: none; border-color: var(--red); }
.ge-transform-input-locked {
  opacity: 0.4;
  cursor: not-allowed;
}
/* Custom themed spinner — two slim chevron buttons stacked to the right
   of the input. Tight stack so the pair reads as one control instead of
   two floating arrows. */
.ge-transform-spin {
  position: absolute;
  right: 3px;
  top: 50%;
  /* Nudged up 4px relative to the input's vertical center. */
  transform: translateY(calc(-50% - 4px));
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  height: 18px;
}
.ge-transform-spin button {
  flex: 1 1 0;
  width: 12px;
  min-height: 0;
  padding: 0;
  background: transparent;
  color: var(--fg-muted);
  border: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
  font-size: 7px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.ge-transform-spin button:first-child { border-radius: 3px 3px 0 0; border-bottom: 0; }
.ge-transform-spin button:last-child  { border-radius: 0 0 3px 3px; }
.ge-transform-spin button:hover {
  background: color-mix(in srgb, var(--accent, var(--red)) 25%, transparent);
  color: var(--fg);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 55%, var(--border));
}
.ge-transform-spin button:active {
  background: color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
}
.ge-transform-input-locked + .ge-transform-spin { display: none; }
/* Position the ° suffix to the LEFT of the spinner so they don't
   overlap. The rotation input's larger padding leaves room. */
.ge-transform-popup-suffix {
  position: absolute;
  right: 20px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 10px;
  opacity: 0.55;
  pointer-events: none;
}
.ge-transform-aspect-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg-muted);
  cursor: pointer;
  /* 2px smaller than the W/H input height so it reads as a chip-style
     toggle rather than another form field. Pulled up 2px so it tucks
     between W and H rather than sitting on their baseline. */
  height: 22px;
  width: 22px;
  padding: 0;
  position: relative;
  top: -3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.5;
  transition: opacity 0.12s, color 0.12s, border-color 0.12s;
}
.ge-transform-aspect-btn:hover { opacity: 1; }
.ge-transform-aspect-btn.active {
  opacity: 1;
  color: var(--accent, var(--red));
  border-color: color-mix(in srgb, var(--accent, var(--red)) 50%, var(--border));
}
/* Quick-action cluster (flip H, flip V, rotate 90°) — sits between the
   rotation input and the Apply button. Each button matches the input
   height and uses the editor's accent palette on hover. */
.ge-transform-quick {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 0 4px;
  /* Pulled up 2px so the flip/rotate icons sit slightly higher than the
     W/H/rotation inputs, matching the visual weight of the head row. */
  position: relative;
  top: -2px;
  border-left: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
}
.ge-transform-quick-btn {
  width: 24px;
  height: 24px;
  padding: 0;
  background: none;
  color: var(--fg-muted);
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.7;
  transition: opacity 0.12s, color 0.12s, border-color 0.12s, background 0.12s;
}
.ge-transform-quick-btn:hover {
  opacity: 1;
  color: var(--fg);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 55%, var(--border));
  background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
}
.ge-transform-quick-btn:active {
  background: color-mix(in srgb, var(--accent, var(--red)) 25%, transparent);
}
.ge-transform-popup-hint {
  font-size: 10px;
  opacity: 0.5;
  margin: 0;
  line-height: 1.3;
}

/* Floating Inpaint prompt — pops next to the user's last brush stroke
   so they can type a description and Generate without diverting to
   the side panel. */
.ge-inpaint-popup {
  position: fixed;
  z-index: 280;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 25%, transparent);
}
.ge-inpaint-popup-input {
  width: 240px;
  padding: 6px 8px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  font: inherit;
  font-size: 12px;
  position: relative;
  top: 0;
}
.ge-inpaint-popup-input:focus { outline: none; border-color: var(--red); }
.ge-inpaint-popup-run {
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 600;
  position: relative;
  top: 1px;
}
.ge-inpaint-popup-close {
  background: none;
  border: none;
  color: var(--fg-muted);
  font-size: 16px;
  line-height: 1;
  padding: 2px 4px;
  margin-left: 2px;
  cursor: pointer;
  position: relative;
  top: 0;
  opacity: 0.7;
  transition: opacity 0.15s, color 0.15s;
}
.ge-inpaint-popup-close:hover { opacity: 1; color: var(--fg); }
/* Loading state: the popup's panel chrome (bg/border/shadow) gets out
   of the way so only the whirlpool floats over the canvas. */
.ge-inpaint-popup.ge-inpaint-popup-loading {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  padding: 0;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Hide the controls wrapper entirely when every tool section inside is
   hidden — otherwise the padding + border draws an empty band above the
   layers panel even when no tool needs sliders/buttons. */
.ge-controls:not(:has(> *:not([style*="display: none"]):not([style*="display:none"]))) {
  display: none;
}
.ge-controls input[type="range"] {
  width: 100%;
  box-sizing: border-box;
  margin: 0;
  display: block;
}
.ge-control-row {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--fg);
}
.ge-control-row label {
  flex-shrink: 0;
  opacity: 0.7;
  min-width: 36px;
}
.ge-color-picker {
  width: 24px;
  height: 24px;
  border: 1px solid var(--border);
  border-radius: 50%;
  padding: 0;
  cursor: pointer;
  background: none;
  overflow: hidden;
}
/* Strip the native <input type="color"> chrome (border + padding around
   the swatch in each engine) so the visible color fills the circle. */
.ge-color-picker::-webkit-color-swatch-wrapper { padding: 0; }
.ge-color-picker::-webkit-color-swatch {
  border: none;
  border-radius: 50%;
}
.ge-color-picker::-moz-color-swatch {
  border: none;
  border-radius: 50%;
}
/* attachColorPicker swaps the native <input type="color"> for a styled
   text input with the same `.ge-color-picker` class plus
   `.cp-swatch-input`. Force the 24×24 circular swatch back on it so
   it doesn't render as a wide text box stretched across the row. */
.ge-color-picker.cp-swatch-input {
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  border: 1px solid var(--border);
  border-radius: 50%;
  padding: 0;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  /* Hide the (read-only) text caret + selection so the element reads
     as a swatch, not a text field. */
  color: transparent;
  text-shadow: none;
  caret-color: transparent;
  font-size: 0;
  user-select: none;
}
.ge-color-picker.cp-swatch-input::selection { background: transparent; }
.ge-color-picker.cp-swatch-input:focus {
  outline: 1px solid var(--red);
  outline-offset: 1px;
}
.ge-size-slider {
  flex: 1 1 auto;
  min-width: 0;
  height: 8px;
  accent-color: var(--red);
  -webkit-appearance: none;
  appearance: none;
  background: color-mix(in srgb, var(--fg) 25%, transparent);
  border-radius: 999px;
  margin: 0;
}
.ge-size-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--red);
  border: none; cursor: pointer;
}
.ge-size-slider:hover::-webkit-slider-thumb,
.ge-size-slider.is-using::-webkit-slider-thumb,
.ge-size-slider:active::-webkit-slider-thumb {
  width: 16px; height: 16px;
}
.ge-size-slider::-moz-range-thumb {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--red);
  border: none; cursor: pointer;
}
.ge-size-slider:hover::-moz-range-thumb,
.ge-size-slider.is-using::-moz-range-thumb,
.ge-size-slider:active::-moz-range-thumb {
  width: 16px; height: 16px;
}
.ge-size-label { font-size: 10px; opacity: 0.5; min-width: 28px; text-align: right; }

/* Eraser controls: tighter rows + a small brush preview on the left of
   each label so the user can see what each slider affects. The three
   previews share the same dot but each leans on a different rendering
   trick (alpha / scatter / blur) to mirror its slider's effect. */
.ge-eraser-row {
  display: flex !important;
  align-items: center !important;
  gap: 8px;
  padding: 2px 0;
  /* Lets the trailing value span pin to the row's right edge so it
     stays visible when the slider expands left over the label. */
  position: relative;
}
/* Inline value chip next to the label text. (Previously absolutely-
   positioned over the slider track to ride the expand animation, which
   caused overlap now that the dynamic-slider behavior is off for these.) */
.ge-eraser-row label > span[id$="-label"] {
  margin-left: 4px;
  padding: 0 4px;
  border-radius: 3px;
  cursor: text;
  font-size: 10px;
  opacity: 0.7;
  font-variant-numeric: tabular-nums;
}
.ge-eraser-row label > span[id$="-label"]:hover {
  background: color-mix(in srgb, var(--fg) 10%, transparent);
}
/* When the value chip is pulled out of its label and placed AFTER the
   slider (see DOM transform in galleryEditor.js init), it sits on the
   row's right edge and naturally shortens the slider's flex space. */
.ge-eraser-row > span[id$="-label"].ge-slider-value {
  flex: 0 0 auto;
  margin: 0 0 0 6px;
  padding: 0 4px;
  font-size: 10px;
  opacity: 0.7;
  font-variant-numeric: tabular-nums;
  min-width: 34px;
  text-align: right;
  cursor: text;
  border-radius: 3px;
  white-space: nowrap;
}
.ge-eraser-row > span[id$="-label"].ge-slider-value:hover {
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  opacity: 1;
}

/* Per-tool model selector row (lives at the top of each AI tool's
   section in the side panel). Mirrors the inpaint Model row layout. */
.ge-tool-model-row {
  display: flex !important;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
  min-width: 0;
}
.ge-tool-model-row > label {
  font-size: 11px;
  opacity: 0.6;
  flex: 0 0 auto;
}
.ge-tool-model {
  flex: 1 1 0;
  min-width: 0;
  font-size: 10px;
  padding: 2px 4px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
}

/* Section label inside a dropdown — small uppercase header. */
.dropdown-section-label {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  opacity: 0.5;
  font-weight: 600;
  padding: 6px 8px 2px;
}
.dropdown-section-divider {
  height: 1px;
  background: var(--border);
  margin: 4px 0;
  opacity: 0.6;
}

/* Inline numeric editor that replaces the value chip on click. */
.ge-slider-edit {
  position: absolute;
  z-index: 11;
  font: inherit;
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  padding: 1px 4px;
  border: 1px solid var(--red);
  border-radius: 3px;
  background: var(--bg);
  color: var(--fg);
  outline: none;
  text-align: center;
}

/* Merge dropdown — position is set by JS each time it opens (relative to
   the button's bounding rect) because the parent .ge-layers has
   overflow:hidden which would otherwise clip the popup. */
.ge-merge-menu {
  position: fixed;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 3px;
  box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 25%, transparent);
  z-index: 50;
  display: flex;
  flex-direction: column;
  min-width: 140px;
}
.ge-merge-menu[hidden] { display: none; }
.ge-merge-menu .dropdown-item-compact {
  background: none;
  border: none;
  width: 100%;
  text-align: left;
  padding: 5px 8px;
  font-size: 11px;
  gap: 8px;
  font: inherit;
  font-size: 11px;
}

/* Thin separator inside a tool section. */
.ge-section-divider {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 10px -2px;
  opacity: 0.6;
}

/* Small explanatory paragraph under AI tool section labels. */
.ge-section-hint {
  font-size: 10.5px;
  line-height: 1.45;
  opacity: 0.55;
  margin: 0 0 8px;
}
/* Clone-tool hint swaps between "Alt-click" (desktop) and "Double-tap"
   (mobile) so the on-screen instruction matches the actual gesture. */
.ge-clone-hint-mobile { display: none; }
@media (max-width: 820px) {
  .ge-clone-hint-desktop { display: none; }
  .ge-clone-hint-mobile { display: inline; }
}
/* Section title + adjacent "?" affordance — collapses the long
   explanatory paragraph into a hoverable tooltip so the side panel
   stays compact. */
.ge-section-title-with-help {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
/* Drop the source-whitespace text node between the title text and the
   ? span so the chip hugs the title with no extra gap. */
.ge-section-title-with-help .ge-section-help { margin-left: 0; }
.ge-section-help {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--fg) 12%, transparent);
  color: var(--fg-muted);
  font-size: 9px;
  font-weight: 700;
  cursor: help;
  user-select: none;
  transition: background 0.12s, color 0.12s;
}
.ge-section-help:hover,
.ge-section-help:focus-visible {
  background: color-mix(in srgb, var(--fg) 24%, transparent);
  color: var(--fg);
  outline: none;
}

/* Lightweight section title — used inside the inpaint panel to group
   the mask actions (Eye / Invert / Clear) under a "Selection" header. */
.ge-section-title {
  margin: 10px 0 4px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  opacity: 0.5;
  font-weight: 600;
}
/* Selection-row buttons stay compact so eye + invert + clear fit on
   one line beside each other. */
.ge-inpaint-mask-row {
  display: flex !important;
  gap: 4px;
  align-items: center;
}
.ge-inpaint-mask-row .ge-btn {
  flex: 0 0 auto;
}
.ge-inpaint-mask-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  opacity: 0.5;
  font-weight: 600;
  margin-right: 4px;
}

/* Fixed label column so the three sliders start at the same x and have
   the same length when unused. Without this, "Flow" (4 chars) leaves
   more room than "Opacity" (7 chars). */
.ge-eraser-row label {
  font-size: 10px;
  opacity: 0.55;
  flex: 0 0 78px;
  white-space: nowrap;
  display: inline-flex;
  justify-content: space-between;
  gap: 4px;
}
/* Fixed-size slider track for all side-panel sliders — no dynamic
   width/height expansion on drag. Only the thumb grows on interaction
   (see ::-webkit-slider-thumb / ::-moz-range-thumb below). */
.ge-eraser-row input[type="range"] {
  flex: 1 1 auto;
  min-width: 0;
  height: 8px;
  accent-color: var(--red);
  -webkit-appearance: none;
  appearance: none;
  background: color-mix(in srgb, var(--fg) 25%, transparent);
  border-radius: 999px;
  margin: 0;
  position: relative;
  z-index: 2;
}
.ge-eraser-row input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--red);
  border: none;
  cursor: pointer;
  transition: width 0.12s ease 0.5s, height 0.12s ease 0.5s;
}
.ge-eraser-row input[type="range"]:hover::-webkit-slider-thumb,
.ge-eraser-row input[type="range"].is-using::-webkit-slider-thumb,
.ge-eraser-row input[type="range"]:active::-webkit-slider-thumb {
  width: 16px; height: 16px;
  transition: width 0.12s ease 0s, height 0.12s ease 0s;
}
.ge-eraser-row input[type="range"]::-moz-range-thumb {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--red);
  border: none;
  cursor: pointer;
  transition: width 0.12s ease 0.5s, height 0.12s ease 0.5s;
}
.ge-eraser-row input[type="range"]:hover::-moz-range-thumb,
.ge-eraser-row input[type="range"].is-using::-moz-range-thumb,
.ge-eraser-row input[type="range"]:active::-moz-range-thumb {
  width: 16px; height: 16px;
  transition: width 0.12s ease 0s, height 0.12s ease 0s;
}
/* Inpaint Brush size slider — slightly larger thumb than the rest of
   the panel since it's the primary interactive control. */
#ge-inpaint-brush-slider::-webkit-slider-thumb { width: 12px; height: 12px; }
#ge-inpaint-brush-slider:hover::-webkit-slider-thumb,
#ge-inpaint-brush-slider.is-using::-webkit-slider-thumb,
#ge-inpaint-brush-slider:active::-webkit-slider-thumb { width: 18px; height: 18px; }
#ge-inpaint-brush-slider::-moz-range-thumb { width: 12px; height: 12px; }
#ge-inpaint-brush-slider:hover::-moz-range-thumb,
#ge-inpaint-brush-slider.is-using::-moz-range-thumb,
#ge-inpaint-brush-slider:active::-moz-range-thumb { width: 18px; height: 18px; }

/* Help "?" icon next to a section title. Subtle by default, hover
   brightens. Tooltip via native `title` attribute. */
.ge-section-help {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  margin-left: 4px;
  font-size: 9px;
  font-weight: 600;
  border: 1px solid var(--border);
  border-radius: 50%;
  color: var(--fg-muted);
  background: none;
  cursor: help;
  vertical-align: middle;
  position: relative;
  top: -1px;
  opacity: 0.7;
  transition: opacity 0.15s, color 0.15s, border-color 0.15s;
}
.ge-section-help:hover { opacity: 1; color: var(--fg); border-color: var(--fg-muted); }

/* Layer-FX popup — floating window bound to one layer. */
.ge-fx-popup {
  position: fixed;
  z-index: 300;
  width: 320px;
  max-height: 80vh;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.45);
  display: flex;
  flex-direction: column;
  font-size: 11px;
  color: var(--fg);
}
.ge-fx-popup-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  font-weight: 600;
}
.ge-fx-popup-title { font-size: 12px; }
.ge-fx-popup-close {
  background: none; border: none; color: var(--fg-muted);
  font-size: 18px; line-height: 1; cursor: pointer; padding: 0 4px;
}
.ge-fx-popup-close:hover { color: var(--fg); }
.ge-fx-popup-body { padding: 8px 10px; overflow-y: auto; }
.ge-fx-popup-foot {
  display: flex; gap: 6px; padding: 6px 10px;
  border-top: 1px solid var(--border);
}
.ge-fx-group-title {
  font-size: 10px; font-weight: 600; opacity: 0.6;
  text-transform: uppercase; letter-spacing: 0.5px;
  margin: 8px 0 4px;
}
.ge-fx-group-title:first-child { margin-top: 0; }
.ge-fx-cb-tone {
  font-size: 10px; opacity: 0.55; font-style: italic;
  margin: 4px 0 2px;
}
.ge-fx-row {
  display: flex; align-items: center; gap: 6px;
  padding: 2px 0;
}
.ge-fx-row-label {
  font-size: 10px; opacity: 0.7;
  flex: 0 0 110px; white-space: nowrap;
}
.ge-fx-row input[type="range"] {
  flex: 1 1 auto; min-width: 0;
  height: 6px; accent-color: var(--red);
  -webkit-appearance: none; appearance: none;
  background: color-mix(in srgb, var(--fg) 25%, transparent);
  border-radius: 999px;
}
.ge-fx-row input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--red); border: none; cursor: pointer;
}
.ge-fx-row input[type="range"]::-moz-range-thumb {
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--red); border: none; cursor: pointer;
}
.ge-fx-row-value {
  font-size: 10px; opacity: 0.7; font-variant-numeric: tabular-nums;
  flex: 0 0 40px; text-align: right;
}

/* Layer-row FX button — tinted when the layer has any non-identity FX. */
.ge-layer-fx-btn {
  opacity: 0.55;
}
.ge-layer-fx-btn:hover { opacity: 0.9; }
.ge-layer-fx-btn.active {
  opacity: 1;
  color: var(--accent, var(--red));
}
/* Add-mask button on each layer row. Subtle by default; lights up red
   when a lasso/wand selection is live so the user instantly sees that
   clicking it will bake that selection into a mask on this layer. */
.ge-layer-mask-btn {
  opacity: 0.45;
}
.ge-layer-mask-btn:hover { opacity: 0.9; }
.ge-layer-mask-btn.from-selection {
  opacity: 1;
  color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
}

/* Shared frosted-glass style for FX menu + adjustment popups. */
.ge-frosted {
  background: color-mix(in srgb, var(--panel) 70%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid color-mix(in srgb, var(--fg) 12%, transparent);
  box-shadow: 0 12px 40px rgba(0,0,0,0.55);
}

/* FX dropdown menu from the layer-row fx icon. */
.ge-fx-menu {
  position: fixed;
  z-index: 305;
  min-width: 200px;
  padding: 4px;
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ge-fx-menu-item {
  background: none;
  border: none;
  text-align: left;
  padding: 7px 10px;
  font-size: 12px;
  color: var(--fg);
  border-radius: 5px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
}
.ge-fx-menu-icon {
  display: inline-flex;
  align-items: center;
  color: var(--fg-muted);
  flex-shrink: 0;
}
.ge-fx-menu-item:hover {
  background: color-mix(in srgb, var(--fg) 12%, transparent);
}
.ge-fx-menu-item:hover .ge-fx-menu-icon { color: var(--fg); }

/* Per-type adjustment popup. */
.ge-adj-popup {
  position: fixed;
  z-index: 305;
  width: 320px;
  border-radius: 10px;
  display: flex;
  flex-direction: column;
  font-size: 11px;
  color: var(--fg);
}
.ge-adj-popup .ge-adj-head {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 12px;
  border-bottom: 1px solid color-mix(in srgb, var(--fg) 10%, transparent);
  cursor: grab;
  user-select: none;
}
.ge-adj-popup .ge-adj-title { flex: 1 1 auto; }
.ge-adj-popup .ge-head-btns { margin-left: auto; }
.ge-adj-popup .ge-adj-head:active { cursor: grabbing; }
.ge-adj-icon {
  display: inline-flex;
  align-items: center;
  color: var(--fg-muted);
  flex-shrink: 0;
}
.ge-adj-title { font-weight: 600; font-size: 12px; flex: 1 1 auto; }
.ge-adj-min, .ge-adj-close, .ge-history-close {
  background: none; border: none; color: var(--fg-muted);
  font-size: 16px; line-height: 1; cursor: pointer; padding: 0 4px;
  flex-shrink: 0;
}
.ge-history-close:hover { color: var(--fg); }
/* Mobile-only: shift head icons/buttons up and enlarge the minimise
   glyph so it reads as a window-minimise affordance, not a centered
   minus. Desktop keeps the original tight head styling. */
@media (max-width: 820px) {
  .ge-adj-icon {
    position: relative;
    top: -4px;
  }
  .ge-adj-title {
    position: relative;
    top: -2px;
  }
  .ge-adj-min, .ge-adj-close, .ge-history-close {
    position: relative;
    top: -4px;
  }
  .ge-adj-min,
  .ge-adj-popup .ge-adj-min,
  .ge-history-head .ge-adj-min,
  .ge-transform-popup-head .ge-adj-min {
    font-size: 22px !important;
    font-weight: 600 !important;
    padding: 0 8px !important;
    line-height: 0.6 !important;
    position: relative;
    top: 4px;
  }
  .ge-adj-head .ge-transform-aspect-btn {
    position: relative;
    top: -4px;
  }
}
.ge-adj-min:hover, .ge-adj-close:hover { color: var(--fg); }

/* Floating dock at bottom-right for minimised FX popups. */
#ge-fx-dock {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 12px;
  z-index: 304;
  display: flex;
  flex-direction: row;
  gap: 6px;
  align-items: center;
  pointer-events: none;
  max-width: calc(100vw - 24px);
  flex-wrap: wrap;
  justify-content: center;
}
.ge-fx-dock-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  border-radius: 6px;
  font-size: 11px;
  color: var(--fg);
  cursor: pointer;
  pointer-events: auto;
  max-width: 220px;
  white-space: nowrap;
  overflow: hidden;
}
.ge-fx-dock-chip:hover {
  background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.ge-fx-dock-icon { display: inline-flex; align-items: center; }
.ge-fx-dock-label { overflow: hidden; text-overflow: ellipsis; }
.ge-fx-dock-close {
  margin-left: 2px; opacity: 0.55; padding: 0 2px;
  font-size: 14px; line-height: 1;
}
.ge-fx-dock-close:hover { opacity: 1; color: var(--red); }

/* Sub-layer row icon in the layer panel. */
.ge-adj-sub-name {
  display: inline-flex !important;
  align-items: center;
  gap: 4px;
}
.ge-adj-sub-icon {
  display: inline-flex; align-items: center;
  color: var(--accent, var(--red));
  flex-shrink: 0;
}
.ge-adj-sub-icon svg { width: 11px; height: 11px; }

/* History panel — labeled timeline of edits. */
#ge-history-panel {
  position: fixed;
  z-index: 10004;
  width: 240px;
  max-height: 60vh;
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  color: var(--fg);
  font-size: 11px;
}
.ge-history-head {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 10px;
  border-bottom: 1px solid color-mix(in srgb, var(--fg) 10%, transparent);
  cursor: grab;
  user-select: none;
}
.ge-history-head:active { cursor: grabbing; }
.ge-history-title { font-weight: 600; font-size: 12px; flex: 1 1 auto; }
.ge-history-close {
  background: none; border: none; color: var(--fg-muted);
  font-size: 16px; line-height: 1; cursor: pointer; padding: 0 4px;
  flex-shrink: 0;
}
.ge-history-close:hover { color: var(--fg); }
.ge-history-list { overflow-y: auto; padding: 4px 0; }
.ge-history-row {
  display: flex; align-items: center; gap: 8px;
  width: 100%;
  padding: 5px 10px;
  background: none; border: none;
  text-align: left; cursor: pointer;
  color: var(--fg); font-size: 11px;
}
.ge-history-row:hover { background: color-mix(in srgb, var(--fg) 8%, transparent); }
.ge-history-row.current { background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent); }
.ge-history-row.current .ge-history-row-dot { background: var(--accent, var(--red)); }
.ge-history-row.future { opacity: 0.45; }
.ge-history-row-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--fg-muted);
  flex-shrink: 0;
}
.ge-history-row-label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ge-history-row-time { font-size: 9px; opacity: 0.55; flex-shrink: 0; }

/* Tight button group in popup heads — min + close sit immediately next
   to each other with no inherited flex gap, pushed to the right edge. */
.ge-head-btns {
  display: inline-flex;
  align-items: center;
  gap: 0;
  margin-left: auto;
  flex-shrink: 0;
}
.ge-head-btns > button {
  width: 26px;
  height: 26px;
  padding: 0 !important;
  margin: 0;
  font-size: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
}
.ge-head-btns > button:hover {
  background: color-mix(in srgb, var(--fg) 12%, transparent);
}

/* Detail-header heart toggle — sits next to the icon Edit button. */
.gallery-detail-fav-header { opacity: 0.7; transition: opacity 0.15s, color 0.15s; }
.gallery-detail-fav-header:hover { opacity: 1; }
.gallery-detail-fav-header.active { color: var(--red); opacity: 1; }

/* Canvas loading overlay — covers the canvas area while a blocking op
   (rotation, big resize, etc.) runs. */
.ge-canvas-loading {
  position: absolute;
  inset: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  border-radius: 8px;
  pointer-events: auto;
}
.ge-canvas-loading-spinner {
  width: 32px; height: 32px;
  border: 3px solid color-mix(in srgb, var(--fg) 25%, transparent);
  border-top-color: var(--accent, var(--red));
  border-radius: 50%;
  animation: ge-canvas-spin 0.8s linear infinite;
}
.ge-canvas-loading-msg { font-size: 12px; opacity: 0.85; }
@keyframes ge-canvas-spin { to { transform: rotate(360deg); } }

/* Missing-dependency notice in tool sections (Bg Remove etc.). */
.ge-dep-notice {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  background: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 30%, transparent);
  border-radius: 6px;
  margin: 4px 0 8px;
  font-size: 11px;
}
.ge-dep-notice-text { flex: 1 1 auto; line-height: 1.4; }
.ge-dep-notice-text strong { display: block; margin-bottom: 2px; }
.ge-dep-notice-text code {
  font-family: 'Fira Code', monospace;
  font-size: 10px;
  padding: 1px 4px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
}
.ge-dep-notice .ge-btn { flex-shrink: 0; }

/* Brief highlight on a Cookbook deps row arrived-at via deep-link. */
.cookbook-pkg-flash { animation: cookbook-pkg-flash 1.4s ease-out; }
@keyframes cookbook-pkg-flash {
  0%   { background: color-mix(in srgb, var(--accent, var(--red)) 30%, transparent); }
  100% { background: transparent; }
}

/* Flash highlight when an anchor-link ([Name](#task-/#skill-/#research-))
   opens a panel and focuses a specific item. */
@keyframes anchor-item-flash {
  0%   { background: color-mix(in srgb, var(--accent, var(--red)) 28%, transparent); }
  60%  { background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent); }
  100% { background: transparent; }
}
.task-card-flash,
.skill-row-flash,
.research-card-flash {
  animation: anchor-item-flash 2s ease-out;
  border-radius: 6px;
}

/* Preview dot icons for the Blur sub-menu — visual cue for each blur
   type (Gaussian = soft round, Zoom = radial streaks). */
.ge-blur-icon {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
  background: var(--fg);
}
.ge-blur-icon.ge-blur-gaussian {
  background: radial-gradient(circle, var(--fg) 0%, var(--fg) 25%, transparent 75%);
  filter: blur(1.5px);
}
.ge-blur-icon.ge-blur-zoom {
  background: radial-gradient(circle, var(--fg) 0%, var(--fg) 20%, transparent 60%);
  box-shadow:
    0 -5px 0 -3px var(--fg),
    0 5px 0 -3px var(--fg),
    -5px 0 0 -3px var(--fg),
    5px 0 0 -3px var(--fg),
    -4px -4px 0 -3px var(--fg),
    4px -4px 0 -3px var(--fg),
    -4px 4px 0 -3px var(--fg),
    4px 4px 0 -3px var(--fg);
}

/* Inline-swatch color picker — small square that fits next to a title. */
.ge-inline-swatch {
  width: 16px !important;
  height: 16px !important;
  padding: 0 !important;
  border: 1px solid var(--border) !important;
  border-radius: 3px !important;
  cursor: pointer;
  flex-shrink: 0;
  -webkit-appearance: none;
  appearance: none;
  background: none;
}
.ge-inline-swatch::-webkit-color-swatch-wrapper { padding: 0; }
.ge-inline-swatch::-webkit-color-swatch { border: none; border-radius: 2px; }
.ge-inline-swatch::-moz-color-swatch { border: none; border-radius: 2px; }

/* Drop overlay over the gallery editor while a file drag is in flight. */
.ge-drop-overlay {
  position: absolute;
  inset: 0;
  z-index: 200;
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  border: 3px dashed color-mix(in srgb, var(--accent, var(--red)) 80%, transparent);
  border-radius: 8px;
}
.ge-drop-overlay-msg {
  padding: 12px 20px;
  font-size: 14px;
  font-weight: 600;
  color: var(--fg);
  background: color-mix(in srgb, var(--panel) 70%, transparent);
  backdrop-filter: blur(10px);
  border-radius: 8px;
}

/* Small buttons that show an SVG icon + text label side-by-side. */
.ge-btn-iconlabel {
  display: inline-flex !important;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}
.ge-btn-iconlabel svg {
  flex-shrink: 0;
  opacity: 0.85;
}
.ge-btn-iconlabel:hover svg { opacity: 1; }

.ge-adj-body {
  padding: 8px 12px;
  max-height: 60vh;
  overflow-y: auto;
  overflow-x: hidden;
}
.ge-adj-popup { box-sizing: border-box; overflow: hidden; }
.ge-adj-foot {
  display: flex; gap: 6px; justify-content: flex-end;
  padding: 8px 12px;
  border-top: 1px solid color-mix(in srgb, var(--fg) 10%, transparent);
}
/* Mobile-only: oversize the foot Cancel/Apply buttons to match the
   Transform popup. Desktop keeps the original ge-btn-sm sizing. */
@media (max-width: 820px) {
  .ge-adj-foot .ge-adj-cancel-btn,
  .ge-adj-foot .ge-adj-apply-btn {
    padding: 8px 16px !important;
    font-size: 13px !important;
    min-width: 80px;
    text-align: center;
  }
}
.ge-adj-row {
  display: flex; align-items: center; gap: 3px;
  padding: 0;
  margin: 0;
  min-width: 0;
  line-height: 1;
}
.ge-adj-row > label {
  flex: 0 0 96px;
  font-size: 10px;
  opacity: 0.7;
}
.ge-adj-row input[type="range"] {
  flex: 1 1 auto;
  min-width: 0;
  height: 6px;
  accent-color: var(--red);
  -webkit-appearance: none; appearance: none;
  background: color-mix(in srgb, var(--fg) 25%, transparent);
  border-radius: 999px;
}
/* Mobile-only: fixed-size sliders + taller rows so the thumb growing
   on active doesn't push the row height around. */
@media (max-width: 820px) {
  .ge-adj-row {
    min-height: 28px;
  }
  .ge-adj-row input[type="range"] {
    flex: 0 0 160px;
    width: 160px;
    min-width: 160px;
    max-width: 160px;
  }
}
.ge-adj-row input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--red);
  cursor: pointer; border: none;
  transition: width 0.12s ease, height 0.12s ease;
}
.ge-adj-row input[type="range"]:hover::-webkit-slider-thumb,
.ge-adj-row input[type="range"]:active::-webkit-slider-thumb {
  width: 18px; height: 18px;
}
.ge-adj-row input[type="range"]::-moz-range-thumb {
  width: 12px; height: 12px;
  border-radius: 50%; background: var(--red); border: none; cursor: pointer;
  transition: width 0.12s ease, height 0.12s ease;
}
.ge-adj-row input[type="range"]:hover::-moz-range-thumb,
.ge-adj-row input[type="range"]:active::-moz-range-thumb {
  width: 18px; height: 18px;
}
/* Value chip — fixed-width column, text aligned LEFT so the chip's
   left edge always sits immediately after the slider regardless of
   value length. Without a fixed width Hue's "180 °" made its slider
   shorter than Saturation's "100" since slider was flex-1. */
.ge-adj-value {
  flex: 0 0 34px;
  margin-left: 0;
  padding: 0 2px;
  font-size: 10px; opacity: 0.7; text-align: left;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  cursor: text;
}
.ge-adj-value:hover { opacity: 1; }
.ge-adj-cb-tone {
  font-size: 10px; opacity: 0.55; font-style: italic;
  margin: 2px 0 0;
  line-height: 1;
}
.ge-adj-cb-row {
  padding: 0;
  gap: 3px;
}
.ge-adj-cb-row .ge-adj-cb-dot {
  width: 10px; height: 10px; border-radius: 50%;
  display: inline-block; flex-shrink: 0;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--fg) 20%, transparent);
}
.ge-adj-cb-tone-picker {
  margin: 0 0 8px;
}
.ge-adj-cb-tone-select {
  width: 100%;
  padding: 6px 8px;
  font-size: 12px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--panel) 90%, transparent);
  color: var(--fg);
}
.ge-adj-cb-tone-select:focus { outline: none; border-color: var(--red); }
.ge-adj-hist-details {
  margin-bottom: 8px;
}
.ge-adj-hist-details > summary {
  font-size: 11px;
  color: var(--fg-muted);
  cursor: pointer;
  padding: 4px 0;
  list-style: none;
  user-select: none;
}
.ge-adj-hist-details > summary::-webkit-details-marker { display: none; }
.ge-adj-hist-details > summary::before {
  content: '▸ ';
  display: inline-block;
  transition: transform 0.12s;
}
.ge-adj-hist-details[open] > summary::before {
  content: '▾ ';
}
.ge-adj-hist-details .ge-adj-hist-wrap {
  margin-top: 4px;
}
.ge-adj-histogram {
  display: block;
  width: 100%;
  height: 80px;
  border-radius: 4px;
  background: rgba(0,0,0,0.25);
}
.ge-adj-hist-wrap {
  position: relative;
  margin-bottom: 14px;
}
.ge-adj-hist-handles {
  position: absolute;
  left: 0; right: 0;
  bottom: -10px;
  height: 12px;
  pointer-events: none;
}
.ge-adj-hist-handle {
  position: absolute;
  width: 12px;
  height: 12px;
  cursor: ew-resize;
  pointer-events: auto;
  border-style: solid;
  border-width: 0 6px 10px 6px;
  border-color: transparent transparent var(--fg-muted) transparent;
  filter: drop-shadow(0 1px 1px rgba(0,0,0,0.4));
}
.ge-adj-hist-handle:hover { border-bottom-color: var(--fg); }
.ge-adj-hist-handle.hist-h-black { border-bottom-color: #111; }
.ge-adj-hist-handle.hist-h-white { border-bottom-color: #fff; }
.ge-adj-hist-handle.hist-h-gamma { border-bottom-color: var(--accent, var(--red)); }
/* Per-slider revert button. */
.ge-adj-revert {
  background: none; border: none;
  color: var(--fg-muted); cursor: pointer;
  padding: 2px 4px; margin-left: 2px;
  opacity: 0.55;
  display: inline-flex; align-items: center; justify-content: center;
  transition: opacity 0.15s, color 0.15s;
}
.ge-adj-revert:hover { opacity: 1; color: var(--fg); }

/* Adjustment sub-layer row in the layer panel — indented under parent. */
.ge-adj-sub-item {
  padding-left: 18px;
  opacity: 0.85;
  border-left: 2px solid color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
  margin-left: 6px;
  font-size: 10.5px;
}
.ge-adj-sub-item .ge-layer-name {
  opacity: 0.85;
  font-style: italic;
}
/* Mask sub-layer rows. Same indent as adj sub-layers but the
   highlight bar uses fg-muted instead of accent — these aren't FX
   effects, just selection-state. When activated (the click target
   for paint / inpaint / generate), border + name go full strength. */
.ge-mask-sub-item {
  border-left-color: color-mix(in srgb, var(--fg) 30%, transparent);
}
.ge-mask-sub-item.active {
  border-left-color: var(--red);
  opacity: 1;
}
.ge-mask-sub-item.active .ge-layer-name { opacity: 1; font-style: normal; font-weight: 600; }
.ge-eraser-preview {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--fg);
  flex-shrink: 0;
  display: inline-block;
  position: relative;
}
/* Opacity preview — translucent disk */
#ge-eraser-preview-opacity { opacity: 0.5; }
/* Flow preview — dotted ring to suggest discrete stamping */
#ge-eraser-preview-flow {
  background: none;
  border: 2px dotted var(--fg);
  opacity: 0.7;
  width: 12px;
  height: 12px;
}
/* Softness preview — blurred edge */
#ge-eraser-preview-softness {
  background: radial-gradient(circle, var(--fg) 0%, var(--fg) 30%, transparent 75%);
  opacity: 0.7;
}
/* Wand feather preview — solid disk that visibly softens at its edge
   as the user drags the Feather slider. JS sets --feather-blur. */
#ge-wand-feather-preview {
  filter: blur(var(--feather-blur, 0px));
  opacity: 0.8;
}
.ge-actions {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}
.ge-btn {
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--fg);
  font-size: 11px;
  cursor: pointer;
  transition: background 0.15s;
}
.ge-btn:hover { background: color-mix(in srgb, var(--fg) 10%, var(--bg)); }
.ge-btn.active {
  background: color-mix(in srgb, var(--accent, var(--red)) 16%, var(--bg));
  border-color: var(--accent, var(--red));
  color: var(--accent, var(--red));
}
.ge-btn-sm { padding: 3px 6px; font-size: 10px; }
/* Busy state on AI tool buttons — whirlpool + verb text side by side. */
.ge-btn-processing {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}
.ge-btn-busy-label { font-size: inherit; line-height: 1; }
/* Mask-vis eye button is icon-only — strip the inherited .ge-btn
   background / border so it reads as a plain affordance, matching
   the layer-row eye toggles. */
.ge-mask-vis-btn {
  background: none !important;
  border: none !important;
  padding: 2px 4px !important;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.5;
  transition: opacity 0.12s;
}
.ge-mask-vis-btn:hover { opacity: 0.85; background: none !important; }
.ge-mask-vis-btn.visible { opacity: 0.9; }
.ge-mask-vis-btn.visible:hover { opacity: 1; }
/* Paint/Erase segmented toggle in the Inpaint Brush section. */
.ge-inpaint-mode-btn {
  font-size: 11px;
  padding: 4px 8px;
  opacity: 0.6;
  border: 1px solid var(--border);
}
.ge-inpaint-mode-btn:hover { opacity: 0.85; }
.ge-inpaint-mode-btn.active {
  opacity: 1;
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  border-color: var(--accent, var(--red));
  color: var(--accent, var(--red));
  font-weight: 600;
}
/* Wand New / + Add / − Subtract segmented toggle. */
.ge-wand-mode-btn {
  flex: 1 1 0;
  font-size: 11px;
  padding: 4px 6px;
  opacity: 0.6;
  border: 1px solid var(--border);
}
.ge-wand-mode-btn:hover { opacity: 0.85; }
.ge-wand-mode-btn.active {
  opacity: 1;
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  border-color: var(--accent, var(--red));
  color: var(--accent, var(--red));
  font-weight: 600;
}
.ge-wand-live-btn {
  flex: 0 0 auto;
  opacity: 0.6;
  min-width: 46px;
  height: 22px;
  padding: 0 10px !important;
  border-radius: 999px !important;
  font-size: 10px;
  line-height: 1;
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  transition: opacity 0.15s, color 0.15s, border-color 0.15s, background 0.15s;
}
.ge-wand-live-btn:hover { opacity: 0.85; }
.ge-wand-live-btn.active {
  opacity: 1;
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  border-color: var(--accent, var(--red));
  color: var(--accent, var(--red));
  font-weight: 600;
}
/* Top-bar buttons (undo/redo, zoom +/-, Fit, 1:1, Resize, Edge, Import,
   Download, Save) — visually their glyphs sit too low compared to the
   surrounding labels, so shift contents up 2 px via asymmetric padding. */
/* Visually the buttons sit lower than the surrounding text labels on the
   topbar. Keep the box padding symmetric but lift the whole box 2 px so it
   centers against the adjacent label baselines. */
.ge-topbar .ge-btn,
.ge-topbar .ge-btn-sm,
.ge-topbar select,
.ge-topbar input { position: relative; top: -2px; }
/* The tiny "Gen" / "Inpaint" inline labels now look too high relative to
   the shifted-up controls — nudge them down 2 px to re-align. */
.ge-topbar span[style*="font-size:9px"] { position: relative; top: 2px; }
/* The Gen and Inpaint model selects — must visually sit on the same line as
   the small "Gen" / "Inpaint" labels (which are at top:2px). Use !important
   so this beats the generic `.ge-topbar select` rule above no matter what
   element-vs-class specificity quirks. */
.ge-topbar .ge-ai-model { position: relative !important; top: 1px !important; }
/* The ◢ glyph baseline reads lower than the surrounding text — shift it
   up 1 px so it visually centers with the "Edge" label. */
.ge-edge-glyph { position: relative; top: -1px; }
/* Flex-line-break helper used in the mobile transform popup. Hidden by
   default so desktop keeps its single-row layout. */
.ge-row-break { display: none; }
/* Stacked button — glyph on top, small uppercase label below. Currently
   used for Undo/Redo so users get an obvious "UNDO" / "REDO" affordance
   label without losing the compact icon. */
.ge-stacked-btn {
  display: inline-flex !important;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 2px 8px !important;
  line-height: 1;
}
.ge-stacked-btn .ge-stacked-glyph {
  font-size: 14px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 14px;
}
.ge-stacked-btn .ge-stacked-glyph svg { display: block; }
.ge-stacked-btn .ge-stacked-label {
  font-size: 8px;
  letter-spacing: 0.06em;
  opacity: 0.65;
  font-weight: 600;
  position: relative;
  top: 2px;
}
/* Zoom % indicator — magnifier glyph on top, percentage label below.
   Matches the Undo/Redo stacked-button layout so the topbar reads as
   one consistent row of stacked icons. */
.ge-zoom-stack {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  line-height: 1;
  padding: 0 4px;
}
.ge-zoom-stack .ge-zoom-glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.7;
  /* Glyph nudged 1 px right (was 2 px left). */
  position: relative;
  left: -1px;
}
.ge-zoom-stack .ge-zoom-glyph svg {
  width: 16px;
  height: 16px;
}
.ge-zoom-stack .ge-zoom-label {
  font-size: 8px;
  letter-spacing: 0.06em;
  opacity: 0.65;
  font-weight: 600;
  position: relative;
  top: 4px;
  left: 1px;
}
/* + Add sits 1 px below the title baseline to match the surrounding
   header buttons after the recent header restructure. */
.ge-layers-header #ge-add-layer { position: relative; top: 1px; }
/* Layer-row buttons in the right panel — the unicode glyphs (◉, ⤢) have
   irregular baseline metrics so the box reads as too low; shift up 2 px. */
.ge-layer-vis,
.ge-layer-btn { position: relative; top: -2px; }
/* Delete (×) button glyph baseline reads even lower than the visibility
   icon — push it up an extra 2 px so it visually centers. */
.ge-layer-btn.danger { top: -4px; }
/* Save / Save as new / Download dropdown anchored to the primary Save
   button in the topbar. */
.ge-save-wrap { position: relative; display: inline-block; }
.ge-save-menu {
  position: fixed;
  min-width: 160px;
  padding: 4px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 18%, transparent);
  z-index: 10005;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ge-save-menu[hidden] { display: none; }
.ge-save-menu .dropdown-item-compact {
  width: 100%;
  background: none;
  border: none;
  text-align: left;
  font: inherit;
}

/* Resize and Edge popups in the topbar — same anchored-dropdown pattern as
   the Save menu. Resize lists preset canvas sizes; Edge is a tiny form.
   Image + Filter menus reuse the same chrome. */
.ge-resize-wrap,
.ge-edge-wrap,
.ge-image-wrap,
.ge-filter-wrap { position: relative; display: inline-block; }
.ge-resize-menu,
.ge-edge-menu,
.ge-image-menu,
.ge-filter-menu {
  position: absolute;
  top: calc(100% + 2px);
  right: 0;
  padding: 4px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 18%, transparent);
  z-index: 12;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* Mobile: escape the topbar's stacking context (z 5) so the dropdown
   lands above the bottom-sheet panels (z 50 / 60). Switch to fixed and
   pin under the topbar. */
@media (max-width: 820px) {
  .ge-resize-menu,
  .ge-edge-menu,
  .ge-image-menu,
  .ge-filter-menu {
    position: fixed;
    top: 44px;
    right: 8px;
    z-index: 500;
    max-width: calc(100vw - 16px);
  }
}
.ge-resize-menu { min-width: 200px; }
.ge-edge-menu { min-width: 220px; padding: 10px; gap: 6px; }
.ge-image-menu { min-width: 180px; }
.ge-filter-menu { min-width: 180px; }
.ge-resize-menu[hidden],
.ge-edge-menu[hidden],
.ge-image-menu[hidden],
.ge-filter-menu[hidden] { display: none; }
.ge-resize-menu .dropdown-item-compact,
.ge-image-menu .dropdown-item-compact,
.ge-filter-menu .dropdown-item-compact {
  width: 100%;
  background: none;
  border: none;
  text-align: left;
  font: inherit;
}
/* Sub-menu label inside the Filter / Image dropdown — small uppercase
   header so "Blur" / "Selection" reads as a category, not an action. */
.ge-filter-submenu-label {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.45;
  font-weight: 600;
  padding: 4px 8px 2px;
  pointer-events: none;
}
/* Disabled dropdown items (e.g. Fill when there's no selection) read
   as muted + non-interactive. */
.ge-image-menu .dropdown-item-compact:disabled,
.ge-filter-menu .dropdown-item-compact:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
/* Filter live-preview modal — translucent backdrop over the editor
   with a frosted parameter panel anchored top-right. The active layer
   re-renders live as the user drags the sliders. */
.ge-filter-overlay {
  position: absolute;
  inset: 0;
  z-index: 14;
  display: flex;
  align-items: flex-start;
  justify-content: flex-end;
  padding: 60px 20px 20px;
  background: rgba(0, 0, 0, 0.18);
  pointer-events: auto;
}
.ge-filter-modal {
  width: 240px;
  background: color-mix(in srgb, var(--panel) 95%, transparent);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
  backdrop-filter: blur(10px);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.ge-filter-modal-head {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  opacity: 0.7;
}
.ge-filter-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ge-filter-row label {
  font-size: 10px;
  opacity: 0.7;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.ge-filter-row-value {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  opacity: 0.85;
}
.ge-filter-row input[type="range"] {
  width: 100%;
}
.ge-filter-modal-actions {
  display: flex;
  gap: 6px;
  justify-content: flex-end;
  margin-top: 4px;
}
.ge-menu-divider {
  height: 1px;
  background: var(--border);
  margin: 4px 0;
}
.ge-edge-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.ge-edge-label {
  font-size: 11px;
  opacity: 0.65;
}
.ge-edge-input {
  padding: 6px 8px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  font: inherit;
  font-size: 12px;
}
.ge-edge-input:focus { outline: none; border-color: var(--red); }
.ge-edge-actions {
  display: flex;
  gap: 6px;
}
.ge-edge-actions .ge-btn { flex: 1; }
.ge-edge-hint {
  margin: 0;
  font-size: 10px;
  opacity: 0.5;
  line-height: 1.4;
}

/* Styled "New canvas" prompt — replaces the native browser prompt(). */
.ge-canvas-prompt {
  max-width: 360px;
  width: 90vw;
}
.ge-canvas-prompt .modal-header h4 { margin: 0; font-size: 14px; }
.ge-canvas-prompt-row {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  justify-content: center;
  margin: 6px 0 2px;
}
.ge-canvas-prompt-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1;
  min-width: 0;
}
.ge-canvas-prompt-field > span {
  font-size: 11px;
  opacity: 0.65;
}
.ge-canvas-prompt-field input {
  width: 100%;
  padding: 8px 10px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
  font-size: 14px;
}
.ge-canvas-prompt-field input:focus { outline: none; border-color: var(--red); }
.ge-canvas-prompt-x {
  font-size: 18px;
  opacity: 0.4;
  align-self: center;
  padding-bottom: 8px;
}
.ge-canvas-prompt-hint {
  margin: 12px 0 0;
  font-size: 11px;
  opacity: 0.5;
  text-align: center;
}

.ge-btn-primary {
  background: var(--red);
  color: #fff;
  border-color: var(--red);
  font-weight: 600;
}
.ge-btn-primary:hover { background: color-mix(in srgb, var(--red) 85%, #000); }
.ge-zoom-label { font-size: 10px; opacity: 0.5; }

/* Nudge whirlpool spinners 1px down so they sit on the text baseline
   when placed inside action buttons (Generate, Harmonize, etc). */
.ge-btn .ai-spinner-whirlpool,
.ge-btn .spinner-whirlpool,
button .ai-spinner-whirlpool,
button .spinner-whirlpool {
  position: relative;
  top: 1px;
}

/* Layers panel */
.ge-layers {
  /* Natural height now that the whole right panel scrolls — flex:1
     would collapse the list inside a scrolling parent. */
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
}
/* Vertical drag handle that sits on the LEFT edge of the right panel.
   Drag horizontally to widen / narrow the entire side panel. Slim
   by default; brighter on hover so the user notices it's interactive. */
.ge-panel-resize {
  position: absolute;
  left: -3px;
  top: 0;
  bottom: 0;
  width: 6px;
  cursor: ew-resize;
  background: transparent;
  z-index: 5;
  transition: background 0.12s;
}
.ge-panel-resize:hover,
.ge-panel-resize:active {
  background: color-mix(in srgb, var(--red) 30%, transparent);
}
/* Center grip dots so the handle reads as draggable. */
.ge-panel-resize::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 2px;
  height: 24px;
  background: color-mix(in srgb, var(--fg) 35%, transparent);
  border-radius: 2px;
}
/* The right panel needs `position: relative` so the handle (absolute)
   anchors to it instead of the viewport. */
.ge-right-panel { position: relative; }

/* Floating value bubble that pops above the slider thumb while
   dragging. Solves the "I can't see the number" problem when the
   slider's opaque plate covers the row's right-edge value chip. */
.ge-slider-bubble {
  /* Fixed positioning so the bubble escapes any overflow:hidden/auto
     on the slider's ancestor chain (the layers list clips, and used
     to swallow the bubble entirely). JS sets viewport-relative
     left/top; the translate centers and lifts the bubble above the
     slider thumb. */
  position: fixed;
  transform: translate(-50%, -100%);
  z-index: 10000;
  padding: 5px 10px;
  background: var(--red);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  border-radius: 6px;
  box-shadow: 0 3px 10px color-mix(in srgb, var(--red) 40%, transparent);
  pointer-events: none;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.1s;
}
.ge-slider-bubble[hidden] { display: none; }
.ge-slider-bubble.visible { opacity: 1; }
/* Tail pointing DOWN at the slider (bubble sits above). */
.ge-slider-bubble::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -4px;
  transform: translateX(-50%);
  width: 8px;
  height: 8px;
  background: var(--red);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}

/* Floating thumbnail shown when hovering a layer row. */
.ge-layer-thumb {
  position: fixed;
  z-index: 60;
  padding: 4px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 6px 18px color-mix(in srgb, var(--fg) 30%, transparent);
  pointer-events: none;
  display: none;
}
.ge-layer-thumb canvas {
  display: block;
  border-radius: 4px;
}
.ge-layers-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  font-size: 11px;
  font-weight: 600;
  color: var(--fg);
  opacity: 0.8;
  border-bottom: 1px solid var(--border);
  position: relative;
}
.ge-layers-title { flex: 1; }
.ge-layers-grab { display: none; } /* mobile only */
.ge-layers-merge-wrap { position: relative; display: inline-flex; }
.ge-layers-merge-wrap #ge-layers-merge-btn { position: relative; top: 0; }
.ge-layers-merge-menu {
  /* Fixed positioning so the menu escapes the layers panel's overflow
     clipping (which made it look transparent). JS sets exact coords
     via getBoundingClientRect on the trigger button at open time. */
  position: fixed;
  z-index: 200;
  min-width: 160px;
  padding: 4px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 18%, transparent);
  display: flex;
  flex-direction: column;
  gap: 2px;
  pointer-events: auto;
}
.ge-layers-merge-menu[hidden] { display: none; }
.ge-layers-merge-menu .dropdown-item-compact {
  width: 100%;
  background: none;
  border: none;
  text-align: left;
  font: inherit;
}
.ge-layers-actions {
  display: flex;
  gap: 4px;
  padding: 6px 8px;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
  /* Pin the action row to the bottom of the right panel's scroll
     viewport. Without this, when the tool-controls section is tall
     enough to overflow the panel, the merge buttons get pushed below
     the fold and require scrolling to reach. */
  position: sticky;
  bottom: 0;
  background: var(--panel);
  z-index: 4;
  /* Centered cluster instead of stretching the buttons to fill the
     panel width. */
  justify-content: center;
}
/* Merge / flatten buttons sit content-sized so the cluster reads as a
   centered group of three, not three stretched bars across the panel. */
.ge-layers-actions .ge-btn {
  flex: 0 0 auto;
  min-width: 0;
  padding: 4px 8px;
  font-size: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  white-space: nowrap;
}
.ge-layers-actions .ge-icon-btn {
  /* Icon-only buttons stay narrow — no risk of label overflow. */
  flex: 0 0 auto;
  padding: 4px 8px;
}
.ge-layers-actions .ge-btn svg {
  flex-shrink: 0;
  margin-right: 0 !important;
}
.ge-layers-list {
  /* Cap the list so a huge layer stack scrolls within its own box
     rather than pushing the whole panel arbitrarily tall. */
  max-height: 320px;
  overflow-y: auto;
  padding: 0;
}
.ge-layer-item {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 5px 8px;
  cursor: pointer;
  border-left: 2px solid transparent;
  transition: background 0.1s;
  font-size: 11px;
  color: var(--fg);
}
/* Row-hover tint removed — kept the layer row's background stable so
   it doesn't shift visually as the user reaches for the opacity slider. */
.ge-layer-item.active {
  background: color-mix(in srgb, var(--red) 8%, transparent);
  border-left-color: var(--red);
}
/* "Active parent" — the layer is selected, but a mask sub-layer below
   it is the actual paint target. Soft hint so the user can see which
   parent owns the active mask without competing with the mask's
   strong red highlight. */
.ge-layer-item.active-parent {
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  border-left-color: color-mix(in srgb, var(--fg) 30%, transparent);
}
.ge-layer-vis {
  border: none;
  background: none;
  color: var(--fg);
  cursor: pointer;
  padding: 2px 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.4;
  transition: opacity 0.12s;
}
.ge-layer-vis:hover { opacity: 0.8; }
.ge-layer-vis.visible { opacity: 0.9; }
.ge-layer-vis.visible:hover { opacity: 1; }
.ge-layer-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.ge-layer-name-input {
  flex: 1;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--fg);
  font-size: 11px;
  padding: 1px 4px;
  border-radius: 3px;
  min-width: 0;
}
/* All sliders inside the image editor share the same visual language as
   the layer opacity slider — rounded pill, dim track, red thumb. Track
   AND thumb grow when interacting for finer tweaking. */
.gallery-editor-container input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  height: 4px;
  background: color-mix(in srgb, var(--fg) 25%, transparent);
  border-radius: 999px;
  accent-color: var(--red);
  cursor: pointer;
  transition: height 0.15s ease;
}
.gallery-editor-container input[type="range"]:hover,
.gallery-editor-container input[type="range"]:focus,
.gallery-editor-container input[type="range"]:active {
  height: 10px;
}
.gallery-editor-container input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--red);
  border: none;
  cursor: pointer;
  transition: width 0.12s ease, height 0.12s ease;
}
.gallery-editor-container input[type="range"]::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--red);
  border: none;
  cursor: pointer;
  transition: width 0.12s ease, height 0.12s ease;
}
.gallery-editor-container input[type="range"]:hover::-webkit-slider-thumb,
.gallery-editor-container input[type="range"]:focus::-webkit-slider-thumb,
.gallery-editor-container input[type="range"]:active::-webkit-slider-thumb {
  width: 18px;
  height: 18px;
}
.gallery-editor-container input[type="range"]:hover::-moz-range-thumb,
.gallery-editor-container input[type="range"]:focus::-moz-range-thumb,
.gallery-editor-container input[type="range"]:active::-moz-range-thumb {
  width: 18px;
  height: 18px;
}

/* Layer-row opacity slider — fixed track, only thumb grows on
   interaction. Takes a bit of row space at rest, but no jumpy expand
   animation. */
.ge-layer-opacity {
  width: 70px;
  height: 6px;
  accent-color: var(--red);
  flex-shrink: 0;
  -webkit-appearance: none;
  appearance: none;
  background: color-mix(in srgb, var(--fg) 25%, transparent);
  border-radius: 999px;
  position: relative;
  top: 0;
}
.ge-layer-opacity:hover::-webkit-slider-thumb,
.ge-layer-opacity:active::-webkit-slider-thumb,
.ge-layer-opacity.dragging::-webkit-slider-thumb {
  width: 18px;
  height: 18px;
}
.ge-layer-opacity:hover::-moz-range-thumb,
.ge-layer-opacity:active::-moz-range-thumb,
.ge-layer-opacity.dragging::-moz-range-thumb {
  width: 18px;
  height: 18px;
}
/* Shrink the slider thumb — the native default is ~16-20 px which looks
   massive next to a 3 px track in the compact layer row. */
.ge-layer-opacity::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--red);
  border: none;
  cursor: pointer;
  /* Match the track: shrink only after a 0.5s grace period. */
  transition: width 0.12s ease 0.5s, height 0.12s ease 0.5s;
}
.ge-layer-opacity::-moz-range-thumb {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--red);
  border: none;
  cursor: pointer;
  transition: width 0.12s ease 0.5s, height 0.12s ease 0.5s;
}
.ge-layer-opacity:hover::-webkit-slider-thumb,
.ge-layer-opacity:active::-webkit-slider-thumb,
.ge-layer-opacity.dragging::-webkit-slider-thumb,
.ge-layer-opacity:hover::-moz-range-thumb,
.ge-layer-opacity:active::-moz-range-thumb,
.ge-layer-opacity.dragging::-moz-range-thumb {
  transition: width 0.12s ease 0s, height 0.12s ease 0s;
}
.ge-layer-controls {
  display: flex;
  gap: 2px;
  flex-shrink: 0;
}
/* Left-edge drag handle on each layer row. */
.ge-layer-drag {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 12px;
  margin-right: 2px;
  opacity: 0.4;
  cursor: grab;
  flex-shrink: 0;
}
.ge-layer-drag:hover { opacity: 0.85; }
/* While dragSort lifts the item via absolute positioning, the placeholder
   slot shows where it'll land — give it a subtle red outline so users see
   the target clearly. */
.ge-layers-list .drag-placeholder {
  background: color-mix(in srgb, var(--red) 12%, transparent);
  border: 1px dashed color-mix(in srgb, var(--red) 50%, transparent);
  border-radius: 4px;
  box-sizing: border-box;
}
.ge-layer-item.dragging {
  background: var(--panel);
  box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 25%, transparent);
  border-radius: 4px;
}
.ge-layer-btn {
  border: none;
  background: none;
  color: var(--fg);
  cursor: pointer;
  font-size: 12px;
  padding: 0 2px;
  opacity: 0.4;
  transition: opacity 0.1s;
}
.ge-layer-btn:hover { opacity: 0.8; }
.ge-layer-btn.danger:hover { color: var(--red); opacity: 1; }

/* Crop apply button */
.ge-crop-apply {
  position: absolute;
  z-index: 10;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 6px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
.ge-crop-apply input[type="number"] {
  width: 56px;
  padding: 3px 4px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  font: inherit;
  font-size: 11px;
  -moz-appearance: textfield;
}
.ge-crop-apply input[type="number"]::-webkit-outer-spin-button,
.ge-crop-apply input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.ge-crop-x { font-size: 11px; opacity: 0.5; }
.ge-crop-apply-btn {
  padding: 4px 10px;
  background: var(--red);
  color: #fff;
  border: none;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  position: relative;
  top: -2px;
}
.ge-crop-apply-btn:hover { background: color-mix(in srgb, var(--red) 85%, #000); }

/* Text input floating */
.ge-text-input {
  background: var(--bg);
  border: 1px solid var(--red);
  color: var(--fg);
  padding: 4px 8px;
  font-size: 14px;
  border-radius: 4px;
  min-width: 120px;
}
.ge-brush-cursor {
  position: fixed; pointer-events: none; z-index: 10000;
  border: 1.5px solid #fff; border-radius: 50%;
  display: none;
  box-sizing: border-box;
}
.ge-brush-cursor::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 3px; height: 3px;
  margin: -1.5px 0 0 -1.5px;
  background: rgba(255,255,255,0.8);
  border-radius: 50%;
}
.ge-inpaint-section {
  padding: 8px 0; border-top: 1px solid var(--border); margin-top: 4px;
}
.ge-inpaint-popover-head {
  display: none;
}
@media (min-width: 821px) {
  .ge-controls.ge-inpaint-popover-host {
    padding: 0;
    border-bottom: 0;
    gap: 0;
  }
  .ge-inpaint-section.ge-inpaint-popover {
    position: fixed;
    z-index: 10004;
    width: 330px;
    max-height: calc(100vh - 24px);
    overflow-y: auto;
    padding: 10px;
    margin: 0;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--panel);
    box-shadow: 0 12px 36px rgba(0, 0, 0, 0.42);
  }
  .ge-inpaint-section.ge-inpaint-popover .ge-inpaint-popover-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin: -10px -10px 8px;
    padding: 8px 10px;
    border-bottom: 1px solid var(--border);
    cursor: grab;
    user-select: none;
  }
  .ge-inpaint-section.ge-inpaint-popover > .ge-section-title-with-help:not(.ge-inpaint-popover-title) {
    display: none;
  }
  .ge-inpaint-section.ge-inpaint-popover .ge-inpaint-popover-title {
    margin: 0;
    flex: 1 1 auto;
  }
  .ge-inpaint-section.ge-inpaint-popover .ge-inpaint-popover-close {
    flex: 0 0 auto;
    position: relative;
    top: -8px;
    border: none;
    background: none;
    color: var(--fg-muted);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    padding: 0 4px;
    border-radius: 4px;
    transition: color 0.14s ease, background-color 0.14s ease;
  }
  .ge-inpaint-section.ge-inpaint-popover .ge-inpaint-popover-close:hover {
    color: var(--fg);
    background: color-mix(in srgb, var(--fg) 10%, transparent);
  }
}
.ge-inpaint-model-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.ge-inpaint-model-row label {
  flex: 0 0 auto;
  font-size: 10px;
  opacity: 0.65;
}
.ge-inpaint-model-row #ge-ai-inpaint {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 10px;
  padding: 4px 6px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
}
.ge-inpaint-prompt {
  width: 100%; padding: 6px 8px; background: var(--bg); color: var(--fg);
  border: 1px solid var(--border); border-radius: 4px; font-size: 12px;
  font-family: inherit; margin-top: 4px; box-sizing: border-box;
}
.ge-inpaint-prompt:focus { border-color: var(--red); outline: none; }

/* Responsive: stack on narrow screens */
@media (max-width: 700px) {
  /* The editor body holds the sidebar | canvas | layers panel; on
     mobile we stack them as toolbar (top) → canvas (middle) → layers
     (bottom). Without this the body stays a flex row and the canvas
     ends up zero-width because the sidebar takes the full width. */
  .ge-editor-body {
    flex-direction: column;
  }
  /* Brush/Size-style rows: stack the label above its slider so the
     slider gets a full row instead of getting smashed against the
     panel's right edge. Doesn't touch .ge-eraser-row which has its own
     value-chip-on-the-right layout. */
  .ge-control-row:not(.ge-eraser-row):has(input[type="range"]) {
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
  }
  .ge-control-row:not(.ge-eraser-row):has(input[type="range"]) > input[type="range"] {
    width: 100%;
  }
  /* More breathing room around lasso/wand sliders on mobile so they
     don't hug the right edge of the controls panel. */
  #ge-lasso-section .ge-eraser-row,
  #ge-wand-section .ge-eraser-row {
    padding-right: 12px;
    margin-top: 6px;
  }
  /* Lasso action buttons (Clear / Copy / To Mask / Bg Remove): pin to
     the right edge of the row instead of left-aligned. */
  #ge-lasso-section .ge-actions,
  #ge-wand-section .ge-actions {
    justify-content: flex-end;
  }
  /* Universal thumb-grow on active for every slider on mobile. */
  .ge-controls input[type="range"]:active::-webkit-slider-thumb,
  .ge-controls input[type="range"].is-using::-webkit-slider-thumb {
    width: 22px;
    height: 22px;
  }
  .ge-controls input[type="range"]:active::-moz-range-thumb,
  .ge-controls input[type="range"].is-using::-moz-range-thumb {
    width: 22px;
    height: 22px;
  }
  .ge-controls input[type="range"]::-webkit-slider-thumb,
  .ge-controls input[type="range"]::-moz-range-thumb {
    transition: width 0.12s ease, height 0.12s ease;
  }
  .ge-toolbar {
    flex-direction: row;
    width: 100%;
    height: auto;
    padding: 4px 0;
    border-right: none;
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    overflow-y: hidden;
    flex-shrink: 0;
    gap: 0;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .ge-toolbar::-webkit-scrollbar { display: none; }
  /* Section labels in the toolbar were vertical breaks — turn them
     into thin vertical dividers when the column flows horizontally. */
  .ge-tool-sep {
    border-top: none;
    border-left: 1px solid var(--border);
    margin: 0 4px;
    padding: 0;
    width: 1px;
    height: 24px;
    text-indent: -9999px;
    overflow: hidden;
    flex-shrink: 0;
  }
  .ge-tool-btn {
    flex-shrink: 0;
    width: 48px;
  }
  /* Canvas is the main thing — give it as much vertical space as
     the editor body can spare. min-height keeps it visible even when
     the right panel is at its max. */
  .ge-canvas-area {
    flex: 1 1 auto;
    min-height: 40vh;
  }
  /* Right panel = layers list. Docked as a bottom sheet with a peek
     state: collapsed shows the header strip (and the active layer
     row peeks through), expanded reveals all layers + actions. The
     user swipes up to expand, down to collapse — the active layer is
     never fully hidden. */
  .ge-right-panel {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    max-height: 70vh;
    height: 70vh;
    background: var(--panel);
    border-left: none;
    border-top: 1px solid var(--border);
    border-radius: 12px 12px 0 0;
    box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.35);
    z-index: 50;
    flex-shrink: 0;
    /* Peek height grows with the layer count up to a cap. JS sets the
       `--peek-height` custom property from _renderLayerPanel(); default
       fallback is enough for the header + one row. */
    transform: translateY(calc(100% - var(--peek-height, 110px)));
    transition: transform 0.22s ease-out;
  }
  .ge-right-panel.expanded {
    transform: translateY(0);
  }
  /* Minimized: panel slides almost fully off-screen — only a thin
     handle strip remains visible so the user can swipe back up. */
  .ge-right-panel.minimized {
    transform: translateY(calc(100% - 24px));
  }
  .ge-layers-grab {
    display: block !important;
    position: absolute;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    width: 40px;
    height: 4px;
    background: color-mix(in srgb, var(--fg) 30%, transparent);
    border-radius: 2px;
  }
  .ge-layers-header {
    padding-top: 16px;
    position: relative;
  }
  /* In peek state, the layers list takes the full panel height (~70vh)
     and never overflows so its own scrollbar doesn't activate. Constrain
     the list to the visible peek area so it scrolls properly when there
     are more layers than the peek can show. */
  .ge-right-panel:not(.expanded) .ge-layers-list {
    max-height: calc(var(--peek-height, 110px) - 52px);
    -webkit-overflow-scrolling: touch;
  }
  /* Mobile layer-row buttons — bump hit targets up so fingers can
     reliably tap visibility / fx / delete. */
  .ge-layer-vis,
  .ge-layer-btn,
  .ge-layer-fx-btn {
    width: 40px !important;
    height: 40px !important;
    font-size: 18px !important;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .ge-layer-btn svg,
  .ge-layer-fx-btn svg {
    width: 18px;
    height: 18px;
  }
  /* Layer opacity slider stays at its expanded size at all times on
     mobile — there's room for it and the resize-on-hover felt jittery
     when scrolling. */
  .ge-layer-opacity,
  .ge-layer-opacity:hover,
  .ge-layer-opacity:active,
  .ge-layer-opacity.dragging {
    width: 160px !important;
    height: 10px !important;
  }
  /* Transform / adjust popups dock at the TOP of the viewport on
     mobile so they overlay the topbar instead of eating canvas space.
     Fixed positioning escapes any transformed ancestor stacking. */
  .ge-transform-popup,
  .ge-adj-popup,
  .ge-fx-popup {
    position: fixed !important;
    left: 8px !important;
    right: 8px !important;
    top: 32px !important;
    bottom: auto !important;
    max-width: calc(100vw - 16px) !important;
    width: auto !important;
    min-width: 0 !important;
    min-height: 240px !important;
    max-height: calc(100vh - 40px);
    overflow-y: auto;
    z-index: 10003 !important;
  }
  .ge-fx-menu {
    position: fixed !important;
    left: 8px !important;
    right: 8px !important;
    top: auto !important;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 8px) !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: calc(100vw - 16px) !important;
    z-index: 10002 !important;
  }
  /* Hide both the minimise and × close buttons in the header on
     mobile — a clearly labelled Cancel button next to Apply in the
     body is more discoverable than tiny header icons. */
  .ge-transform-popup #ge-transform-min,
  .ge-transform-popup #ge-transform-cancel {
    display: none !important;
  }
  /* Wrap the transform body across three rows on mobile:
       Row 1: W + H
       Row 2: ↻ rotate input + rotate-90 quick
       Row 3: flip-h flip-v ............ Apply  */
  .ge-transform-popup-body {
    flex-wrap: wrap !important;
    align-items: center;
    row-gap: 8px;
    column-gap: 6px;
  }
  .ge-transform-popup-body #ge-transform-rot {
    width: 3.5ch !important;
    min-width: 0 !important;
  }
  /* 3-row layout, enforced by explicit `.ge-row-break` elements in the
     DOM (the only reliable way to force a flex-wrap line break).
       Row 1: W input + H input
       Row 2: aspect lock + ↻ rotate input
       Row 3: flip-h, flip-v, rot-90 + Cancel + Apply */
  .ge-transform-popup-body .ge-row-break {
    display: block;
    flex-basis: 100%;
    height: 0;
    width: 100%;
  }
  /* Flip buttons slightly bigger so they're easier to tap. */
  .ge-transform-quick-btn {
    padding: 8px 10px !important;
    min-width: 40px;
    min-height: 40px;
  }
  .ge-transform-quick-btn svg {
    width: 18px;
    height: 18px;
  }
  /* Cancel + Apply share the right side of row 2, same size, paired. */
  .ge-transform-popup-body #ge-transform-cancel-btn {
    margin-left: auto;
  }
  .ge-transform-popup-body #ge-transform-cancel-btn,
  .ge-transform-popup-body #ge-transform-apply {
    padding: 8px 16px !important;
    font-size: 13px !important;
    min-width: 80px;
    text-align: center;
  }
  /* Stepper-style number control on mobile:
       [ − ]  [ input ]  [ + ]
     Stack reverses so the − sits visually on the LEFT of the input, +
     on the RIGHT. Big finger-sized squares, big +/- glyphs, both
     buttons support tap-and-hold auto-repeat (JS) for fast scrubbing. */
  .ge-transform-field {
    display: inline-flex !important;
    align-items: stretch;
    gap: 0;
  }
  .ge-transform-field > label {
    display: inline-flex;
    align-items: center;
    padding-right: 6px;
    font-weight: 600;
  }
  .ge-transform-field:not(:has(#ge-transform-rot)) > label {
    position: relative;
    top: -1px;
  }
  .ge-transform-spin {
    display: inline-flex !important;
    flex-direction: row !important;
    /* Use grid-order to swap positions of [−][+]: the − should sit
       BEFORE the input, the + AFTER it. Achieve this by absolute
       order — first child styled differently, second styled
       differently, then we move the whole .ge-transform-spin's first
       button visually before the input via reverse-flex. */
    order: 0;
    width: auto !important;
  }
  .ge-transform-field {
    flex-direction: row;
  }
  /* Re-flow:  label  +  [-]  +  input  +  [+]
     We split the .ge-transform-spin's two buttons across the field's
     flex line using order: order:0 for −, order:2 for input, order:3
     for +. The spin <span> itself gets `display: contents` so its
     children can be reordered independently against the input. */
  .ge-transform-spin {
    display: contents !important;
  }
  .ge-transform-field > input.ge-transform-popup-input {
    order: 2;
    text-align: center;
  }
  .ge-transform-spin button[data-spin="down"] {
    order: 1;
  }
  .ge-transform-spin button[data-spin="up"] {
    order: 3;
  }
  /* The buttons themselves — chunky, square, identical in size. Reset
     the base `:first-child` rule (which strips the bottom border for
     the old stacked layout) so both buttons render with the same
     visual box in the new row layout. */
  .ge-transform-spin button {
    box-sizing: border-box !important;
    width: 36px !important;
    height: 36px !important;
    min-width: 36px;
    min-height: 36px;
    padding: 0 0 0 0 !important;
    font-size: 18px !important;
    font-weight: 700;
    line-height: 1 !important;
    border: 1px solid var(--border) !important;
    border-radius: 6px !important;
    flex-shrink: 0;
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    position: relative;
    top: -4px;
  }
  .ge-transform-spin button:first-child,
  .ge-transform-spin button:last-child {
    border-radius: 6px !important;
    border-bottom: 1px solid var(--border) !important;
  }
  /* The × glyph in close buttons also sits visually low — lift it 4 px
     to match the +/- button alignment. Same asymmetric-padding trick. */
  .modal-close,
  .close-btn,
  .ge-adj-close,
  .ge-adj-min {
    padding-top: 0 !important;
    padding-bottom: 8px !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
  }
  .ge-transform-spin button[data-spin="down"] { margin-right: 4px; }
  .ge-transform-spin button[data-spin="up"]   { margin-left: 4px; }
  /* The input between them — generous tap area and clear typography. */
  .ge-transform-field > input.ge-transform-popup-input {
    height: 36px !important;
    padding: 0 6px !important;
    font-size: 14px !important;
    min-width: 56px;
    position: relative;
    top: 2px;
  }
  /* Rotate input — compact so it fits on row 2 alongside the flip
     quick buttons and the Cancel + Apply pair. */
  .ge-transform-field > #ge-transform-rot.ge-transform-popup-input {
    min-width: 0;
    width: 64px !important;
  }
  /* Suffix (°) sits right after the rotate input but before the + button. */
  .ge-transform-popup-suffix { order: 3; align-self: center; padding: 0 2px; }
  /* Slider thumb grows only while actively dragged — keeps the resting
     row compact, thumb pops larger when the user grabs it. */
  .gallery-editor-container input[type="range"]:active::-webkit-slider-thumb,
  .ge-layer-opacity:active::-webkit-slider-thumb,
  .ge-layer-opacity.dragging::-webkit-slider-thumb {
    width: 22px !important;
    height: 22px !important;
  }
  .gallery-editor-container input[type="range"]:active::-moz-range-thumb,
  .ge-layer-opacity:active::-moz-range-thumb,
  .ge-layer-opacity.dragging::-moz-range-thumb {
    width: 22px !important;
    height: 22px !important;
  }
  /* Slider value bubble on mobile — bigger text/padding for
     readability. Position is JS-driven via `position: fixed` from the
     base rule, so don't override top/bottom here — `bottom: 100%`
     with fixed positioning was pushing the bubble off-screen. */
  .ge-slider-bubble {
    font-size: 14px !important;
    padding: 5px 10px !important;
  }
  /* Editor topbar — scrolls horizontally on narrow viewports. Buttons
     fill more vertical+horizontal space so each is a comfortable
     finger-tap target instead of feeling cramped. */
  .ge-topbar {
    overflow-x: auto;
    overflow-y: hidden;
    flex-wrap: nowrap;
    justify-content: flex-start;
    gap: 6px;
    padding: 6px 8px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .ge-topbar::-webkit-scrollbar { display: none; }
  .ge-topbar-left,
  .ge-topbar-right {
    flex-shrink: 0;
    gap: 6px;
  }
  .ge-alpha-badge {
    display: none;
  }
  #ge-undo {
    position: sticky !important;
    left: 8px;
    background: var(--panel);
    z-index: 8;
    box-shadow: 8px 0 10px -10px color-mix(in srgb, var(--fg) 55%, transparent);
  }
  .ge-topbar .ge-btn,
  .ge-topbar .ge-btn-sm {
    padding: 7px 11px !important;
    font-size: 13px !important;
    min-height: 34px;
    line-height: 1;
    top: 0 !important;
  }
  .ge-topbar .ge-btn svg,
  .ge-topbar .ge-btn-sm svg {
    width: 16px;
    height: 16px;
  }
  .ge-topbar select,
  .ge-topbar input,
  .ge-topbar .ge-ai-model {
    padding: 6px 8px !important;
    font-size: 12px !important;
    min-height: 32px;
    top: 0 !important;
  }
  .ge-topbar .ge-canvas-size {
    font-size: 12px !important;
    padding: 0 6px;
    line-height: 34px;
  }
  #ge-history-panel {
    left: 0 !important;
    right: 0 !important;
    top: auto !important;
    bottom: 0 !important;
    width: auto !important;
    max-height: min(60dvh, 420px);
    border-radius: 12px 12px 0 0 !important;
    z-index: 10001 !important;
    box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.35);
  }
  #ge-history-panel .ge-history-head { cursor: default; }
  #ge-shortcuts-btn {
    display: none !important;
  }
  /* Topbar popups that need real interaction (Resize sliders, Edge
     form, Save with sub-items) become bottom-sheet slide-ups on
     mobile so there's room to interact. Image and Filter are tight
     single-column dropdowns — they stay anchored to their button. */
  .ge-resize-menu,
  .ge-edge-menu,
  .ge-save-menu {
    position: fixed !important;
    left: 0 !important;
    right: 0 !important;
    top: auto !important;
    bottom: 0 !important;
    min-width: 0 !important;
    max-width: 100vw !important;
    width: auto;
    padding: 18px 16px 24px !important;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 12px 12px 0 0 !important;
    box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.35);
    z-index: 10005;
    animation: ge-controls-slide-up 0.2s ease-out;
  }
  /* Image / Filter stay as anchored dropdowns; just cap width
     so they don't run off-screen on narrow viewports. */
  .ge-image-menu,
  .ge-filter-menu {
    max-width: calc(100vw - 16px);
  }
  .ge-resize-menu::before,
  .ge-edge-menu::before,
  .ge-save-menu::before {
    content: '';
    position: absolute;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    width: 40px;
    height: 4px;
    background: color-mix(in srgb, var(--fg) 30%, transparent);
    border-radius: 2px;
  }
  /* The Edge form needs a bit of breathing room when it spans the
     viewport; the W/H inputs in the crop apply pop should stay compact. */
  .ge-edge-menu { padding: 12px; }
  .ge-canvas-prompt {
    width: calc(100vw - 16px) !important;
    max-width: none !important;
  }
  /* Inpaint floating prompt — keep it inside the canvas area on
     mobile so it doesn't run off the right edge. */
  .ge-inpaint-popup {
    max-width: calc(100vw - 16px);
  }
  .ge-inpaint-popup-input { width: 160px; }

  /* Tool-specific controls (brush size/color, eraser opacity/flow,
     lasso feather, inpaint prompt panel, etc.) lift out of the right
     panel and dock as a slide-up bottom sheet so they have actual room
     to breathe on a phone. A short grab-handle visually separates it
     from the canvas. */
  .ge-controls {
    position: fixed !important;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 60;
    border-bottom: none;
    border-top: 1px solid var(--border);
    background: var(--panel);
    border-radius: 12px 12px 0 0;
    box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.35);
    max-height: 60vh;
    overflow-y: auto;
    padding: 18px 16px 24px;
    gap: 12px;
    /* Slide-up entrance animation when the controls become visible. */
    animation: ge-controls-slide-up 0.18s ease-out;
  }
  .ge-controls::before {
    content: '';
    position: absolute;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    width: 40px;
    height: 4px;
    background: color-mix(in srgb, var(--fg) 30%, transparent);
    border-radius: 2px;
  }
  @keyframes ge-controls-slide-up {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
  }
  /* User swiped the sheet down — keep tool active but hide its
     controls. Re-tapping the same tool button toggles back. */
  .ge-controls.dismissed {
    transform: translateY(100%);
    pointer-events: none;
    /* Smooth slide-out matching the slide-in duration. */
    animation: none;
    transition: transform 0.18s ease-in;
  }
}

/* ── Group Chat ────────────────────────────────────── */
.msg-group .role {
  font-weight: 600;
}
#group-toggle-btn.active,
.overflow-menu-item#overflow-group-btn.active {
  color: var(--red);
  background: color-mix(in srgb, var(--red) 12%, transparent);
}
/* Group model picker — match app theme */
#group-model-picker .modal-content {
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
}
#group-model-picker .modal-header {
  border-bottom: 1px solid var(--border);
}
#group-model-picker .memory-item {
  border-radius: 6px;
  transition: background 0.1s;
}
#group-model-picker .memory-item:hover {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
}
#group-model-picker input[type="checkbox"] {
  accent-color: var(--accent, var(--red));
}
#group-model-picker input[type="radio"] {
  accent-color: var(--accent, var(--red));
}
#group-model-picker .btn-primary {
  background: var(--accent, var(--red));
  color: #fff;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-weight: 500;
}
#group-model-picker .btn-primary:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
#group-model-picker .btn-primary:hover:not(:disabled) {
  filter: brightness(1.1);
}

/* ── Email document type ── */
.doc-email-header {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--bg);
  flex-shrink: 0;
}
.email-field {
  display: flex;
  align-items: center;
  gap: 8px;
}
.email-field label {
  font-size: 11px;
  font-weight: 600;
  color: var(--fg);
  opacity: 0.5;
  min-width: 50px;
  text-align: right;
}
.email-field input {
  flex: 1;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 5px 8px;
  font-size: 13px;
  font-family: inherit;
  color: var(--fg);
  outline: none;
}
.email-field input:focus {
  border-color: var(--accent, #4a9eff);
}
.email-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 2px;
}
.email-draft-btn {
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 6px 14px;
  font-size: 12px;
  cursor: pointer;
  font-family: inherit;
}
.email-draft-btn:hover { background: var(--hover-bg, rgba(255,255,255,0.05)); }
.email-draft-btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── Email inbox list ── */
.email-body { border-top: 1px solid var(--border); }
.email-folder-bar { padding: 4px 8px; border-bottom: 1px solid var(--border); }
.email-folder-select {
  background: transparent; border: 1px solid var(--border); border-radius: 4px;
  color: var(--fg); font-size: 11px; padding: 2px 6px; cursor: pointer; width: 100%; height: 26px;
}
.email-list { overflow-y: auto; max-height: 400px; }
.email-item {
  display: flex; align-items: flex-start; gap: 8px; padding: 8px 12px;
  cursor: pointer; border-bottom: 1px solid var(--border); position: relative; transition: background 0.1s;
}
.email-item:hover { background: var(--hover-bg, rgba(255,255,255,0.03)); }
.email-item:hover .email-menu-btn { opacity: 0.5; }
.email-item-spam { opacity: 0.4; }
.email-item-spam:hover { opacity: 0.75; }
.email-tag-spam {
  display: inline-flex; align-items: center; gap: 4px;
  background: color-mix(in srgb, var(--accent, var(--red)) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 30%, transparent);
  color: var(--accent, var(--red));
  border-radius: 10px; padding: 1px 6px; font-size: 9px; font-weight: 600;
  text-transform: lowercase; letter-spacing: 0.3px;
}
.email-spam-unflag {
  background: none; border: none; color: inherit; cursor: pointer;
  padding: 0 2px; font-size: 10px; line-height: 1; opacity: 0.6;
}
.email-spam-unflag:hover { opacity: 1; color: var(--fg); }
/* Unread state is now a subtle icon, not full styling */
.email-done-check {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; border-radius: 50%; background: transparent;
  border: 1.5px solid var(--border); color: transparent;
  flex-shrink: 0; cursor: pointer; transition: all 0.15s;
  margin-right: 4px;
}
.email-done-check:hover { border-color: var(--accent, #4a9eff); }
.email-done-check.active { background: var(--accent, #4a9eff); border-color: var(--accent, #4a9eff); color: #fff; }
.email-done-check.active svg { display: block; }
.email-done-check svg { display: none; }

/* Email titles that carry a cached AI summary get a thin underline-dot hint
   so the user knows a hover-preview is available. */
.memory-item-title.email-card-has-summary {
  cursor: help;
  text-decoration: underline dotted color-mix(in srgb, var(--accent) 60%, transparent) 1px;
  text-underline-offset: 3px;
}

.doclib-card.email-card-removing {
  pointer-events: none !important;
  max-height: 0 !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  border-color: transparent !important;
  opacity: 0;
  transform: translateX(-10px) scale(0.985);
  transition:
    opacity 0.18s ease,
    transform 0.22s ease,
    max-height 0.23s ease,
    padding 0.23s ease,
    margin 0.23s ease,
    border-color 0.18s ease;
}

@media (prefers-reduced-motion: reduce) {
  .doclib-card.email-card-removing {
    transition: opacity 0.08s ease;
    transform: none;
  }
}

/* Library card done check — always visible, subtle icon next to title */
.email-card-done {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; cursor: pointer;
  transition: opacity 0.15s, color 0.15s;
  opacity: 0.15; color: var(--fg);
}
/* Hover preview: bright accent when unchecked so the user sees a check coming.
   Once active, keep the exact same color on hover so the done state does not
   visually flip while the pointer is still over it. */
.email-card-done:not(.active):hover {
  opacity: 0.75 !important;
  color: var(--accent-primary, var(--red));
}
.email-card-done.active { opacity: 0.95; color: var(--accent-primary, var(--red)); }
.email-card-done.active:hover {
  opacity: 0.95 !important;
  color: var(--accent-primary, var(--red)) !important;
}
.email-card-done.just-checked {
  animation: check-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Reverse animation when un-checking — shrink-and-fade so the click is
   obviously felt even when hover styling otherwise keeps the icon visible. */
.email-card-done.just-unchecked {
  animation: check-unpop 0.42s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes check-pop {
  0% { transform: scale(1); }
  40% { transform: scale(1.7) rotate(-10deg); }
  70% { transform: scale(1.1) rotate(4deg); }
  100% { transform: scale(1) rotate(0); }
}
@keyframes check-unpop {
  0%   { transform: scale(1); opacity: 0.95; }
  35%  { transform: scale(0.45) rotate(8deg); opacity: 0.05; }
  65%  { transform: scale(0.9); opacity: 0.2; }
  100% { transform: scale(1); }
}

/* Expanded email preview in library — override .memory-item row layout */
.doclib-card.email-card-expanded.memory-item {
  cursor: default !important;
  background: var(--bg) !important;
  border: 1px solid var(--border) !important;
  display: flex !important;
  flex-direction: column !important;
  align-items: stretch !important;
  /* Fill the available grid height instead of the old hardcoded 70vh —
     the modal already caps height (90dvh on mobile, auto on desktop), so
     the card just needs to flex into whatever remains. */
  flex: 1 1 auto !important;
  height: 100% !important;
  min-height: 0 !important;
  max-height: none !important;
  padding: 0 !important;
  overflow: hidden !important;
  gap: 0 !important;
}
.doclib-card.email-card-expanded.memory-item > div:first-child {
  /* The summary content (subject, date) - acts as the title bar. Left padding
     trimmed 14→6px to shift the title 8px left total. */
  padding: 2px 4px 10px !important;  /* top trimmed to lift the header text up */
  margin-top: -14px !important;      /* pull the whole bar up into the freed space */
  border-bottom: 1px solid var(--border);
  flex: 0 0 auto !important;
  width: 100%;
}
.doclib-card.email-card-expanded .memory-item-actions {
  display: none !important;
}
/* When expanded inline, show the subject a touch larger than the 12px card
   title (close to the new-tab window header) without being oversized. */
.doclib-card.email-card-expanded .memory-item-title {
  font-size: 14px;
  font-weight: 600;
}
/* The sender name is redundant once expanded (the From: row shows it just
   below), so drop it and keep only the date — which then sits left-aligned
   directly under the subject. Nudge the date 1px right to line up exactly with
   the subject text, and up closer to it. */
.doclib-card.email-card-expanded .email-meta-sender,
.doclib-card.email-card-expanded .email-meta-sep {
  display: none;
}
.doclib-card.email-card-expanded .memory-item-meta {
  margin-top: -9px !important;
}
.doclib-card.email-card-expanded .email-meta-date {
  margin-left: 3px;
}
.email-card-reader {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  font-size: 12px;
}
.email-card-reader-loading {
  background: color-mix(in srgb, var(--panel) 82%, transparent);
}
.email-card-reader-loading .spinner-whirlpool,
.email-card-reader-loading .ai-spinner-whirlpool {
  opacity: 0.85;
}
/* Per-email unread dot in the expanded reader's title row. Background color
   stays inline (per-sender hue from _senderColor), but the dot gets a soft
   breathing glow + a 4px vertical nudge so it reads as centered against
   the row instead of riding the baseline. */
.email-card-unread-dot {
  position: relative;
  top: 0;
  animation: email-card-unread-breathe 2.2s ease-in-out infinite;
}
@keyframes email-card-unread-breathe {
  0%, 100% {
    box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 0%, transparent);
    opacity: 0.85;
    transform: scale(1);
  }
  50% {
    /* currentColor here is unreliable (background, not color) — use a
       subtle white-tinted halo so the glow shows on any sender hue. */
    box-shadow: 0 0 6px 2px rgba(255, 255, 255, 0.18);
    opacity: 1;
    transform: scale(1.15);
  }
}
@media (prefers-reduced-motion: reduce) {
  .email-card-unread-dot { animation: none; }
}
.email-reader-header {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 12px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--bg);
  flex-shrink: 0;
}
.email-reader-header > .email-reader-meta {
  flex: 1; min-width: 0;
}
.email-reader-meta {
  min-width: 0; opacity: 0.85; line-height: 1.7; font-size: 11px;
  display: flex; flex-direction: column; gap: 4px;
}
.email-reader-meta-row {
  display: flex; align-items: center; gap: 6px;
  min-width: 0;
}
.email-reader-meta-row strong { opacity: 0.5; font-weight: 600; flex-shrink: 0; min-width: 36px; }
.email-reader-meta-row > span {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  min-width: 0; flex: 1;
}
/* Recipient chips */
.recipient-chips {
  display: inline-flex !important; flex-wrap: wrap; gap: 4px;
  white-space: normal !important; overflow: visible !important;
  text-overflow: clip !important;
}
.recipient-chip {
  display: inline-flex; align-items: center;
  gap: 5px;
  padding: 1px 8px; font-size: 10px;
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--fg);
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, max-width 0.2s;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.recipient-chip-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.recipient-chip-copy {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  top: -2px;
  width: 14px;
  height: 14px;
  padding: 0;
  border: none;
  background: none;
  color: inherit;
  opacity: 0.55;
  cursor: pointer;
  flex: 0 0 auto;
}
.recipient-chip-copy:hover,
.recipient-chip-copy.copied {
  opacity: 1;
  color: var(--accent-primary, var(--red));
}
.recipient-chip-copy[hidden] {
  display: none !important;
}
.recipient-chip:hover {
  background: color-mix(in srgb, var(--accent-primary, var(--red)) 12%, transparent);
  border-color: color-mix(in srgb, var(--accent-primary, var(--red)) 40%, transparent);
}
.recipient-chip.expanded {
  background: color-mix(in srgb, var(--accent-primary, var(--red)) 15%, transparent);
  border-color: var(--accent-primary, var(--red));
  max-width: 500px;
}
@container docpane (max-width: 460px) {
  .email-reader-header {
    flex-direction: column;
    gap: 6px;
  }
  .email-reader-actions {
    align-self: flex-end;
  }
  .email-reader-meta-row {
    display: grid;
    grid-template-columns: 1fr;
    gap: 2px;
    align-items: start;
  }
  .email-reader-meta-row strong {
    min-width: 0;
  }
  .recipient-chip {
    max-width: 100%;
  }
}
.email-reader-actions {
  display: flex; gap: 4px; flex-wrap: nowrap; align-items: center;
  flex-shrink: 0;
  justify-content: flex-end;
  margin-top: -4px;
}
/* The HTML wraps the buttons in two .email-reader-actions-row divs (primary
   + secondary). On mobile those flatten via `display: contents` inside the
   max-width:768px block; apply the same here so the whole row stays on one
   line on desktop too. */
.email-reader-actions-row {
  display: contents;
}
.email-reader-atts {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.email-reader-body {
  font-size: 12px;
  line-height: 1.55;
  white-space: pre-wrap;
  word-wrap: break-word;
  overflow-wrap: anywhere;
  flex: 1;
  overflow-y: auto;
  padding: 14px;
  min-height: 0;
  /* Use the OS colored emoji font for incoming mail content */
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif,
               "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla";
  font-variant-emoji: emoji;
}
/* When rendering HTML emails, override pre-wrap so layout works */
.email-reader-body.html-body {
  white-space: normal;
}
.email-reader-body.html-body img { max-width: 100%; height: auto; }
.email-reader-body.html-body table { max-width: 100%; border-collapse: collapse; }
/* Preserve highlights — sender's intent (snark, callouts) gets through, but
   rendered with a theme-aware accent so it stays legible in any theme. The
   sanitizer rewrites `<span style="background:yellow">…</span>` to <mark>. */
.email-reader-body mark {
  background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
  color: inherit;
  padding: 0 2px;
  border-radius: 2px;
}

/* "Other from this sender" — slide-out panel inside the email reader. */
.email-card-reader.from-sender-open { position: relative; }
.from-sender-panel {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 280px;
  background: var(--bg);
  border-left: 1px solid var(--border);
  box-shadow: -4px 0 16px rgba(0, 0, 0, 0.18);
  z-index: 5;
  display: flex;
  flex-direction: column;
  animation: from-sender-slide 0.22s ease-out;
}
@keyframes from-sender-slide {
  from { transform: translateX(8px); opacity: 0; }
  to   { transform: translateX(0);    opacity: 1; }
}
.from-sender-head {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 12px 12px 10px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.from-sender-title-wrap { flex: 1; min-width: 0; }
.from-sender-title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.from-sender-addr {
  font-size: 10px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  opacity: 0.55;
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.from-sender-count {
  font-size: 10px;
  opacity: 0.6;
  margin-top: 4px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.from-sender-list {
  flex: 1;
  overflow-y: auto;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.from-sender-loading { display: flex; justify-content: center; padding: 24px 0; }
.from-sender-empty { padding: 16px 12px; font-size: 11px; opacity: 0.6; }
/* Chat-bubble feel — each email row is a self-contained darker bubble. */
.from-sender-row {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: stretch;
  width: 100%;
  min-height: 44px;
  text-align: left;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--fg) 10%, transparent);
  border-radius: 12px;
  color: var(--fg);
  font-family: inherit;
  transition: background 0.12s, border-color 0.12s, transform 0.08s;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
  overflow: hidden;
}
.from-sender-row-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  text-align: left;
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
  font-family: inherit;
  /* Shifted text up 2px / left 2px relative to the bubble (padding 8/20 → 6/18). */
  padding: 6px 4px 10px 18px;
  transition: background 0.12s;
}
.from-sender-row-main:hover {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
}
.from-sender-row-main:active { transform: translateY(1px); }
.from-sender-row-more {
  flex: 0 0 auto;
  background: none;
  border: none;
  color: var(--fg);
  cursor: pointer;
  opacity: 0.45;
  width: 28px;
  padding: 0 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.12s, background 0.12s;
}
.from-sender-row-more:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.from-sender-row:hover {
  background: color-mix(in srgb, var(--fg) 12%, transparent);
  border-color: color-mix(in srgb, var(--fg) 20%, transparent);
}
.from-sender-row.from-sender-unread { font-weight: 600; }
.from-sender-row.from-sender-unread::before {
  content: '';
  position: absolute;
  left: 7px;
  top: 50%;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent, var(--red));
  transform: translateY(-50%);
  box-shadow: 0 0 6px color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
}
.from-sender-row-top {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  min-width: 0;
}
.from-sender-row-bottom {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-width: 0;
  margin-top: 3px;
}
.from-sender-subj {
  font-size: 12px;
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.from-sender-att {
  flex-shrink: 0;
  opacity: 0.55;
  color: var(--fg);
}
.from-sender-row:hover .from-sender-att { opacity: 0.85; }
.from-sender-date {
  font-size: 10px;
  opacity: 0.55;
  flex-shrink: 0;
}
.from-sender-folder {
  font-size: 9px;
  opacity: 0.6;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  padding: 1px 5px;
  border-radius: 3px;
  margin-left: auto;
}
/* Header row above search — holds the sender chip, attachment toggle,
   and a small close X for exiting the panel. */
.from-sender-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 8px 8px 12px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.from-sender-header-empty {
  font-size: 11px;
  opacity: 0.6;
  letter-spacing: 0.3px;
  text-transform: uppercase;
}
.from-sender-close {
  background: none;
  border: none;
  color: var(--fg);
  cursor: pointer;
  width: 22px;
  height: 22px;
  padding: 0 0 4px 0;
  font-size: 16px;
  line-height: 1;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.55;
  flex-shrink: 0;
  position: relative;
  top: -4px;
  transition: opacity 0.12s, background 0.12s;
}
.from-sender-close:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
}
.from-sender-toggle {
  /* Default: compact 22x22 circle. When .is-active (filter ON) it stretches
     vertically to match the header's content height — that's the visual cue
     that the filter is engaged. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border: 1px solid var(--border);
  color: var(--fg);
  border-radius: 50%;
  width: 22px;
  height: 22px;
  padding: 0;
  font-family: inherit;
  cursor: pointer;
  opacity: 0.75;
  flex-shrink: 0;
  margin-left: auto;
  position: relative;
  top: -2px;
  transition: opacity 0.12s, background 0.12s, border-color 0.12s, color 0.12s,
              height 0.18s ease, border-radius 0.18s ease;
}
.from-sender-toggle:hover { opacity: 1; }
.from-sender-toggle.is-active {
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 40%, transparent);
  color: var(--accent, var(--red));
  opacity: 1;
  /* Grow vertically to match the chip's height when the filter is engaged. */
  align-self: stretch;
  height: auto;
  border-radius: 999px;
  top: 0;
}
.from-sender-search-wrap {
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
/* The search input now sits alone in its row (chip + toggles moved to the
   header), so render it as a normal field instead of a chip-input child. */
.from-sender-search-wrap > .from-sender-search {
  width: 100%;
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 12px;
  color: var(--fg);
  outline: none;
  transition: border-color 0.15s;
  box-sizing: border-box;
}
.from-sender-search-wrap > .from-sender-search:focus { border-color: var(--accent, var(--red)); }
.from-sender-search-wrap { position: relative; }

/* Multi-chip container in the header — wraps when several chips accumulate. */
.from-sender-chips {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  flex: 1 1 auto;
  min-width: 0;
}
.from-sender-chips:empty { flex: 0 0 0; }

/* Suggestion dropdown rendered just below the search input. */
.from-sender-suggest {
  position: absolute;
  left: 12px;
  right: 12px;
  top: calc(100% - 4px);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.25);
  z-index: 20;
  max-height: 240px;
  overflow-y: auto;
}
.from-sender-suggest-item {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 10px;
  font-size: 12px;
  color: var(--fg);
  cursor: pointer;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
}
.from-sender-suggest-item:last-child { border-bottom: none; }
.from-sender-suggest-item.active {
  background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
}
.from-sender-suggest-item .suggest-name { font-weight: 500; }
.from-sender-suggest-item .suggest-addr {
  font-size: 11px;
  opacity: 0.55;
  margin-left: auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 60%;
}

/* Chip-input search bar — sender chip lives inline with the input;
   X-ing the chip turns it into a global search. */
.from-sender-chip-input {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 6px;
  transition: border-color 0.15s;
}
.from-sender-chip-input:focus-within { border-color: var(--accent, var(--red)); }
.from-sender-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
  color: var(--accent, var(--red));
  border-radius: 999px;
  padding: 2px 4px 2px 8px;
  font-size: 11px;
  font-weight: 500;
  max-width: 60%;
  flex-shrink: 0;
}
.from-sender-chip-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 160px;
}
.from-sender-chip-x {
  background: none;
  border: none;
  color: inherit;
  cursor: pointer;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  padding: 0;
  font-size: 18px;
  line-height: 0.55;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.7;
  position: relative;
  top: -4px;
  transition: opacity 0.12s, background 0.12s;
}
.from-sender-chip-x:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
}
.from-sender-search {
  flex: 1;
  min-width: 80px;
  background: none;
  border: none;
  color: var(--fg);
  font-family: inherit;
  font-size: 12px;
  padding: 4px 4px;
  outline: none;
}

.from-sender-mode-note {
  font-size: 9px;
  opacity: 0.5;
  margin-top: 4px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.email-card-reader.from-sender-open .email-reader-body { padding-right: 292px; }
@media (max-width: 768px) {
  .from-sender-panel { width: 100%; }
  .email-card-reader.from-sender-open .email-reader-body { padding-right: 0; visibility: hidden; }
}
/* Force any phone/sms/date/address auto-link to inherit the body color so
   numbers don't render as bright-blue browser-default text inside email
   content. format-detection in <head> turns off detection on iOS, but some
   mail clients ship pre-wrapped tel: links — those still need taming. */
.email-reader-body a[href^="tel:"],
.email-reader-body a[href^="sms:"],
.email-reader-body a[x-apple-data-detectors],
.email-bubble-body a[href^="tel:"],
.email-bubble-body a[href^="sms:"],
.email-bubble-body a[x-apple-data-detectors],
.email-thread-turn-body a[href^="tel:"],
.email-thread-turn-body a[href^="sms:"],
.email-thread-turn-body a[x-apple-data-detectors] {
  color: inherit !important;
  text-decoration: none !important;
  pointer-events: auto;
}
.email-reader-body a,
.email-bubble-body a,
.email-thread-turn-body a {
  color: var(--accent-primary, var(--red));
  text-decoration: underline;
  overflow-wrap: anywhere;
  word-break: normal;
  cursor: pointer;
}
.email-reader-body a:hover,
.email-bubble-body a:hover,
.email-thread-turn-body a:hover { opacity: 0.8; }
/* Summary block — same band chrome as Attachments / Signature / Earlier-
   thread (no accent tint, no rounded corners, no leading star icon). The
   only difference is the label, so all collapsible sections in the email
   reader look uniform. */
.email-summary-panel {
  margin: 0 0 12px 0;
  padding: 10px 12px;
  background: color-mix(in srgb, var(--accent-primary, var(--red)) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent-primary, var(--red)) 30%, transparent);
  border-radius: 6px;
}
.email-summary-header {
  display: flex; align-items: center; gap: 5px;
  font-size: 10px; font-weight: 600; text-transform: uppercase;
  color: var(--accent-primary, var(--red)); opacity: 0.85;
  margin-bottom: 6px; letter-spacing: 0.4px;
}
.email-summary-content {
  font-size: 12px; line-height: 1.5; color: var(--fg); white-space: pre-wrap;
}
/* Click-to-fold: tap the summary header to collapse/expand. The chevron
   flips when collapsed, the content hides + the panel's bottom margin
   tightens so a folded summary doesn't take up vertical space. */
.email-summary-toggle { cursor: pointer; user-select: none; }
.email-summary-toggle:hover { opacity: 1; }
.email-summary-panel.collapsed { padding-bottom: 6px; margin-bottom: 6px; }
.email-summary-panel.collapsed .email-summary-header { margin-bottom: 0; }
.email-summary-panel.collapsed .email-summary-content { display: none; }
.email-summary-panel.collapsed .email-summary-chevron { transform: rotate(-90deg); }

/* Foldable attachments — same fold-on-click UX as the summary panel. */
.email-reader-atts-wrap {
  display: flex; flex-direction: column;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.email-reader-atts-header {
  display: flex; align-items: center; gap: 5px;
  padding: 6px 14px;
  font-size: 10px; font-weight: 600; text-transform: uppercase;
  color: var(--fg); opacity: 0.7;
  cursor: pointer; user-select: none;
  letter-spacing: 0.4px;
}
.email-reader-atts-wrap > .email-reader-atts {
  border-bottom: none !important;
}
.email-reader-atts-wrap.collapsed > .email-reader-atts { display: none; }
.email-reader-atts-wrap.collapsed .email-summary-chevron { transform: rotate(-90deg); }

/* Quote fold = neutral full-width band (matches attachments header). */
.email-quote-fold {
  margin: 0 -14px;
  padding: 0;
  border: 0;
  border-top: 1px solid var(--border) !important;
  border-radius: 0;
  outline: 0 !important;
  box-shadow: none !important;
  /* Neutralise any rich-mail HTML that injects a yellow / colored bg
     or border onto blockquote ancestors. */
  background: transparent !important;
}
/* Same neutralisation for any element nested inside the fold (the inner
   blockquote, paragraphs, divs from the original message). Yellow outlines
   in Outlook-style mails almost always come from inline border-color or
   box-shadow on these. */
.email-quote-fold *,
.email-quote-fold *::before,
.email-quote-fold *::after {
  outline-color: transparent !important;
  box-shadow: none !important;
}
.email-quote-fold blockquote,
.email-quote-fold blockquote[type="cite"],
.email-quote-fold table,
.email-quote-fold div {
  border-color: var(--border) !important;
  /* Override the .msg blockquote rule that sets a colored left border via
     var(--hl-function) — that's where the "yellow" outline was coming from. */
  border-left-color: var(--border) !important;
  border-radius: 0 !important;
  background: transparent !important;
}
/* JS marks the last .email-quote-fold in each email body with this class,
   so it's the only one that gets rounded bottom corners. Avoids :has()
   browser-support quirks. */
.email-quote-fold.last-fold {
  border-radius: 0 0 8px 8px;
  overflow: hidden;
}
/* ── Threaded reply turns (recursive parser) ── */
.email-thread-turn-body {
  padding: 8px 14px 4px;
  font-size: 12px;
  line-height: 1.45;
}
/* Nested turns indent slightly + get their own card outline so the
   reply hierarchy is obvious at a glance. */
.email-thread-turn-body .email-thread-turn {
  margin: 8px 0 4px;
  margin-left: 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: color-mix(in srgb, var(--fg) 2%, transparent);
}
.email-thread-turn-body .email-thread-turn .email-fold-summary {
  padding: 5px 10px;
}
.email-thread-turn-body .email-thread-turn .email-thread-turn-body {
  padding: 6px 10px 2px;
  font-size: 11px;
}
/* The original blockquote's left stripe is redundant when each turn is
   already a card — drop it inside thread bodies. */
.email-thread-turn-body blockquote {
  border-left: none !important;
  margin: 0 !important;
  padding: 0 !important;
  background: transparent !important;
}
/* Flatten the inner blockquote: no rounded corners, no yellow tint
   inherited from rich-mail HTML. Subtle left border + neutral bg. */
.email-quote-fold blockquote {
  margin: 0;
  padding: 8px 12px;
  border: 0;
  border-left: 2px solid var(--border);
  border-radius: 0 !important;
  background: transparent !important;
  color: inherit;
}
/* "From … · Date …" chip appended to the summary line */
.email-fold-summary-meta {
  margin-left: 6px;
  font-size: 10px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  opacity: 0.55;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
/* Sender name is the primary affordance — slightly larger and not all-caps so
   "From: Sam" feels like a click target on the person, not a section label. */
.email-fold-summary-name {
  font-size: 11px;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0.1px;
  color: var(--fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.email-quote-fold .email-fold-summary {
  display: flex; align-items: center; gap: 5px;
  padding: 6px 14px;
  cursor: pointer; list-style: none;
  font-size: 10px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.4px;
  /* Force neutral page colors regardless of any rich-mail HTML wrapping us */
  color: var(--fg) !important;
  background: transparent !important;
  background-color: transparent !important;
  text-shadow: none !important;
  font-style: normal !important;
  opacity: 0.7;
  user-select: none;
  border-radius: 0;
}
.email-quote-fold .email-fold-summary > * {
  /* Prevent inline-color attrs on summary children (icon SVGs, meta span)
     from picking up a forced color from ancestor email HTML. */
  color: inherit !important;
  background: transparent !important;
}
.email-quote-fold .email-fold-summary::-webkit-details-marker,
.email-quote-fold .email-fold-summary::marker { display: none; content: none; }
.email-quote-fold .email-fold-summary { list-style: none; list-style-type: none; }
.email-quote-fold .email-fold-summary:hover { opacity: 1; }
.email-quote-fold[open] .email-summary-chevron { transform: rotate(180deg); }
.email-quote-fold[open] > *:not(summary) {
  padding: 8px 14px 12px 14px;
  border-top: 1px solid var(--border);
}

/* Signature fold = clean full-width band like the attachments header. No
   accent overlay, no rounded corners — neutral chrome so it doesn't look
   like the AI Summary panel. */
.email-sig-fold {
  margin: 8px 0 0;
  padding: 0;
  border: 0;
  border-top: 1px dashed color-mix(in srgb, var(--fg) 18%, transparent);
  background: transparent;
  border-radius: 0;
}
.email-sig-fold .email-fold-summary {
  display: flex; align-items: center; gap: 5px;
  padding: 4px 0;
  cursor: pointer;
  list-style: none; list-style-type: none;
  font-size: 9.5px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--fg); opacity: 0.55;
  user-select: none;
}
/* Suppress the global `summary::before { content: '▶' }` rule from
   leaking the chevron-emoji onto our header — we use the trailing SVG
   chevron only. */
.email-sig-fold .email-fold-summary::before,
.email-quote-fold .email-fold-summary::before { content: none !important; }
.email-sig-fold .email-fold-summary::-webkit-details-marker,
.email-sig-fold .email-fold-summary::marker { display: none; content: none; }
.email-sig-fold .email-fold-summary:hover { opacity: 1; }
.email-sig-fold[open] .email-summary-chevron { transform: rotate(180deg); }
.email-sig-fold[open] > *:not(summary) {
  padding: 6px 0 0;
  border-top: 0;
  font-size: 11px; line-height: 1.45;
  color: color-mix(in srgb, var(--fg) 75%, transparent);
  white-space: pre-wrap;
}

/* ── Chat-bubble layout for email threads ──
   Each parsed turn becomes a bubble. The active account's outgoing
   replies align right (mine); everyone else aligns left (theirs).
   Bubbles read top→bottom oldest→newest, like a chat. */
.email-bubbles {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 4px 0 8px;
  margin: 0 -2px;
}
.email-bubble-row {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  width: 100%;
  min-width: 0;
}
.email-bubble-row.email-bubble-mine {
  flex-direction: row-reverse;
  justify-content: flex-start;
}
.email-bubble-row.email-bubble-theirs {
  /* Avatar lives at the END of the row (right side) so the bubble has
     the full left edge to start from — gives bigger reading width and
     a cleaner left margin. */
  flex-direction: row-reverse;
  justify-content: flex-end;
}
.email-bubble {
  flex: 0 1 auto;
  max-width: 90%;
  min-width: 0;
  padding: 8px 12px;
  border: 1px solid var(--border);
  background: var(--panel);
  font-size: 12.5px;
  line-height: 1.5;
  word-wrap: break-word;
  overflow-wrap: anywhere;
  overflow: hidden;
}
.email-bubble-mine .email-bubble {
  background: color-mix(in srgb, var(--bubble-accent, var(--fg)) 10%, var(--bg));
  border-color: color-mix(in srgb, var(--bubble-accent, var(--border)) 35%, var(--border));
  border-radius: 14px 14px 4px 14px;
}
.email-bubble-theirs .email-bubble {
  background: color-mix(in srgb, var(--bubble-accent, var(--fg)) 5%, var(--panel));
  border-color: color-mix(in srgb, var(--bubble-accent, var(--border)) 25%, var(--border));
  border-radius: 14px 14px 14px 4px;
}
.email-bubble-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 10.5px;
  margin-bottom: 4px;
  opacity: 0.7;
  min-width: 0;
}
.email-bubble-mine .email-bubble-head { justify-content: flex-end; }
.email-bubble-author {
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 50ch;
}
.email-bubble-date {
  opacity: 0.7;
  font-weight: 400;
  white-space: nowrap;
}
.email-bubble-body {
  white-space: normal;
  /* Reader-controlled typography — override sender's inline styles AND
     attribute-based sizing (<font size>, <big>, <small>, h1..h6). Weight
     and italics still pass through for emphasis. */
  font-family: inherit;
  font-size: var(--email-body-size, 13.5px);
  line-height: 1.5;
}
.email-bubble-body *:not(code):not(pre):not(kbd):not(samp) {
  font-family: inherit !important;
  font-size: inherit !important;
  line-height: inherit !important;
}
/* Heading tags carry browser-default font-size multipliers that bypass
   `font-size: inherit` when an inline style was set; pin them too. */
.email-bubble-body h1,
.email-bubble-body h2,
.email-bubble-body h3,
.email-bubble-body h4,
.email-bubble-body h5,
.email-bubble-body h6,
.email-bubble-body big,
.email-bubble-body small,
.email-bubble-body font {
  font-size: inherit !important;
  line-height: inherit !important;
}
.email-bubble-body img {
  max-width: 100%;
  height: auto;
}
/* Sender HTML often wraps content in a fixed-width <div style="width:600px">
   or <table width="600">. Override so content fills the reader's available
   width — otherwise resizing the email window doesn't reflow anything. */
.email-bubble-body,
.email-bubble-body * {
  max-width: 100% !important;
}
.email-bubble-body div[style*="width"],
.email-bubble-body table[width],
.email-bubble-body table {
  width: 100% !important;
  box-sizing: border-box !important;
}
.email-bubble-body blockquote {
  border-left: 2px solid var(--border) !important;
  margin: 4px 0 !important;
  padding: 4px 10px !important;
  background: transparent !important;
  border-radius: 0 !important;
  color: inherit;
}
.email-bubble-body table {
  max-width: 100%;
}
.email-bubble-avatar {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  /* Per-sender color set inline (`style="background:hsl(...)"`); this
     fallback only kicks in when JS hasn't assigned one. */
  background: color-mix(in srgb, var(--fg) 14%, transparent);
  color: #fff;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.22);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.3px;
  user-select: none;
}
/* Sig folds inside a bubble: tone down — no full-bleed band, since the
   bubble already provides the chrome. */
.email-bubble-body .email-sig-fold {
  margin: 8px 0 0;
  border-top: 1px dashed var(--border);
}
.email-bubble-body .email-sig-fold .email-fold-summary,
.email-bubble-body .email-quote-fold .email-fold-summary {
  padding: 4px 0;
}
.email-bubble-body .email-sig-fold[open] > *:not(summary),
.email-bubble-body .email-quote-fold[open] > *:not(summary) {
  padding: 6px 0 0;
  border-top: 0;
}
@media (max-width: 600px) {
  .email-bubble { max-width: 96%; }
  .email-bubble-avatar { display: none; }
}

/* ── Schedule Send modal ── */
.schedule-send-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px 18px 12px;
}
.schedule-send-label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  color: var(--fg);
  opacity: 0.55;
  margin-top: 6px;
}
.schedule-send-label:first-child { margin-top: 0; }
.schedule-send-presets {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  margin-bottom: 4px;
}
.schedule-send-presets .memory-toolbar-btn {
  padding: 5px 10px 11px;
  font-size: 12px;
  justify-content: center;
  line-height: 1;
}
.schedule-send-confirm svg {
  width: 12px;
  height: 12px;
  vertical-align: -1px;
  margin-right: 6px;
}
.schedule-send-datetime {
  font-size: 13px;
  padding: 8px 10px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 6px;
  outline: none;
  font-family: inherit;
  width: 100%;
  box-sizing: border-box;
}
.schedule-send-datetime:focus {
  border-color: var(--hl-function);
}
.schedule-send-footer {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 8px;
  padding: 10px 18px 14px;
  border-top: 1px solid var(--border);
  margin-top: 4px;
}
.schedule-send-confirm {
  background: var(--accent-primary, var(--red)) !important;
  color: #fff !important;
  border-color: var(--accent-primary, var(--red)) !important;
}
.schedule-send-confirm:hover {
  background: color-mix(in srgb, var(--accent-primary, var(--red)) 85%, white) !important;
}

/* Senders embed inline colors in their HTML that clash with the app's
   theme (black text in dark mode, white backgrounds, yellow highlights,
   etc.). Force theme colors on every descendant of the rendered body so
   emails inherit the user's chosen palette regardless of what the
   sender's mail client emits. */
.email-reader-body *,
.email-bubble-body * {
  color: var(--fg) !important;
  -webkit-text-fill-color: currentColor !important;
  background-color: transparent !important;
  background-image: none !important;
}
.email-reader-body a,
.email-bubble-body a,
.email-thread-turn-body a {
  color: var(--hl-function) !important;
}
.email-reader-body a[href^="tel:"],
.email-reader-body a[href^="sms:"],
.email-reader-body a[href^="x-apple-data-detectors:"],
.email-reader-body a[x-apple-data-detectors],
.email-bubble-body a[href^="tel:"],
.email-bubble-body a[href^="sms:"],
.email-bubble-body a[href^="x-apple-data-detectors:"],
.email-bubble-body a[x-apple-data-detectors],
.email-thread-turn-body a[href^="tel:"],
.email-thread-turn-body a[href^="sms:"],
.email-thread-turn-body a[href^="x-apple-data-detectors:"],
.email-thread-turn-body a[x-apple-data-detectors] {
  color: inherit !important;
  -webkit-text-fill-color: currentColor !important;
  text-decoration: none !important;
  background-color: transparent !important;
  background-image: none !important;
  -webkit-background-clip: border-box !important;
  background-clip: border-box !important;
}

/* Prefer local/system emoji fonts; avoid remote font providers. */
.doc-editor-textarea, .doc-editor-highlight, #doc-editor-code,
.doc-md-preview, .doc-csv-preview {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', monospace;
  font-variant-emoji: text;
}
/* Preview panes claim the editor's space so the action footer stays pinned
   at the bottom of the pane even when the rendered content is short
   (shorter table, single-paragraph markdown, etc). */
.doc-csv-preview,
.doc-md-preview {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}

/* Email documents override: use colored system emoji in compose. */
.doc-editor-textarea.email-mode,
#doc-editor-code.email-mode,
#doc-editor-code.email-mode .doc-editor-highlight,
#doc-editor-code.email-mode .doc-editor-textarea {
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif,
               "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla" !important;
  font-variant-emoji: emoji;
}

/* Single-layer rendering for ALL documents (not just email): the highlight
   overlay (<pre> with hljs spans) and the transparent textarea use two
   different rendering paths internally — the textarea uses the browser's
   native form line-breaker, the overlay uses CSS line-breaking — and they
   can never be guaranteed to wrap byte-identical no matter how many CSS
   properties are pinned. Pick one source of truth: make the textarea its
   own visible text, hide the overlay. Trade-off: no syntax highlighting in
   the live editor (rendered markdown preview / chat output still has it).
   The caret is now glued to the typed text by definition since both are
   rendered by the same element. */
.doc-editor-highlight,
#doc-editor-code.email-mode .doc-editor-highlight {
  display: none !important;
}
/* Find bar is open: float the highlight overlay ON TOP of the
   textarea with transparent background + transparent text, so only
   the solid <mark.doc-find-mark> spans are visible. pointer-events
   off so clicks still go through to the textarea below. */
body.doc-find-active .doc-editor-highlight,
body.doc-find-active #doc-editor-code.email-mode .doc-editor-highlight {
  display: block !important;
  z-index: 5 !important;
  background: transparent !important;
  pointer-events: none;
}
body.doc-find-active .doc-editor-highlight,
body.doc-find-active .doc-editor-highlight * {
  color: transparent !important;
  background: transparent !important;
}
/* Marks remain solid so they pop through the otherwise-invisible
   overlay; outlined extra-bright for the currently-focused one. */
body.doc-find-active mark.doc-find-mark {
  background: var(--accent) !important;
  color: var(--bg) !important;
}
body.doc-find-active mark.doc-find-mark.current {
  background: var(--accent) !important;
  box-shadow: 0 0 0 2px var(--fg) !important;
  outline: 1px solid var(--fg) !important;
}
.doc-editor-textarea,
#doc-editor-code.email-mode .doc-editor-textarea {
  color: var(--fg) !important;
  caret-color: var(--fg) !important;
  z-index: 1;
}

/* Emoji picker (monochrome icon picker) */
.emoji-picker-btn {
  background: none; border: none; color: var(--fg); opacity: 0.35;
  padding: 4px 7px; cursor: pointer; min-height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  transition: opacity 0.1s;
}
.emoji-picker-btn:hover { opacity: 1; }
.emoji-picker {
  width: min(300px, 92vw); max-height: min(300px, 55vh);
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.3);
  display: flex; flex-direction: column; overflow: hidden;
}
.emoji-picker-search {
  padding: 8px 12px; background: var(--panel); border: none;
  border-bottom: 1px solid var(--border); color: var(--fg);
  font-size: 12px; font-family: inherit; outline: none;
}
.emoji-picker-groups { overflow-y: auto; padding: 4px 0; flex: 1; }
.emoji-picker-group { padding: 0 6px 4px 6px; }
.emoji-picker-group-name {
  font-size: 10px; font-weight: 600; opacity: 0.5;
  text-transform: uppercase; letter-spacing: 0.5px;
  padding: 4px 4px 2px 4px;
}
.emoji-picker-grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 1px; }
.emoji-picker-item {
  background: none; border: none; cursor: pointer;
  padding: 4px; border-radius: 4px;
  display: flex; align-items: center; justify-content: center;
  color: var(--fg); transition: background 0.1s;
}
.emoji-picker-item:hover { background: color-mix(in srgb, var(--accent) 15%, transparent); }
.emoji-picker-item svg { width: 16px; height: 16px; }

.email-avatar {
  width: 28px; height: 28px; border-radius: 50%; background: var(--accent, #4a9eff);
  color: #fff; display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 600; flex-shrink: 0; margin-top: 1px;
}
.email-item-content { flex: 1; min-width: 0; overflow: hidden; }
.email-item-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.email-sender { font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.email-sender-clickable { cursor: pointer; }
.email-sender-clickable:hover { text-decoration: underline; }
.email-filter-chip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  margin: 4px 8px 6px;
  padding: 4px 8px;
  border-radius: 12px;
  font-size: 11px;
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 40%, transparent);
  color: var(--fg);
}
.email-filter-chip-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.email-filter-chip-clear {
  background: none;
  border: none;
  color: inherit;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 0 2px;
  opacity: 0.6;
}
.email-filter-chip-clear:hover { opacity: 1; }
.email-date { font-size: 10px; opacity: 0.5; white-space: nowrap; flex-shrink: 0; }
.email-subject { font-size: 11px; opacity: 0.6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 1px; }
.email-tags { display: inline-flex; gap: 3px; margin-left: 6px; vertical-align: middle; }
.email-tag {
  display: inline-block;
  font-size: 9px;
  line-height: 1;
  padding: 2px 5px;
  border-radius: 8px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  background: rgba(127, 127, 127, 0.18);
  color: var(--fg);
}
.email-tag-work { background: rgba(96, 165, 250, 0.22); color: #60a5fa; }
.email-tag-personal { background: rgba(74, 222, 128, 0.22); color: #4ade80; }
.email-tag-finance { background: rgba(250, 204, 21, 0.22); color: #facc15; }
.email-tag-bills { background: rgba(251, 146, 60, 0.22); color: #fb923c; }
.email-tag-receipt { background: rgba(251, 146, 60, 0.22); color: #fb923c; }
.email-tag-travel { background: rgba(167, 139, 250, 0.22); color: #a78bfa; }
.email-tag-newsletter { background: rgba(148, 163, 184, 0.22); color: #94a3b8; }
.email-tag-promo,
.email-tag-marketing { background: rgba(244, 114, 182, 0.22); color: #f472b6; }
.email-tag-notification { background: rgba(148, 163, 184, 0.22); color: #94a3b8; }
.email-tag-security { background: rgba(248, 113, 113, 0.22); color: #f87171; }
.email-tag-urgent { background: color-mix(in srgb, var(--color-error, #e06c75) 25%, transparent); color: var(--color-error, #e06c75); font-weight: 600; }
.email-tag-reply-soon { background: rgba(240, 173, 78, 0.22); color: #f0ad4e; }
.email-tag-social { background: rgba(56, 189, 248, 0.22); color: #38bdf8; }
.email-tag-shopping { background: rgba(236, 72, 153, 0.22); color: #ec4899; }
.email-tag-calendar { background: rgba(167, 139, 250, 0.22); color: #a78bfa; }
.email-menu-wrap { position: relative; flex-shrink: 0; }
.email-menu-btn {
  background: none; border: none; color: var(--fg); opacity: 0; cursor: pointer;
  padding: 4px; transition: opacity 0.15s;
  position: relative; top: 0px;
}
.email-dropdown {
  position: absolute; right: 0; top: 100%; z-index: 9999;
  min-width: 120px; background: var(--bg); border: 1px solid var(--border);
  border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); padding: 4px 0;
}
.email-loading { padding: 16px; text-align: center; font-size: 12px; opacity: 0.5; }
.email-load-more { padding: 8px; text-align: center; }
.email-load-more-btn {
  background: transparent; border: 1px solid var(--border); border-radius: 4px;
  color: var(--fg); padding: 4px 12px; font-size: 11px; cursor: pointer;
}
.email-load-more-btn:hover { background: var(--hover-bg, rgba(255,255,255,0.05)); }
.email-spinner {
  display: inline-block; width: 12px; height: 12px; border: 2px solid rgba(255,255,255,0.3);
  border-top-color: #fff; border-radius: 50%; animation: email-spin 0.6s linear infinite;
  vertical-align: middle; margin-right: 4px;
}
@keyframes email-spin { to { transform: rotate(360deg); } }

/* Email section doesn't collapse — hide the auto-injected chevron */
#email-section .section-collapse-btn { display: none !important; }

/* Compose + button: show on section hover, spin on button hover */
#email-compose-btn {
  opacity: 0;
  transition: opacity 0.15s ease;
}
#email-section:hover #email-compose-btn {
  opacity: 0.7;
}
#email-compose-btn:hover {
  opacity: 1 !important;
  background: none !important;
}
#email-compose-btn svg {
  width: 11px;
  height: 11px;
  transition: transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
}
#email-compose-btn .list-item-plus-label {
  top: calc(50% - 0.5px);
  color: var(--fg);
  font-size: 10px;
}
#email-compose-btn:hover svg {
  transform: rotate(180deg) scale(1.15);
}

/* Library row "+" — identical to the email compose "+": hidden until the row
   is hovered, no accent box, and the same rotate-in animation. */
#library-new-doc-btn {
  opacity: 0;
  transition: opacity 0.15s ease;
}
#tool-library-btn:hover #library-new-doc-btn {
  opacity: 0.7;
}
#library-new-doc-btn:hover {
  opacity: 1 !important;
  background: none !important;
}
#library-new-doc-btn svg {
  transition: transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
}
#library-new-doc-btn .list-item-plus-label {
  top: calc(50% + 1px);
}
#library-new-doc-btn:hover svg {
  transform: rotate(180deg) scale(1.15);
}

/* Satisfying send effect: email slides out to the right and fades */
.email-send-fx {
  animation: email-send-slide 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  pointer-events: none;
}
@keyframes email-send-slide {
  0% {
    transform: translateX(0);
    opacity: 1;
    filter: blur(0);
  }
  60% {
    filter: blur(1px);
  }
  100% {
    transform: translateX(110%);
    opacity: 0;
    filter: blur(3px);
  }
}

/* MD toolbar attach button — matches other toolbar buttons */
.md-toolbar-attach-btn {
  background: none; border: none; color: var(--fg); opacity: 0.35;
  padding: 4px 7px; cursor: pointer; min-height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  transition: opacity 0.1s;
}
.md-toolbar-attach-btn:hover { opacity: 1; }

/* Email reader icon buttons — vertical icon + label stack. */
.memory-toolbar-btn.reader-icon-btn {
  width: 48px;
  height: 44px;
  padding: 4px 2px;
  position: relative;
  top: 1px;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  flex: 0 0 auto;
}
.memory-toolbar-btn.reader-icon-btn svg { width: 18px; height: 18px; }
.memory-toolbar-btn.reader-icon-btn.active {
  background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 40%, transparent);
  color: var(--accent, var(--red));
}
/* The "More" (kebab) reader button — accent-colored so it stands out as the
   actions-menu trigger. When the dropdown is open we add .reader-more-active
   for a filled tint, matching the toggle states elsewhere. */
.memory-toolbar-btn.reader-icon-btn[data-act="more"] {
  color: var(--accent, var(--red));
}
.memory-toolbar-btn.reader-icon-btn[data-act="more"] svg { fill: currentColor; }
.memory-toolbar-btn.reader-icon-btn.reader-more-active {
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 45%, transparent);
  color: var(--accent, var(--red));
}
/* Cookbook Serve dropdown triggers (the cached-model kebab + the saved-config
   arrow) — accent-colored to signal they open a menu, with a filled tint when
   their dropdown is open. Matches the email reader More button pattern. */
.hwfit-cached-menu-btn,
.cookbook-saved-arrow {
  color: var(--accent, var(--red));
  /* Base .memory-item-btn is flex with align-items but no justify-content,
     so the kebab SVG sat left-aligned inside the button instead of being
     horizontally centered. Pin it center. */
  justify-content: center;
}
.hwfit-cached-menu-btn svg { fill: currentColor; }
.hwfit-cached-menu-btn.cookbook-menu-active,
.cookbook-saved-arrow.cookbook-menu-active {
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 45%, transparent);
  color: var(--accent, var(--red));
}
/* Mobile: center the kebab/menu dropdowns in the viewport so they don't
   slide left/right as the window width changes (they were positioned via
   inline `right: ...px` relative to the kebab button). Stable position is
   easier to land on by touch. The inline `top:` set by the menu code is
   preserved; we only override the horizontal axis. */
@media (max-width: 768px) {
  .email-card-dropdown,
  .cookbook-saved-menu,
  .cookbook-dep-menu {
    left: 50% !important;
    right: auto !important;
    transform: translateX(-50%);
  }
}
/* Mobile-only Cancel item appended to dropdowns (kebab/More menus). On
   desktop outside-click closes them cleanly; on touch that's fiddly, so
   give an explicit Cancel row. Visually separated from the action items
   above by a top divider + slight muted color. */
.dropdown-cancel-mobile { display: none; }
@media (max-width: 768px) {
  .dropdown-cancel-mobile {
    display: flex;
    border-top: 1px solid var(--border);
    margin-top: 4px;
    padding-top: 8px;
    opacity: 0.85;
  }
}
/* Launch-command Copy button morphs into Cancel once Launch has been clicked. */
.hwfit-serve-copy.hwfit-serve-cancel {
  border-color: var(--color-danger, #e06c75) !important;
  color: var(--color-danger, #e06c75) !important;
}
.hwfit-serve-copy.hwfit-serve-cancel:hover {
  background: color-mix(in srgb, var(--color-danger, #e06c75) 14%, transparent);
}
/* Email modal title unread badge — small accent pill next to "Email". */
.email-lib-unread-badge {
  display: inline-block;
  font-size: 0.55em;
  font-weight: 700;
  background: var(--accent-primary, var(--red));
  color: var(--bg, #1a1a1a);
  border-radius: 9px;
  padding: 1px 7px;
  margin-left: 8px;
  vertical-align: 2px;
  line-height: 1.3;
  letter-spacing: 0.02em;
  cursor: pointer;
}
.email-lib-unread-badge:hover { filter: brightness(1.08); }
#email-lib-modal.email-lib-unread-active .doclib-modal-content {
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--accent, var(--red)) 22%, transparent),
    0 0 18px color-mix(in srgb, var(--accent, var(--red)) 18%, transparent),
    var(--shadow-lg, 0 18px 50px rgba(0, 0, 0, 0.35));
}
#email-lib-modal .email-lib-header-actions .minimize-btn {
  position: relative;
  left: 6px;
}
/* Per-card prev/next nav — only visible on the currently expanded card,
   sits at the right of the title row next to the done check. */
.email-card-nav-arrows {
  display: none;
  margin-left: auto;
  gap: 2px;
  flex-shrink: 0;
  align-items: center;
  position: relative;
  top: -3px;
  left: 19px;
}
.doclib-card.email-card-expanded .email-card-nav-arrows {
  display: inline-flex;
}
.email-card-header-menu {
  display: none;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--fg);
  opacity: 0.65;
  cursor: pointer;
  position: relative;
  top: -3px;
  transition: opacity 0.12s, background 0.12s;
}
.doclib-card.email-card-expanded .email-card-header-menu {
  display: inline-flex;
}
.email-card-header-menu:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.email-card-nav-btn {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.65;
  cursor: pointer;
  padding: 6px 8px;
  border-radius: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.12s, background 0.12s;
}
.email-card-nav-btn:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.email-card-nav-btn:disabled {
  opacity: 0.25;
  cursor: default;
  pointer-events: none;
}

/* ── Email document editor ── */
.doc-email-header {
  display: flex; flex-direction: column; gap: 6px; padding: 10px 12px;
  border-bottom: 1px solid var(--border); background: var(--bg); flex-shrink: 0;
}
.doc-email-fields {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 0;
}
.doc-email-collapse-btn {
  width: 100%;
  min-height: 24px;
  display: none;
  align-items: center;
  gap: 7px;
  padding: 2px 4px 3px;
  border: none;
  background: transparent;
  color: var(--fg);
  font: inherit;
  font-size: 11px;
  cursor: pointer;
  opacity: 0.72;
  text-align: left;
}
.doc-email-collapse-btn:hover { opacity: 1; color: var(--accent, var(--red)); }
.doc-email-collapse-btn svg {
  flex-shrink: 0;
  opacity: 0.65;
  transition: transform 0.14s ease;
}
.doc-email-collapse-summary {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  opacity: 0.74;
}
.doc-email-header:not(.doc-email-header-collapsed) .doc-email-collapse-summary {
  opacity: 0.45;
}
.doc-email-header.doc-email-header-collapsed {
  gap: 0;
  padding-top: 5px;
  padding-bottom: 5px;
}
.doc-email-header.doc-email-header-collapsed .doc-email-fields {
  display: none;
}
.doc-email-header.doc-email-header-collapsed .doc-email-collapse-btn svg {
  transform: rotate(180deg);
}
.email-field { display: flex; align-items: center; gap: 8px; position: relative; }
.email-field label { font-size: 11px; font-weight: 600; color: var(--fg); opacity: 0.5; min-width: 50px; text-align: right; flex-shrink: 0; }
.email-field input {
  flex: 1; width: 100%; background: transparent; border: 1px solid var(--border); border-radius: 4px;
  padding: 5px 8px; font-size: 13px; font-family: inherit; color: var(--fg); outline: none;
  min-width: 0;
}
.email-field input:focus { border-color: var(--accent, #4a9eff); }
@container docpane (max-width: 460px) {
  .doc-email-header .email-field {
    display: grid;
    grid-template-columns: 1fr;
    gap: 3px;
    align-items: stretch;
  }
  .doc-email-header .email-field label {
    min-width: 0;
    text-align: left;
  }
  .doc-email-header .email-field input {
    width: 100%;
  }
}
/* Cc toggle and attach button are absolute so they don't steal width from the To input */
.email-field .email-cc-toggle {
  position: absolute; right: 6px; top: calc(50% + 4px); transform: translateY(-50%);
  z-index: 2;
}
@media (min-width: 769px) {
  .email-field .email-cc-toggle {
    top: calc(50% + 4px);
  }
}
.email-field input { padding-right: 60px; }
.email-field #doc-email-cc, .email-field #doc-email-bcc, .email-field #doc-email-subject { padding-right: 8px; }

.doc-email-actions {
  display: flex; gap: 8px; justify-content: flex-end; padding: 10px 14px;
  border-top: 1px solid var(--border); background: var(--bg); flex-shrink: 0;
  align-items: center;
}
/* Documents footer — X + Undo stay on the LEFT, Copy/Export group pushed
   to the RIGHT. Email composer keeps its original flex-end layout. */
#doc-actions-footer {
  justify-content: flex-start;
}
#doc-actions-footer .email-send-split {
  margin-left: auto;
}
/* WYSIWYG email body — what the recipient sees, edited in place. */
.doc-email-richbody {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 12px 16px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  color: var(--fg);
  background: var(--bg);
  outline: none;
  white-space: pre-wrap;
  word-wrap: break-word;
}
.doc-email-richbody:empty::before {
  content: "Write your email\2026";
  opacity: 0.4;
  pointer-events: none;
}
.doc-email-richbody a { color: var(--accent, #4af); }
.doc-email-richbody blockquote {
  margin: 6px 0; padding-left: 10px;
  border-left: 3px solid var(--border); opacity: 0.8;
}
.doc-email-richbody h1, .doc-email-richbody h2, .doc-email-richbody h3 { margin: 0.4em 0; }
.doc-email-richbody p { margin: 0.5em 0; }
.email-more-menu {
  position: absolute; bottom: 100%; right: 0; margin-bottom: 6px;
  min-width: 160px; background: var(--bg); border: 1px solid var(--border);
  border-radius: 6px; box-shadow: 0 -4px 12px rgba(0,0,0,0.2); padding: 4px;
  z-index: 100;
}
.email-send-btn {
  background: color-mix(in srgb, var(--accent-primary, var(--red)) 20%, transparent);
  color: var(--accent-primary, var(--red));
  border: 1px solid color-mix(in srgb, var(--accent-primary, var(--red)) 50%, transparent);
  border-radius: 6px;
  padding: 0 14px; height: 28px; font-size: 11px; font-weight: 600; cursor: pointer;
  font-family: inherit; transition: all 0.15s; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 6px;
}
/* Split send button: [ Send │ ▾ ] — the caret drops the send-options menu UP. */
.email-send-split { position: relative; display: inline-flex; }
.email-send-split .email-send-main {
  border-top-right-radius: 0; border-bottom-right-radius: 0; border-right: none;
}
.email-send-split .email-send-caret {
  border-top-left-radius: 0; border-bottom-left-radius: 0;
  padding: 0 8px; gap: 0;
  /* its left border is the single divider between the two halves */
  border-left: 1px solid color-mix(in srgb, var(--accent-primary, var(--red)) 50%, transparent);
}
.email-send-split .email-send-caret svg { transition: transform 0.15s ease; }
.email-send-split .email-send-caret[aria-expanded="true"] svg { transform: rotate(180deg); }
.email-send-btn svg { flex-shrink: 0; }
.email-send-btn:hover {
  background: color-mix(in srgb, var(--accent-primary, var(--red)) 30%, transparent);
  border-color: var(--accent-primary, var(--red));
}
.email-send-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.email-draft-btn {
  background: none; border: 1px solid var(--border);
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  border-radius: 6px;
  padding: 0 12px; height: 28px; font-size: 11px; cursor: pointer;
  font-family: inherit; transition: all 0.15s; white-space: nowrap;
}
.email-draft-btn:hover { border-color: var(--fg); color: var(--fg); }
.email-draft-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.email-discard-btn {
  background: transparent;
  /* Dim the label text via color-mix instead of `opacity:0.6` on the whole
     button — opacity multiplies through to the SVG and washes out the accent
     X glyph. Per-element color dimming keeps the X at full accent strength. */
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 6px 14px; font-size: 12px; cursor: pointer; font-family: inherit;
}
/* The ✕ glyph itself reads in accent. */
.email-discard-btn svg { color: var(--accent-primary, var(--red)); opacity: 1; }
.email-discard-btn:hover { opacity: 1; color: var(--red, #e55); border-color: var(--red, #e55); }

/* Compose email "more" menu button — icon only, matches draft btn height */
#doc-email-more-btn {
  background: none; border: 1px solid var(--border);
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  border-radius: 6px;
  width: 28px; height: 28px; padding: 0;
  cursor: pointer; font-family: inherit; transition: all 0.15s;
  display: inline-flex; align-items: center; justify-content: center;
}
#doc-email-more-btn:hover {
  border-color: var(--fg); color: var(--fg);
}
#doc-email-more-btn svg { opacity: 0.8; }
.email-more-wrap { display: inline-flex; }
.email-attachments {
  display: flex; flex-wrap: wrap; gap: 6px; padding: 4px 0 0 58px;
}
.email-attachment-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 8px; font-size: 11px; background: var(--hover-bg, rgba(255,255,255,0.05));
  border: 1px solid var(--border); border-radius: 12px; color: var(--fg);
  text-decoration: none; cursor: pointer; transition: background 0.15s, border-color 0.15s;
  max-width: 200px; min-width: 0;
}
.email-attachment-chip:hover {
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  border-color: var(--accent);
  /* Expand chip on hover so the full filename is revealed. Native title-
     attribute tooltips were slow / unreliable, so we just grow the chip. */
  max-width: 90vw;
}
.email-attachment-chip > span:not(.att-size) {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  flex: 1 1 auto; min-width: 0;
}
.email-attachment-chip:hover > span:not(.att-size) {
  overflow: visible;
  text-overflow: clip;
}
.email-attachment-chip .att-size { opacity: 0.5; font-size: 10px; flex-shrink: 0; }
/* "Open in editor" launch icon — same prominent style on desktop AND mobile
   (was 24px / dim / no border on desktop, easy to miss). Accent-tinted
   background + border makes it read as a real action. */
.email-attachment-open {
  display: inline-flex; align-items: center; gap: 4px;
  height: 22px; padding: 0 9px; border-radius: 11px;
  margin-left: 6px; flex-shrink: 0;
  font-size: 10px; font-weight: 500; letter-spacing: 0.02em;
  color: var(--accent-primary, var(--red));
  background: color-mix(in srgb, var(--accent-primary, var(--red)) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent-primary, var(--red)) 40%, transparent);
  cursor: pointer; transition: background 0.1s, border-color 0.1s, color 0.1s;
}
.email-attachment-open svg,
.email-attachment-open > svg {
  width: 12px; height: 12px;
  opacity: 0.9;
}
.email-attachment-open:hover {
  background: color-mix(in srgb, var(--accent-primary, var(--red)) 22%, transparent);
  border-color: color-mix(in srgb, var(--accent-primary, var(--red)) 70%, transparent);
}
.email-attachment-open-label { line-height: 1; }
/* Collapsed chip: open button is icon-only (the labeled pill crowds the
   small chip). Hover expands the chip and reveals the "Open" label too. */
.email-attachment-chip:not(:hover) .email-attachment-open-label {
  display: none;
}
.email-attachment-chip:not(:hover) .email-attachment-open {
  width: 22px;
  padding: 0;
  justify-content: center;
  gap: 0;
}
@media (max-width: 768px) {
  .doc-email-collapse-btn {
    background: inherit;
  }
  /* Mobile: keep the pill but ensure a comfortable touch target. */
  .email-attachment-open {
    height: 26px; padding: 0 10px;
    min-height: 26px !important;
  }
  .email-attachments,
  .email-compose-atts {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-left: 0;
    padding-bottom: 2px;
  }
  .email-attachments::-webkit-scrollbar,
  .email-compose-atts::-webkit-scrollbar {
    display: none;
  }
  /* Attachment chip body — modest minimum height so the open icon sits
     neatly without dominating. */
  .email-attachment-chip,
  .email-compose-chip {
    flex: 0 0 auto;
    padding: 6px 8px !important;
    min-height: 36px !important;
  }
  .email-compose-chip .compose-chip-name {
    max-width: 190px;
  }
}

/* Compose attachment chips (when sending new email) */
.email-compose-atts {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: 6px 0 0 58px;
}
.email-compose-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 4px 4px 8px; font-size: 11px;
  background: var(--hover-bg, rgba(255,255,255,0.05));
  border: 1px solid var(--border); border-radius: 12px; color: var(--fg);
}
.email-compose-chip .compose-chip-name { max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.email-compose-chip .att-size { opacity: 0.5; font-size: 10px; }
.email-compose-chip .compose-chip-remove {
  background: none; border: none; color: var(--fg); opacity: 0.5;
  font-size: 16px; line-height: 1; padding: 0 4px; cursor: pointer;
}
.email-compose-chip .compose-chip-remove:hover { opacity: 1; color: var(--red, #e55); }

.email-cc-toggle {
  background: none; border: none; color: var(--fg);
  opacity: 0.4; font-size: 11px; cursor: pointer;
  padding: 4px 8px; font-family: inherit;
}
.email-cc-toggle:hover {
  opacity: 1;
  color: var(--accent, #4a9eff);
  background: none !important;
}

@media (max-width: 768px) {
  .doc-email-collapse-btn {
    display: flex;
  }
}
@media (min-width: 769px) {
  #doc-email-header #doc-email-collapse-btn.doc-email-collapse-btn {
    display: none !important;
  }
  #doc-email-header.doc-email-header-collapsed .doc-email-fields {
    display: flex !important;
  }
}

.email-autocomplete {
  position: absolute; top: 100%; left: 58px; right: 0; z-index: 1000;
  background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.2); max-height: 240px; overflow-y: auto;
  margin-top: 2px;
}
.contact-suggestion {
  display: flex; justify-content: space-between; gap: 8px;
  padding: 6px 10px; font-size: 12px; cursor: pointer;
  border-bottom: 1px solid var(--border);
}
.contact-suggestion:last-child { border-bottom: none; }
.contact-suggestion:hover, .contact-suggestion.active {
  background: color-mix(in srgb, var(--accent) 15%, transparent);
}
.contact-suggestion .contact-name { font-weight: 600; color: var(--fg); }
.contact-suggestion .contact-email { opacity: 0.6; font-size: 11px; }
.email-ai-reply-btn {
  background: none;
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0 12px; height: 28px; font-size: 11px; cursor: pointer;
  font-family: inherit; transition: all 0.15s;
  display: inline-flex; align-items: center; white-space: nowrap;
}
.email-ai-reply-btn:hover {
  border-color: var(--accent, #4a9eff);
  color: var(--accent, #4a9eff);
}
.email-ai-reply-btn:disabled { opacity: 0.5; cursor: not-allowed; }
/* Align select/new buttons in popup library toolbar */
#email-lib-select-btn, #email-lib-refresh-btn, #email-lib-compose-btn { position: relative; top: -4px; }
/* Select + Refresh sit slightly lower than Compose on desktop. */
#email-lib-select-btn, #email-lib-refresh-btn { top: -2px; }
@media (max-width: 768px) {
  /* On mobile they're 1px higher than desktop. */
  #email-lib-select-btn, #email-lib-refresh-btn { top: -3px; }
}

/* On mobile, the "New" (compose) button deserves a touch-friendly size
   so it stands out from the smaller toolbar buttons next to it. Lift
   it slightly visually with a bigger SVG, padding, and font. */
@media (max-width: 768px) {
  #email-lib-compose-btn {
    padding: 8px 14px !important;
    font-size: 13px !important;
    min-height: 36px;
    border-radius: 8px;
    font-weight: 600;
    top: -2px;
  }
  #email-lib-compose-btn svg {
    width: 14px !important;
    height: 14px !important;
    margin-right: 5px !important;
  }
}

/* ── Notes ── */
/* Notes panel — body-level flex sibling of chat-container */
body.notes-view .chat-container { flex: 1; min-width: 0; }

/* Mobile: notes panel takes over full screen */
@media (max-width: 768px) {
  body.notes-view .notes-pane {
    position: fixed;
    inset: 0;
    max-width: 100%;
    width: 100% !important;
    /* Desktop sets height: min(80vh, 820px) which wins over inset:0's bottom:0
       and leaves a gap at the bottom of the viewport. Force full height here. */
    height: 100dvh !important;
    max-height: 100dvh !important;
    z-index: 170;
    /* Bottom-sheet treatment like the document editor: stroked, rounded top,
       slides up from the bottom. */
    border: 1px solid var(--border);
    border-bottom: none;
    border-radius: 14px 14px 0 0;
    /* Pane itself never scrolls — its inner .notes-pane-body is the scroller.
       Two nested scrollers caused the body's flex:1 to lose its height bound
       on Firefox mobile and the last row got clipped off-screen. */
    overflow: hidden;
    animation: sheet-enter 0.25s cubic-bezier(0.2, 0.8, 0.2, 1) both;
    transform-origin: bottom center;
  }
  /* Required for the flex:1 body to actually constrain to remaining pane
     height instead of expanding to its content (default min-height:auto on
     flex items). Without this both grid and list views overflow the viewport
     bottom on phones. */
  body.notes-view .notes-pane-body {
    min-height: 0;
    -webkit-overflow-scrolling: touch;
  }
  body.notes-view #notes-divider {
    display: none;
  }
  body.notes-view .notes-mobile-grabber {
    display: block;
    flex-shrink: 0;
    height: 18px;
    position: relative;
    background: var(--bg);
    touch-action: none;
    cursor: grab;
  }
  body.notes-view .notes-mobile-grabber::before {
    content: '';
    position: absolute;
    top: 7px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    background: var(--fg);
    opacity: 0.25;
    border-radius: 2px;
  }
  body.notes-view .chat-container {
    display: none;
  }
  body.notes-view .mobile-new-chat-btn,
  body.notes-view .hamburger-btn {
    display: none !important;
  }
}
/* ── Mobile notes UX ────────────────────────────────────
   Tiles become read-only previews on touch ≤768px wide.
   Tap opens a fullscreen overlay where editing happens; long-press
   toggles drag-to-reorder. Wired up in static/js/notes.js. */
body.notes-mobile-mode .note-card-corner,
body.notes-mobile-mode .note-card-corner-edit,
body.notes-mobile-mode .note-card-corner-archive,
body.notes-mobile-mode .note-card-corner-copy,
body.notes-mobile-mode .note-card-corner-trash,
body.notes-mobile-mode .note-card-corner-unarchive,
body.notes-mobile-mode .note-card-corner-done,
body.notes-mobile-mode .note-card-edit-corner,
body.notes-mobile-mode .note-card-done,
body.notes-mobile-mode .note-card-actions,
body.notes-mobile-mode .note-cl-quickadd,
body.notes-mobile-mode .note-card .note-checkbox-rm,
/* Panel-fullscreen toggle is redundant on mobile — the panel always
   fills the viewport already. */
body.notes-mobile-mode #notes-fullscreen-toggle {
  display: none !important;
}

/* Header toggle icons (archive view, list/grid switch) — defaults are
   tuned for the dense desktop header; on mobile bump them slightly so
   they read as tap targets without dominating the header. */
body.notes-mobile-mode #notes-archive-toggle,
body.notes-mobile-mode #notes-view-toggle {
  width: 30px;
  height: 30px;
  padding: 6px;
}
body.notes-mobile-mode #notes-archive-toggle svg,
body.notes-mobile-mode #notes-view-toggle svg {
  width: 17px;
  height: 17px;
}
/* Disable inline checkbox toggling on tiles — checking off a todo
   requires opening the note. The visual dot stays but it's styled
   as a passive status indicator, not a tappable button. */
body.notes-mobile-mode .note-card .note-checkbox,
body.notes-mobile-mode .note-card .note-checkbox-rm {
  pointer-events: none;
}
body.notes-mobile-mode .note-card .note-check-dot,
body.notes-mobile-mode .note-card .note-checkbox-dot,
body.notes-mobile-mode .note-card .note-cl-dot {
  opacity: 0.55;
  transform: none !important;
  transition: none !important;
  cursor: default !important;
}
/* The whole card is the tap target on mobile — bigger feedback */
body.notes-mobile-mode .note-card {
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
body.notes-mobile-mode .note-card:active {
  transform: scale(0.985);
}

/* Drag-to-reorder mode — desaturate the grid and sweep a subtle
   shimmer across every tile so it's obvious you're in rearrange mode
   (the Google Keep "active edit" cue). Also disable scroll inside the
   panel so the drag works smoothly. */
body.notes-drag-mode .notes-pane-body {
  overflow: hidden;
  touch-action: none;
}
body.notes-drag-mode .note-card {
  cursor: grab;
  transform: scale(0.985);
  filter: saturate(0.7);
  opacity: 0.92;
  position: relative;
  overflow: hidden;
  transition: transform 0.22s cubic-bezier(0.2, 0.7, 0.3, 1),
              filter 0.18s ease,
              opacity 0.18s ease;
}
body.notes-drag-mode .note-card::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    115deg,
    transparent 30%,
    color-mix(in srgb, var(--fg) 14%, transparent) 50%,
    transparent 70%
  );
  background-size: 250% 100%;
  animation: notes-drag-shimmer 2.1s linear infinite;
  border-radius: inherit;
  z-index: 1;
  mix-blend-mode: overlay;
}
@media (max-width: 768px) {
  body.notes-drag-mode .note-card {
    filter: none;
    opacity: 0.96;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent, var(--red)) 28%, transparent);
  }
  body.notes-drag-mode .note-card::after {
    display: none;
  }
}
@keyframes notes-drag-shimmer {
  0%   { background-position: 250% 0; }
  100% { background-position: -150% 0; }
}
/* The grabbed card lifts above its siblings; suppress the shimmer on
   it so it reads as the "live" element being moved. */
body.notes-drag-mode .note-card.note-card-dragging {
  transform: scale(1.06);
  box-shadow: 0 16px 40px rgba(0,0,0,0.45);
  opacity: 1;
  filter: none;
  z-index: 10001;
  cursor: grabbing;
  transition: box-shadow 0.18s ease;
}
body.notes-drag-mode .note-card.note-card-dragging::after { display: none; }
body.notes-drag-mode .note-card .note-card-pin {
  visibility: hidden !important;
  opacity: 0 !important;
  pointer-events: none !important;
}
body.notes-drag-mode .note-card-pinned .note-card-pin,
body.notes-drag-mode .note-card-pin.active,
body.notes-drag-mode .note-card-pin svg {
  visibility: hidden !important;
  opacity: 0 !important;
}
/* Placeholder — same footprint as the lifted card, dotted outline so
   the drop target is visible while the card follows the finger. */
.note-card-placeholder {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  border: 2px dashed color-mix(in srgb, var(--fg) 25%, transparent);
  border-radius: 8px;
  box-sizing: border-box;
  flex-shrink: 0;
  transition: width 0.18s ease, height 0.18s ease;
}
@media (prefers-reduced-motion: reduce) {
  body.notes-drag-mode .note-card::after { animation: none; }
}


/* ── Fullscreen single-note edit overlay ──────────────── */
.note-fullscreen-overlay {
  position: fixed;
  inset: 0;
  z-index: 10500;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  opacity: 0;
  /* Bigger zoom range so the transition reads as a real "tile expands
     into a page" motion. transform-origin is set per-tile by JS. */
  transform: scale(0.45);
  transform-origin: center center;
  transition: opacity 0.28s ease, transform 0.32s cubic-bezier(0.18, 0.74, 0.24, 1.06);
  will-change: transform, opacity;
}
.note-fullscreen-overlay.open {
  opacity: 1;
  transform: scale(1);
}
.note-fullscreen-overlay.closing {
  opacity: 0;
  transform: scale(0.7);
  transition: opacity 0.22s ease, transform 0.22s cubic-bezier(0.4, 0, 0.6, 1);
}
.note-fullscreen-header {
  flex-shrink: 0;
  padding: 8px 12px;
  display: flex;
  align-items: center;
  gap: 6px;
  border-bottom: 1px solid var(--border);
}
.note-fullscreen-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 6px;
}
/* Compact the relocated Archive/Delete buttons so two of them fit
   alongside the back chevron without crowding. */
.note-fullscreen-actions .note-form-text-btn {
  padding: 8px 12px;
  font-size: 12px;
  border-radius: 8px;
}
.note-fullscreen-actions .note-form-text-btn svg {
  width: 14px;
  height: 14px;
  margin-right: 4px;
}
.note-fullscreen-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  background: none;
  border: none;
  color: var(--fg);
  border-radius: 8px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.note-fullscreen-back:active {
  background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.note-fullscreen-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 8px 14px 24px;
}
/* The reused .note-form inside the fullscreen overlay should breathe —
   it was originally sized to slot into a grid cell. */
.note-fullscreen-body .note-form {
  max-width: 720px;
  margin: 0 auto;
  border: none;
  box-shadow: none;
  background: transparent;
}

/* Bigger touch targets inside the fullscreen overlay. The default form
   is tuned for the grid-card context (tiny icons fit a 200px wide
   tile); on a phone-sized full-bleed page we want chunky thumb-sized
   controls. */
.note-fullscreen-back svg {
  width: 28px;
  height: 28px;
}
.note-fullscreen-overlay .note-form-type-pill {
  padding: 10px 16px;
  font-size: 14px;
}
.note-fullscreen-overlay .note-form-type-pill svg {
  width: 16px;
  height: 16px;
}
.note-fullscreen-overlay .note-cl-row {
  padding: 8px 0;
  gap: 8px;
  align-items: center;
}
.note-fullscreen-overlay .note-cl-grip {
  font-size: 22px;
  line-height: 1;
  padding: 6px 8px;
  opacity: 0.5;
  cursor: grab;
  touch-action: none;
  user-select: none;
  /* Pull 4px toward the left edge of the row. */
  margin-left: -4px;
}
.note-fullscreen-overlay .note-cl-grip:active { cursor: grabbing; opacity: 0.9; }
.note-fullscreen-overlay .note-cl-dot {
  width: 16px;
  height: 16px;
  border-width: 2px;
  border-radius: 50%;
  flex-shrink: 0;
  position: relative;
  /* Generous invisible tap target around the visible dot */
}
.note-fullscreen-overlay .note-cl-dot::before {
  content: '';
  position: absolute;
  inset: -12px;
}
/* Filled checkmark glyph when the row is marked done — gives a clear
   visual cue beyond the colored fill. Nudged up 2px so it sits
   centered in the smaller dot. */
.note-fullscreen-overlay .note-cl-row.done .note-cl-dot::after {
  content: '';
  position: absolute;
  left: 2px; top: 1px;
  width: 8px; height: 4px;
  border-left: 2px solid #fff;
  border-bottom: 2px solid #fff;
  transform: rotate(-45deg);
}
.note-fullscreen-overlay .note-cl-text {
  font-size: 14px;
  padding: 7px 6px;
  min-height: 32px;
}
.note-fullscreen-overlay .note-cl-rm {
  width: 40px;
  height: 40px;
  font-size: 28px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.7;
  /* Nudge 4px further toward the right edge of the row. */
  margin-right: -4px;
}
.note-fullscreen-overlay .note-cl-rm:active { opacity: 1; }
/* Lifted row + placeholder while dragging a checklist item. Mirrors
   the card-drag look so the two interactions feel consistent. */
.note-cl-row-dragging {
  box-shadow: 0 12px 28px rgba(0,0,0,0.4);
  background: var(--bg);
  border-radius: 8px;
  opacity: 0.98;
}
.note-cl-row-placeholder {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  border: 2px dashed color-mix(in srgb, var(--fg) 22%, transparent);
  border-radius: 6px;
  margin: 4px 0;
  box-sizing: border-box;
  transition: height 0.16s ease;
}
.note-fullscreen-overlay .note-form-text-btn {
  padding: 10px 16px;
  font-size: 14px;
}
.note-fullscreen-overlay .note-form-text-btn svg {
  width: 16px;
  height: 16px;
}
/* + Add (new checklist item) was tiny — bump for thumbs */
.note-fullscreen-overlay .note-cl-add {
  display: inline-flex;
  align-items: center;
  padding: 10px 18px;
  font-size: 15px;
  font-weight: 600;
  border-radius: 8px;
  letter-spacing: -0.01em;
  opacity: 0.85;
}
.note-fullscreen-overlay .note-cl-add:active { opacity: 1; }
/* + Add and the camera/photo button share a row inside checklists, so
   they're both within thumb reach when editing items. The whole row
   reads as a single dashed "tap to add" surface — tapping ANYWHERE
   on it triggers + Add (the photo button stays its own target). */
.note-fullscreen-overlay .note-cl-add-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 10px;
  padding: 4px 8px 4px 4px;
  border: 1px solid color-mix(in srgb, var(--fg) 22%, transparent);
  border-radius: 10px;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.note-fullscreen-overlay .note-cl-add-row:active {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border-color: color-mix(in srgb, var(--fg) 36%, transparent);
}
.note-fullscreen-overlay .note-cl-add-row .note-cl-add {
  flex: 1;
  justify-content: flex-start;
  background: transparent;
  border: none;
}
.note-fullscreen-overlay .note-cl-add-row .note-cl-add { margin: 0; }
.note-fullscreen-overlay .note-cl-add-row .note-form-photo-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  width: auto;
  height: 36px;
  padding: 0 12px 0 10px;
  border-radius: 8px;
  /* No border / no background — the surrounding +Add row provides the
     bordered container, the camera is just an icon inside it. */
  border: none;
  background: transparent;
}
.note-fullscreen-overlay .note-cl-add-row .note-form-photo-btn::before {
  content: '+';
  font-size: 17px;
  font-weight: 600;
  line-height: 1;
  opacity: 0.85;
}
.note-fullscreen-overlay .note-cl-add-row .note-form-photo-btn svg {
  width: 16px;
  height: 16px;
}

/* Read-mode overlay shown over the textarea for plain notes. Renders
   linkified content so URLs are tappable. Click anywhere off a link
   to flip to edit mode (focuses the underlying textarea). */
.note-form-content-reader {
  font-size: 15px;
  line-height: 1.55;
  padding: 10px 6px;
  white-space: pre-wrap;
  word-wrap: break-word;
  color: var(--fg);
  cursor: text;
  min-height: 40px;
}
.note-form-content-reader a {
  color: var(--accent, var(--red));
  text-decoration: underline;
  text-underline-offset: 2px;
}
.note-form-content-reader a:active { opacity: 0.7; }

/* Per-row read mode for todo items — replaces the bare <input> with a
   clickable linkified span when the value contains a URL. */
.note-fullscreen-overlay .note-cl-text-reader {
  flex: 1;
  padding: 7px 6px;
  font-size: 14px;
  line-height: 1.4;
  min-height: 32px;
  display: inline-flex;
  align-items: center;
  word-break: break-word;
  color: var(--fg);
  cursor: text;
}
.note-fullscreen-overlay .note-cl-text-reader a {
  color: var(--accent, var(--red));
  text-decoration: underline;
  text-underline-offset: 2px;
}
.note-fullscreen-overlay .note-cl-text-reader a:active { opacity: 0.7; }
.note-fullscreen-overlay .note-cl-row.done .note-cl-text-reader {
  opacity: 0.5;
  text-decoration: line-through;
}
/* Reminder bell + other header icon-buttons. Tightened down 2px so
   the bell sits more proportionally next to the title field. */
.note-fullscreen-overlay .note-form-icon-btn {
  width: 36px;
  height: 36px;
}
.note-fullscreen-overlay .note-form-icon-btn svg {
  width: 16px;
  height: 16px;
}

/* The plain-note textarea was rows="4" — fine for an in-grid card but
   cramped in fullscreen. Let it grow to fill the available height so
   writing feels like a real notepad, not a tweet box. The read-mode
   reader inherits the same minimum so the layout doesn't jump when
   the user taps to edit. */
.note-fullscreen-overlay .note-form-content,
.note-fullscreen-overlay .note-form-content-reader {
  min-height: 55vh;
  font-size: 15px;
  line-height: 1.55;
}

/* Tags input is JS-relocated into the bottom actions row, pinned
   left of Cancel/Update. Style it as a dashed pill so it reads as
   metadata rather than another control. */
.note-fullscreen-overlay .note-form-actions-group .note-form-label {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  padding: 8px 10px;
  margin: 0 auto 0 0;
  background: transparent;
  border: 1px dashed color-mix(in srgb, var(--fg) 16%, transparent);
  border-radius: 6px;
  text-align: left;
}

/* Reorganized meta-row layout for fullscreen: color picker pinned to
   the LEFT, type-toggle (Note/Todo/Draw) shoved to the RIGHT, with
   the photo button next to the color picker. Tags are moved up into
   the form header (next to the reminder bell) via JS. */
.note-fullscreen-overlay .note-form-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding-top: 8px;
}
.note-fullscreen-overlay .note-color-picker { order: 1; }
.note-fullscreen-overlay .note-form-photo-btn { order: 2; }
.note-fullscreen-overlay .note-form-type-seg {
  order: 3;
  margin-left: auto;  /* shove right */
  /* The overlay enlarges the pills (padding 10px), but the base seg is only
     28px tall with overflow:hidden — which clipped them. Grow the track so
     the bigger touch targets fit. */
  height: 40px;
  border-radius: 12px;
}
.note-fullscreen-overlay .note-form-actions-group {
  order: 4;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}
.note-fullscreen-overlay .note-form-actions-group .note-form-cancel,
.note-fullscreen-overlay .note-form-actions-group .note-form-save {
  flex: 0 0 auto;
}

.notes-mobile-grabber { display: none; }
/* Notes panel: mounted like a window, docked to the right on desktop. */
.notes-pane-backdrop {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  pointer-events: none;
  animation: notes-backdrop-fade-in 180ms ease both;
}
.notes-pane-backdrop .notes-pane {
  pointer-events: auto;
}
.notes-pane-backdrop:has(.notes-pane.modal-right-docked),
.notes-pane-backdrop:has(.notes-pane.modal-left-docked) {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  pointer-events: none;
}
.notes-pane-backdrop:has(.notes-pane.modal-right-docked) .notes-pane,
.notes-pane-backdrop:has(.notes-pane.modal-left-docked) .notes-pane {
  pointer-events: auto;
}
.notes-pane-backdrop:has(.notes-pane.notes-window-fullscreen) {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
@keyframes notes-backdrop-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.notes-pane {
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  width: min(880px, 92vw);
  height: min(80vh, 820px);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45);
  box-sizing: border-box;
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  letter-spacing: -0.015em;
  /* Smooth open: scale up + fade in from the centre. */
  animation: notes-pane-enter 200ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
  transform-origin: center center;
  will-change: transform, opacity;
}
.notes-pane.modal-right-docked {
  border-left: 1px solid var(--border);
  border-radius: 0;
  box-shadow: -4px 0 14px rgba(0, 0, 0, 0.18);
  transition: none !important;
}
.notes-pane.modal-left-docked {
  border-right: 1px solid var(--border);
  border-radius: 0;
  box-shadow: 4px 0 14px rgba(0, 0, 0, 0.18);
  transition: none !important;
}
@keyframes notes-pane-enter {
  from { transform: scale(0.96); opacity: 0; }
  to   { transform: scale(1);    opacity: 1; }
}
.notes-pane.notes-pane-leaving {
  animation: notes-pane-leave 160ms cubic-bezier(0.4, 0, 1, 1) both;
  pointer-events: none;
}
@keyframes notes-pane-leave {
  from { transform: scale(1);    opacity: 1; }
  to   { transform: scale(0.96); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .notes-pane,
  .notes-pane.notes-pane-leaving { animation: none; }
}
.notes-pane-header {
  display: flex;
  align-items: center;
  gap: 4px;
  justify-content: space-between;
  padding: 0 0 6px;
  margin: 10px 10px 0;
  border-bottom: 1px solid var(--border);
  background: inherit;
  flex-shrink: 0;
  flex-wrap: nowrap;
  min-height: 30px;
  overflow: hidden;
}

/* Archive mode — sepia tint over the panel + a header strip so it's clearly
   a different view. Active reminders sort + glow rules don't apply here. */
.notes-pane-archive {
  background: color-mix(in srgb, #b48a4a 6%, var(--bg));
}
.notes-pane-archive .notes-pane-header {
  background: color-mix(in srgb, #b48a4a 12%, var(--bg));
  border-bottom-color: color-mix(in srgb, #b48a4a 35%, var(--border));
}
.notes-pane-archive .notes-pane-body {
  background: color-mix(in srgb, #b48a4a 4%, var(--bg));
}
/* Smooth the archive↔active swap so the colour tint eases in/out instead
   of snapping. Covers the pane, its header, and its body. */
.notes-pane,
.notes-pane .notes-pane-header,
.notes-pane .notes-pane-body {
  transition: background 0.28s ease, border-color 0.28s ease;
}
.notes-pane-archive .notes-pane-title::after {
  content: 'Archive';
  margin-left: 8px;
  padding: 2px 8px 2px 22px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: #b48a4a;
  background-color: color-mix(in srgb, #b48a4a 18%, transparent);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b48a4a' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='3' width='20' height='5' rx='1'/%3E%3Cpath d='M4 8v11a2 2 0 002 2h12a2 2 0 002-2V8'/%3E%3Cpath d='M10 12h4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: 6px center;
  background-size: 11px 11px;
  border: 1px solid color-mix(in srgb, #b48a4a 45%, transparent);
  border-radius: 10px;
  vertical-align: middle;
}
/* Hide the "Add a to-do…" quick-add bar in archive view — you can't add
   new items from the archive. */
.notes-pane-archive .notes-quick-add { display: none !important; }
.notes-pane-archive .note-card {
  background: color-mix(in srgb, #b48a4a 8%, var(--panel));
  border-color: color-mix(in srgb, #b48a4a 25%, var(--border));
}
.notes-pane-header .doc-action-icon-btn {
  flex-shrink: 0;
}
.notes-pane-header .notes-pane-title {
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex-shrink: 1;
}
.notes-pane-footer {
  padding: 6px 8px;
  border-top: 1px solid var(--border);
  background: var(--panel);
  flex-shrink: 0;
  display: flex;
  gap: 6px;
}
.notes-new-btn {
  background: none;
  border: 1px dashed color-mix(in srgb, var(--accent) 40%, transparent);
  color: color-mix(in srgb, var(--fg) 50%, transparent);
  border-radius: 6px;
  padding: 6px 0;
  flex: 1;
  cursor: pointer;
  font-size: 11px;
  font-family: inherit;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  transition: border-color 0.15s, color 0.15s;
}
.notes-new-btn:hover {
  border-color: var(--accent);
  color: var(--fg);
}
.notes-pane-title {
  /* Match the other tool headers (.modal-header h4). */
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--red);
  white-space: nowrap;
  height: 24px;
  line-height: 22px;
  padding: 0 6px;
  margin: 6px 0 0 0;
  display: inline-block;
  vertical-align: middle;
  /* Optical nudge — sits 1px above its row baseline on desktop, more on mobile
     where the surrounding controls are taller. Overridden in the mobile
     media block below. */
  position: relative;
  top: -1px;
  box-sizing: border-box;
}
@media (max-width: 768px) {
  .notes-pane-title { top: -4px; }
  .notes-pane-title svg {
    position: relative;
    top: -2px;
  }
  /* Mobile header is tight — keep Archive / Toggle View icon-only. */
  .notes-header-btn-label { display: none; }
  .notes-header-text-btn { width: 32px !important; padding: 0 !important; }
}
.notes-header-btn-label {
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.2px;
  white-space: nowrap;
}
.notes-header-text-btn {
  width: auto !important;
  padding: 0 8px !important;
}
.notes-pane-body {
  flex: 1;
  overflow-y: auto;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  scrollbar-width: thin;
}

.note-card.note-card-sliding-out {
  transform: translateX(120%);
  opacity: 0;
  transition: transform 0.32s ease-in, opacity 0.32s ease-in;
  pointer-events: none;
}

/* ⋯ corner menu button (replaced the copy corner button) */
.note-card-corner-menu {
  background: none;
  border: none;
  color: var(--fg);
  cursor: pointer;
  padding: 2px;
  opacity: 0.5;
  transition: opacity 0.15s;
  display: inline-flex;
}
.note-card-corner-menu:hover { opacity: 0.9; }
/* Dropdown spawned by the ⋯ menu */
.note-corner-menu-dropdown {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.3);
  padding: 4px;
  min-width: 168px;
  font-size: 12px;
  animation: ncm-pop 0.12s ease-out both;
}
@keyframes ncm-pop { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }
.note-corner-menu-dropdown .ncm-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  background: none;
  border: none;
  color: var(--fg);
  font-family: inherit;
  font-size: 12px;
  text-align: left;
  padding: 8px 10px;
  border-radius: 6px;
  cursor: pointer;
}
.note-corner-menu-dropdown .ncm-item:hover {
  background: color-mix(in srgb, var(--fg) 8%, transparent);
}
/* "Agent" tag on a note that has a linked agent chat session */
.note-agent-tag {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
  color: var(--accent, var(--red));
  border-radius: 999px;
  padding: 3px 10px 3px 8px;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  margin-top: 2px;
  transition: background 0.12s;
}
.note-agent-tag:hover { background: color-mix(in srgb, var(--accent, var(--red)) 24%, transparent); }

.note-card {
  /* Same tint that .doclib-card uses so a default (uncolored) note
     visually separates from the panel background it sits on. */
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  border: 1px solid var(--border);
  border-radius: 8px;
  /* Extra right padding so the corner buttons (pin / edit / copy / done)
     never collide with the title, and a min-height so an empty/short
     note is still tall enough to fit that row of corner controls. */
  padding: 10px 14px;
  padding-right: 30px;
  min-height: 64px;
  cursor: default;
  transition: background 0.15s;
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* Keep drawings/images clipped to the card so they can't spill outside it —
     but allow a small overhang (the pin bubble sits at right:-8px and would
     otherwise be cropped). overflow-clip-margin extends the clip-box without
     letting full images bleed past. */
  overflow: clip;
  overflow-clip-margin: 14px;
  /* Don't shrink inside the flex-column list pane — otherwise cards squish to
     fit (clipped/too short) and the container never overflows, so it won't
     scroll. Keeping natural height makes the list overflow → scrollable. */
  flex-shrink: 0;
}
.note-card:hover:not([class*="note-color-"]) {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
}
.note-card { position: relative; }
.note-card-image { max-width: 100%; }

/* Mobile: bigger, more legible note cards + a sticky quick-add bar so you can
   add a to-do without scrolling back up; and keep media inside the card. */
@media (max-width: 768px) {
  .note-card { padding: 14px 16px; padding-right: 34px; min-height: 84px; font-size: 15px; }
  .note-card-image { max-height: 260px; max-width: 100%; object-fit: cover; }
  /* Sticky create-todo bar — pin it to the top of the scrolling list pane.
     Safe now that the cards no longer shrink (the list actually overflows). */
  .notes-quick-add {
    position: sticky;
    top: -8px;
    z-index: 12;
    flex-shrink: 0;
    margin-top: 0;
    box-shadow: 0 -8px 0 8px var(--panel), 0 8px 0 0 var(--panel);
  }
}
.note-card-pinned {
  border-top: 2px solid color-mix(in srgb, var(--fg) 30%, transparent);
}
.note-card.note-card-reminder-fired {
  animation: note-reminder-pulse 1.2s ease-in-out 2;
}
@keyframes note-reminder-pulse {
  0%, 100% {
    box-shadow:
      inset 0 2px 0 0 color-mix(in srgb, var(--fg) 14%, transparent),
      0 0 0 0 color-mix(in srgb, var(--accent, var(--red)) 0%, transparent);
  }
  50% {
    box-shadow:
      inset 0 2px 0 0 color-mix(in srgb, var(--fg) 14%, transparent),
      0 0 0 5px color-mix(in srgb, var(--accent, var(--red)) 26%, transparent),
      0 0 18px color-mix(in srgb, var(--accent, var(--red)) 30%, transparent);
  }
}
/* Sticky outside glow — applied to the exact note whose reminder fired.
   It stays until the user interacts with that card. */
.note-card.note-card-reminder-fired-sticky {
  position: relative;
  outline: 1px solid color-mix(in srgb, var(--accent, var(--red)) 36%, transparent);
  outline-offset: 2px;
  animation: note-reminder-glow 1.6s ease-in-out infinite;
}
@keyframes note-reminder-glow {
  0%, 100% {
    box-shadow:
      inset 0 2px 0 0 color-mix(in srgb, var(--fg) 14%, transparent),
      0 0 0 3px color-mix(in srgb, var(--accent, var(--red)) 14%, transparent),
      0 0 14px color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  }
  50% {
    box-shadow:
      inset 0 2px 0 0 color-mix(in srgb, var(--fg) 14%, transparent),
      0 0 0 6px color-mix(in srgb, var(--accent, var(--red)) 22%, transparent),
      0 0 26px color-mix(in srgb, var(--accent, var(--red)) 30%, transparent);
  }
}
.note-card-selected {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* Hover-only buttons (Google Keep style) */
.note-card-select,
.note-card-pin {
  position: absolute;
  top: -8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 50%;
  width: 18px;
  height: 18px;
  padding: 0;
  cursor: pointer;
  display: none;
  align-items: center;
  justify-content: center;
  color: var(--fg);
  opacity: 0.7;
  z-index: 2;
  transition: opacity 0.15s, transform 0.15s, background 0.15s;
}
.note-card-select { left: -8px; }
.note-card-pin    { right: -8px; }
/* Pencil + checkmark glyphs both have empty space along the top of their
   24×24 viewBox, so flex-centering leaves them sitting visually high.
   Nudge the inner SVG down a touch to compensate. */
.note-card-edit-corner svg,
.note-card-done svg {
  position: relative;
  top: 0.5px;
}
.note-card-edit-corner {
  position: absolute;
  /* Both Edit + Done at top-right: Edit on the left (right:40px),
     Done on the right (right:6px). */
  top: 6px; right: 40px;
  width: 28px; height: 28px;
  /* Fully invisible at rest. Materializes (with a solid panel fill blocking
     any text underneath) only when the card is hovered or the button is
     focused. Same rule on touch — opacity stays 0 until the card is tapped
     to expose actions. */
  background: transparent;
  border: 1px solid transparent;
  border-radius: 50%;
  cursor: pointer; padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--fg); opacity: 0;
  touch-action: manipulation;
  transition: opacity 0.15s, background 0.15s, border-color 0.15s;
  z-index: 2;
}
.note-card:hover .note-card-edit-corner {
  opacity: 0.85;
  background: var(--panel);
  border-color: color-mix(in srgb, var(--fg) 18%, transparent);
}
.note-card-edit-corner:hover,
.note-card-edit-corner:focus-visible {
  opacity: 1 !important;
  background: color-mix(in srgb, var(--accent) 18%, var(--panel)) !important;
  border-color: color-mix(in srgb, var(--accent) 50%, var(--border)) !important;
}
@media (hover: none) {
  .note-card-edit-corner {
    opacity: 0.7;
    background: var(--panel);
    border-color: color-mix(in srgb, var(--fg) 18%, transparent);
  }
}
/* Copy corner — bottom-right counterpart to the edit/done corner buttons.
   Round, subtle, hover-reveal on desktop and always faintly visible on
   touch so mobile users can tap it without needing hover. */
.note-card-copy-corner {
  position: absolute;
  bottom: 6px;
  right: 6px;
  top: auto;
  width: 26px;
  height: 26px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 50%;
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--fg);
  opacity: 0;
  touch-action: manipulation;
  transition: opacity 0.15s, background 0.15s, border-color 0.15s;
  z-index: 2;
}
.note-card:hover .note-card-copy-corner {
  opacity: 0.7;
  background: var(--panel);
  border-color: color-mix(in srgb, var(--fg) 18%, transparent);
}
.note-card-copy-corner:hover,
.note-card-copy-corner:focus-visible {
  opacity: 1 !important;
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, var(--panel)) !important;
  border-color: color-mix(in srgb, var(--accent, var(--red)) 50%, var(--border)) !important;
}
@media (hover: none) {
  .note-card-copy-corner {
    opacity: 0.6;
    background: var(--panel);
    border-color: color-mix(in srgb, var(--fg) 18%, transparent);
  }
}
/* Pin button on mobile — only reveal once the user has long-pressed to
   enter rearrange (drag) mode. Outside drag mode the card stays clean. */
body.notes-mobile-mode.notes-drag-mode .note-card-pin {
  display: flex !important;
  opacity: 0.7;
}
body.notes-mobile-mode.notes-drag-mode .note-card-pin.active {
  opacity: 1;
}
/* Unarchive corner reuses the pencil's spot in archive view. */
.note-card-unarchive-corner:hover,
.note-card-unarchive-corner:focus-visible {
  background: color-mix(in srgb, var(--accent) 22%, transparent) !important;
  border-color: color-mix(in srgb, var(--accent) 50%, var(--border)) !important;
  color: var(--accent);
}

/* "Done" pill in the bottom-right of every active note card. Visible-only on
   hover (and always on touch) so it's clearly readable but doesn't clutter
   the grid at rest. Distinct from the edit pencil so it's obvious what it
   does — green checkmark + label, not just an icon. */
/* Matches .note-card-edit-corner footprint (22×22 circle), just anchored
   to the bottom-right corner instead of top-right. */
.note-card-done {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 28px;
  height: 28px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 50%;
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--green, #98c379);
  opacity: 0;
  touch-action: manipulation;
  transition: opacity 0.15s, background 0.15s, border-color 0.15s;
  z-index: 2;
}
.note-card:hover .note-card-done {
  opacity: 0.85;
  background: var(--panel);
  border-color: color-mix(in srgb, var(--fg) 18%, transparent);
}
.note-card-done:hover,
.note-card-done:focus-visible {
  opacity: 1 !important;
  background: color-mix(in srgb, var(--green, #98c379) 22%, var(--panel)) !important;
  border-color: color-mix(in srgb, var(--green, #98c379) 50%, var(--border)) !important;
}
@media (hover: none) {
  .note-card-done {
    opacity: 0.7;
    background: var(--panel);
    border-color: color-mix(in srgb, var(--fg) 18%, transparent);
  }
}
/* Edit + Done are now side-by-side at top-right by default — title-only
   fallback no longer needed. */
.note-card-selectmode .note-card-done { display: none !important; }

/* Copy corner — bottom-right of the card. Same opaque-fill pattern as
   trash/unarchive so the underlying text/image never bleeds through. */
.note-card-corner-copy {
  position: absolute;
  bottom: 6px;
  right: 6px;
  width: 28px;
  height: 28px;
  background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--fg) 18%, transparent);
  border-radius: 50%;
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--fg);
  visibility: hidden;
  touch-action: manipulation;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
  z-index: 2;
}
.note-card-corner-copy svg { position: relative; top: 1px; opacity: 0.85; transition: opacity 0.15s; }
.note-card:hover .note-card-corner-copy { visibility: visible; }
.note-card-corner-copy:hover,
.note-card-corner-copy:focus-visible {
  background: color-mix(in srgb, var(--accent) 18%, var(--panel));
  border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
  color: var(--accent);
}
.note-card-corner-copy:hover svg { opacity: 1; }
@media (hover: none) {
  .note-card-corner-copy { visibility: visible; }
  .note-card-corner-copy svg { opacity: 0.7; }
}
/* Copy stays on every active card regardless of body shape — Edit + Done
   live at the top-right pair, so the bottom-right copy slot is always
   free. Hidden only in select mode. */
.note-card-selectmode .note-card-corner-copy { display: none !important; }

/* ── Archive view corners — trash (top-left) + unarchive (top-right) ── */
/* Background fill stays fully opaque (var(--panel)) so the note text or
   image behind doesn't bleed through. Visibility is gated by `visibility`
   + a faded ICON instead of `opacity` on the whole button. */
.note-card-corner-trash,
.note-card-corner-unarchive {
  position: absolute;
  top: 6px;
  width: 28px;
  height: 28px;
  background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--fg) 18%, transparent);
  border-radius: 50%;
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--fg);
  visibility: hidden;
  touch-action: manipulation;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
  z-index: 2;
}
.note-card-corner-trash svg,
.note-card-corner-unarchive svg { opacity: 0.85; transition: opacity 0.15s; }
.note-card-corner-trash { left: 6px; }
.note-card-corner-unarchive { right: 6px; }
.note-card:hover .note-card-corner-trash,
.note-card:hover .note-card-corner-unarchive { visibility: visible; }
.note-card-corner-trash:hover,
.note-card-corner-trash:focus-visible {
  background: color-mix(in srgb, var(--red) 18%, var(--panel));
  border-color: color-mix(in srgb, var(--red) 50%, var(--border));
  color: var(--red);
}
.note-card-corner-trash:hover svg,
.note-card-corner-unarchive:hover svg { opacity: 1; }
.note-card-corner-unarchive:hover,
.note-card-corner-unarchive:focus-visible {
  background: color-mix(in srgb, var(--accent) 18%, var(--panel));
  border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
  color: var(--accent);
}
@media (hover: none) {
  .note-card-corner-trash,
  .note-card-corner-unarchive { visibility: visible; }
  .note-card-corner-trash svg,
  .note-card-corner-unarchive svg { opacity: 0.7; }
}

/* ─── Auto-AI ✨ chip on note cards ─────────────────────────────────── */
.note-card-ai-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: 6px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
  font-family: inherit;
  background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
  color: var(--accent, var(--red));
  cursor: pointer;
  transition: background 0.12s, transform 0.12s, box-shadow 0.12s;
  vertical-align: middle;
}
.note-card-ai-chip:hover {
  background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
  transform: translateY(-1px);
  box-shadow: 0 2px 6px color-mix(in srgb, var(--accent, var(--red)) 30%, transparent);
}
.note-card-ai-chip svg {
  flex-shrink: 0;
  animation: note-ai-shine 2.4s ease-in-out infinite;
}
@keyframes note-ai-shine {
  0%, 100% { opacity: 0.85; filter: drop-shadow(0 0 0 transparent); }
  50%      { opacity: 1;    filter: drop-shadow(0 0 4px color-mix(in srgb, var(--accent, var(--red)) 60%, transparent)); }
}
.note-card-ai-solving {
  cursor: default;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  border-color: color-mix(in srgb, var(--fg) 18%, transparent);
  color: color-mix(in srgb, var(--fg) 70%, transparent);
}
.note-card-ai-solving svg { animation: spin 0.9s linear infinite; filter: none; }
.note-card-ai-done { background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent); }
.note-card-ai-done svg { animation: none; }
#notes-auto-ai-toggle.active { color: var(--accent, var(--red)); }

/* ─── AI improve button (note form header) ───────────────────────────── */
.note-form-improve-btn {
  width: 38px !important;
  height: 38px !important;
  color: var(--accent, var(--red));
}
.note-form-improve-btn svg {
  width: 22px !important;
  height: 22px !important;
  animation: note-ai-shine 2.4s ease-in-out infinite;
}
.note-form-improve-btn:disabled,
.note-form-improve-btn.busy {
  opacity: 0.6;
  cursor: wait;
}
.note-form-improve-btn.busy svg {
  animation: spin 0.9s linear infinite;
  filter: none;
}

.note-card:hover .note-card-select,
.note-card:hover .note-card-pin,
.note-card-selected .note-card-select,
.note-card-pinned .note-card-pin {
  display: flex;
}
.note-card-select:hover, .note-card-pin:hover {
  opacity: 1;
  transform: scale(1.15);
  background: color-mix(in srgb, var(--accent) 15%, var(--bg));
}
.note-card-pin.active {
  display: flex;
  background: color-mix(in srgb, var(--accent, var(--red)) 12%, var(--bg));
  border-color: color-mix(in srgb, var(--accent, var(--red)) 45%, var(--border));
  color: var(--accent, var(--red));
  opacity: 1;
}
.note-card-selected .note-card-select {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  opacity: 1;
}

/* Select-mode checkbox on cards (mirrors docs library) */
.note-card-cb {
  position: absolute;
  top: 6px;
  left: 6px;
  z-index: 3;
  margin: 0;
}
.note-card-selectmode {
  padding-left: 28px;
  cursor: pointer;
}
/* In select mode the whole card is a checkbox — kill every hover affordance
   that would otherwise suggest "preview" or "open" so users know exactly
   what their tap will do (toggle selection). */
.note-card-selectmode .note-card-pin,
.note-card-selectmode .note-card-actions { pointer-events: none; opacity: 0.4; }
.note-card-selectmode .note-card-edit-corner,
.note-card-selectmode .note-card-pin {
  display: none !important;
}
.note-card-selectmode:hover { background: color-mix(in srgb, var(--fg) 3%, transparent) !important; }
.note-card-selectmode .note-card-title:hover { opacity: 1 !important; }
.note-card-selectmode .note-card-edit-corner,
.note-card-selectmode:hover .note-card-edit-corner { opacity: 0 !important; }

/* Bottom action row — hidden everywhere. The archive/delete actions and the
   color picker now live inside the edit form (click the pencil corner). */
.note-card-actions { display: none; }
.note-agent-menu {
  background: var(--panel, var(--bg));
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.3);
  padding: 4px 0;
  min-width: 140px;
  font-size: 12px;
}
.note-card-colors {
  display: flex;
  gap: 3px;
}
/* Re-asserted AFTER the base rule above so the mobile-hide actually
   wins the cascade — earlier @media (hover:none) block was being
   overridden because the unconditional display:flex rule came later. */
@media (hover: none) {
  .note-card-colors { display: none; }
}
.note-card-color-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  cursor: pointer;
  border: 1.5px solid color-mix(in srgb, var(--fg) 20%, transparent);
  transition: transform 0.15s, border-color 0.15s;
}
.note-card-color-dot:hover {
  transform: scale(1.25);
  border-color: var(--fg);
}
.note-card-color-dot.active {
  border-color: var(--fg);
  border-width: 2px;
}
.note-card-action {
  background: none;
  border: none;
  cursor: pointer;
  padding: 3px;
  color: var(--fg);
  opacity: 0.4;
  display: flex;
  align-items: center;
  border-radius: 3px;
  transition: opacity 0.15s, background 0.15s;
}
.note-card-action svg { transform: translateY(2px); }

/* Note image */
.note-card-image {
  width: 100%;
  max-height: 180px;
  object-fit: cover;
  border-radius: 4px;
  margin: 4px 0;
  display: block;
}
.note-form-image-wrap {
  position: relative;
  margin: 4px 0;
}
.note-form-image {
  width: 100%;
  max-height: 200px;
  object-fit: cover;
  border-radius: 4px;
  display: block;
  /* Images are draggable=true by default; that catches the mousedown
     and beats the X button to the click. Disabling drag at the CSS
     level (the -webkit-user-drag prop) keeps the X button reliably
     clickable even if the JS forgets to set draggable="false" on the
     element. Unprefixed user-drag was never standardized — Firefox
     drops it with a warning. */
  -webkit-user-drag: none;
  user-select: none;
}
.note-form-image-rm {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 5;
  background: rgba(0,0,0,0.65);
  border: none;
  color: #fff;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 20px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.note-form-photo-btn {
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg);
  cursor: pointer;
  border-radius: 4px;
  width: auto;
  min-width: 32px;
  height: 32px;
  padding: 0 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  opacity: 1;
  transition: opacity 0.15s, border-color 0.15s, background 0.15s;
}
.note-form-photo-btn:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }

/* Quick-add bar (always at top) */
.notes-quick-add {
  display: flex;
  align-items: center;
  gap: 4px;
  background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, var(--border));
  border-radius: 6px;
  /* Toggle pill sits at the very left of the bar — no inset padding. The
     pill's own border provides visual breathing room from the wrapper edge. */
  padding: 4px 6px 4px 4px;
  margin-top: 2px;
  margin-bottom: 8px;
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
  /* Subtle idle glow so the field reads as "you can type here" without
     the user having to click it first. Fades out the moment the user
     hovers or focuses it. */
  animation: notes-quick-pulse 2.8s ease-in-out infinite;
}
@keyframes notes-quick-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent, var(--red)) 0%, transparent); }
  50%      { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, var(--red)) 14%, transparent); }
}
.notes-quick-add:hover {
  border-color: color-mix(in srgb, var(--accent, var(--red)) 55%, var(--border));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  animation: none;
}
.notes-quick-add:focus-within {
  border-color: var(--accent, var(--red));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, var(--red)) 25%, transparent);
  background: color-mix(in srgb, var(--fg) 3%, var(--panel));
  animation: none;
}
.notes-quick-input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: var(--fg);
  caret-color: var(--accent, var(--red));
  font-family: inherit;
  font-size: 12px;
  padding: 6px 0;
}
.notes-quick-input::placeholder {
  color: color-mix(in srgb, var(--fg) 45%, transparent);
}
/* Blinking text caret hint before the placeholder so empty + unfocused
   reads as a live input. Sits on the wrapper because <input> elements
   can't carry ::before. The pseudo-element is ALWAYS rendered (so its
   width is reserved and the placeholder doesn't jump left when the
   hint hides); only the colour/animation toggle, not the layout. */
.notes-quick-add::before {
  content: '|';
  color: var(--accent, var(--red));
  margin-right: 6px;
  font-weight: 500;
  font-size: 13px;
  animation: notes-quick-caret 1s steps(2, jump-none) infinite;
  pointer-events: none;
}
/* Hide visually (keep space) on hover, focus, or once the input has any
   text — prevents the layout shift the user reported when the hint
   collapsed and the placeholder slid left. */
.notes-quick-add:hover::before,
.notes-quick-add:focus-within::before,
.notes-quick-add:not(:has(.notes-quick-input:placeholder-shown))::before {
  visibility: hidden;
  animation: none;
}
@keyframes notes-quick-caret {
  0%, 49%  { opacity: 1; }
  50%, 100%{ opacity: 0; }
}
.notes-quick-icon {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.45;
  cursor: pointer;
  padding: 5px;
  border-radius: 4px;
  display: flex;
  align-items: center;
  transition: opacity 0.15s, background 0.15s;
}
.notes-quick-icon:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
}
/* 2-pill Note/Todo toggle in the quick-add bar — same look as the form's
   type-seg but slimmer to fit the compact row. */
.notes-quick-type-seg {
  display: inline-flex;
  height: 28px;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  position: relative;
  flex-shrink: 0;
  /* Negative order pushes the toggle BEFORE the wrapper's ::before caret
     hint, so the blinking | sits between the toggle and the input rather
     than at the far-left edge. */
  order: -1;
}
.notes-quick-type-seg::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 50%;
  height: 100%;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  border-radius: 6px;
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
  z-index: 0;
}
.notes-quick-type-seg.is-todo::before { transform: translateX(100%); }
.notes-quick-type-pill {
  background: none;
  border: none;
  color: color-mix(in srgb, var(--fg) 40%, transparent);
  cursor: pointer;
  padding: 0 11px;
  font-family: inherit;
  transition: color 0.2s;
  white-space: nowrap;
  height: 100%;
  display: inline-flex;
  align-items: center;
  position: relative;
  z-index: 1;
}
.notes-quick-type-pill svg {
  width: 16px;
  height: 16px;
}
.notes-quick-type-pill:not(.active):hover {
  color: color-mix(in srgb, var(--fg) 65%, transparent);
}
.notes-quick-type-pill.active,
.notes-quick-type-pill.active:hover,
.notes-quick-type-pill.active:focus {
  color: var(--fg);
  cursor: default;
}
/* Mobile: thumb-sized Note/Todo toggle. The 22px desktop pill is too
   small to hit reliably on a phone. */
@media (max-width: 768px) {
  .notes-quick-type-seg {
    height: 36px;
    border-radius: 10px;
  }
  .notes-quick-type-seg::before {
    border-radius: 9px;
  }
  .notes-quick-type-pill {
    padding: 0 14px;
  }
  .notes-quick-type-pill svg {
    width: 17px;
    height: 17px;
  }
  /* Match the input + photo button heights to the bigger toggle so the
     row reads as one consistent bar. */
  .notes-quick-input {
    height: 36px;
    font-size: 15px;
  }
  .notes-quick-icon {
    padding: 9px;
  }
  .notes-quick-icon svg {
    width: 18px;
    height: 18px;
  }
}
.notes-empty-msg {
  text-align: center;
  /* 0.4 dropped this empty-state text to ~2.8:1; 0.65 keeps it readable
     (WCAG AA) while staying visibly secondary. */
  opacity: 0.65;
  padding: 30px 20px;
  font-size: 11px;
}

/* Loading skeleton */
.notes-skeleton {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 4px;
}
.notes-skeleton-card {
  height: 72px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--fg) 4%, transparent) 0%,
    color-mix(in srgb, var(--fg) 10%, transparent) 50%,
    color-mix(in srgb, var(--fg) 4%, transparent) 100%
  );
  background-size: 200% 100%;
  animation: notes-skeleton-shimmer 1.4s ease-in-out infinite;
}
.notes-skeleton-card.short { height: 46px; }
@keyframes notes-skeleton-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* Type toggle (note vs todo) */
.note-form-type-row {
  display: flex;
  gap: 4px;
  margin-top: -2px;
}
/* Reminder bell button */
.note-form-remind-btn {
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg);
  border-radius: 4px;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 1;
  position: relative;
  top: -2px;
  transition: opacity 0.15s, border-color 0.15s, background 0.15s, color 0.15s;
}
.note-form-remind-btn:hover { opacity: 1; border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.note-form-remind-btn.has-date { color: var(--accent); border-color: var(--accent); }

/* Calendar event form + notes form: jingle the little bell when the user
   picks a reminder. Anchored near the top of the bell so the swing reads
   as the bell clanging side-to-side. */
.cal-remind-bell,
.note-form-remind-btn > svg {
  transform-origin: 50% 4px;
  transition: transform 0.1s ease;
}
.cal-remind-bell.jingling,
.note-form-remind-btn > svg.jingling {
  animation: cal-bell-jingle 0.65s cubic-bezier(0.36, 0, 0.66, -0.56) both;
}
@keyframes cal-bell-jingle {
  0%   { transform: rotate(0deg); }
  15%  { transform: rotate(-22deg); }
  30%  { transform: rotate(18deg); }
  45%  { transform: rotate(-14deg); }
  60%  { transform: rotate(10deg); }
  75%  { transform: rotate(-6deg); }
  88%  { transform: rotate(3deg); }
  100% { transform: rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .cal-remind-bell.jingling,
  .note-form-remind-btn > svg.jingling { animation: none; }
}

/* Reminder tag inside form */
.note-form-reminder-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.note-form-reminder-tags:empty { display: none; }
.note-reminder-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
  border: none;
  border-radius: 12px;
  padding: 4px 4px 4px 9px;
  font-size: 10px;
  font-family: inherit;
  cursor: pointer;
  font-weight: 500;
}
.note-reminder-tag:hover { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.note-reminder-tag-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 25%, transparent);
  font-size: 12px;
  line-height: 1;
  position: relative;
  top: -2px;
}
.note-reminder-tag-x:hover { background: var(--red); color: #fff; }

/* Reminder tag on card */
.note-card-reminder {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  font-size: 9px;
  font-weight: 500;
  padding: 2px 7px;
  border-radius: 10px;
  align-self: flex-start;
  margin-top: 2px;
}
.note-card-reminder.overdue {
  background: color-mix(in srgb, var(--red) 18%, transparent);
  color: var(--red);
}

/* Reminder dropdown menu */
.note-reminder-menu {
  position: fixed;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 6px 24px rgba(0,0,0,0.25);
  /* Above the fullscreen note overlay (z-index: 10500) — otherwise the
     reminder picker opens hidden behind it on mobile and reads as
     "broken". */
  z-index: 12000;
  min-width: 220px;
  padding: 6px 0;
  font-family: inherit;
  font-size: 12px;
}
.note-reminder-menu-title {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  opacity: 0.5;
  padding: 6px 14px 4px;
  letter-spacing: 0.5px;
}
.note-reminder-menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: none;
  border: none;
  color: var(--fg);
  font-family: inherit;
  font-size: 12px;
  padding: 7px 14px;
  cursor: pointer;
  text-align: left;
  transition: background 0.1s;
}
.note-reminder-menu-item:hover { background: color-mix(in srgb, var(--fg) 6%, transparent); }
.note-reminder-menu-item.active { color: var(--accent); }
.note-reminder-menu-sub {
  font-size: 10px;
  opacity: 0.5;
  margin-left: 12px;
}
.note-reminder-menu-check { color: var(--accent); font-size: 12px; }
.note-reminder-menu-divider {
  height: 1px;
  background: var(--border);
  margin: 4px 0;
}
.note-reminder-menu-picker {
  padding: 6px 14px 8px;
}
.note-reminder-date-input {
  width: 100%;
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg);
  font-family: inherit;
  font-size: 12px;
  padding: 6px 8px;
  outline: none;
  color-scheme: dark light;
  box-sizing: border-box;
}
.note-reminder-date-input:focus { border-color: var(--accent); }
.note-reminder-menu-confirm {
  font-weight: 600;
  color: var(--accent);
  justify-content: center;
}
.note-reminder-menu-confirm.disabled,
.note-reminder-menu-confirm[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
  color: var(--fg);
}
.note-reminder-menu-arrow {
  opacity: 0.5;
  margin-left: 8px;
  font-size: 14px;
  line-height: 1;
}
.note-reminder-menu-back {
  display: flex;
  align-items: center;
  gap: 4px;
  width: 100%;
  background: none;
  border: none;
  color: var(--fg);
  font-family: inherit;
  font-size: 11px;
  opacity: 0.7;
  padding: 6px 12px;
  cursor: pointer;
  text-align: left;
  transition: opacity 0.1s, background 0.1s;
}
.note-reminder-menu-back:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 6%, transparent); }
.note-reminder-menu-arrow-back {
  font-size: 14px;
  line-height: 1;
}
.note-reminder-menu-sublabel {
  font-size: 10px;
  opacity: 0.5;
  padding: 6px 14px 2px;
  letter-spacing: 0.3px;
}
.note-reminder-weekday-row {
  display: flex;
  gap: 4px;
  padding: 4px 12px 8px;
  flex-wrap: wrap;
}
.note-reminder-day-chip {
  flex: 1 1 0;
  min-width: 24px;
  height: 28px;
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg);
  font-family: inherit;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.1s, border-color 0.1s, color 0.1s;
  padding: 0;
}
.note-reminder-day-chip.wide { padding: 0 6px; }
.note-reminder-day-chip:hover {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
}
.note-reminder-day-chip.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.note-form-type-btn,
.note-form-type-toggle {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg);
  cursor: pointer;
  border-radius: 4px;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 1;
  transition: opacity 0.15s, background 0.15s, border-color 0.15s;
}
.note-form-type-toggle:hover { opacity: 1; border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.note-form-type-toggle[data-type="todo"] { color: var(--accent); }

/* Note | Todo | Draw segmented toggle — mirrors .mode-toggle (agent/chat) */
.note-form-type-seg {
  display: inline-flex;
  height: 32px;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  position: relative;
  flex-shrink: 0;
}
.note-form-type-seg::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  /* 3 pills now (note / todo / draw) — Goal was removed. Slider takes
     1/3 of the track and translates by full multiples of itself. */
  width: 33.3333%;
  height: 100%;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  border-radius: 9px;
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  z-index: 0;
}
.note-form-type-seg.is-todo::before { transform: translateX(100%); }
.note-form-type-seg.is-draw::before { transform: translateX(200%); }
.note-form-type-pill {
  background: none;
  border: none;
  color: color-mix(in srgb, var(--fg) 40%, transparent);
  cursor: pointer;
  padding: 0 12px;
  font-size: 13px;
  font-weight: 500;
  font-family: inherit;
  transition: color 0.2s;
  white-space: nowrap;
  height: 100%;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  position: relative;
  z-index: 1;
}
.note-form-type-pill:not(.active):hover { color: color-mix(in srgb, var(--fg) 60%, transparent); }
.note-form-type-pill.active,
.note-form-type-pill.active:hover,
.note-form-type-pill.active:focus {
  color: var(--fg);
  cursor: default;
}
@media (max-width: 480px) {
  .note-form-type-pill span { display: none; }
  .note-form-type-pill { padding: 0 8px; }
}

.note-card-action:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
}
.note-card-delete:hover { color: var(--red); }

/* Tag/label */
.note-card-label {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 2px;
}
.note-card-label-chip {
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent);
  border-radius: 999px;
  padding: 2px 6px;
  font: inherit;
  font-size: 9px;
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
}
.note-card-label-chip:hover {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-color: color-mix(in srgb, var(--accent) 60%, var(--border));
}

/* Search bar */
.notes-search-bar {
  padding: 6px 8px;
  display: flex;
  align-items: center;
  gap: 6px;
}
.notes-select-trigger {
  position: relative;
  top: 3px;
  flex-shrink: 0;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: inherit;
  font-size: 11px;
  height: 30px;
  padding: 0 12px;
  cursor: pointer;
  opacity: 0.75;
  transition: opacity 0.15s, border-color 0.15s, background 0.15s, color 0.15s;
}
.notes-select-trigger:hover { opacity: 1; border-color: color-mix(in srgb, var(--accent) 50%, var(--border)); }
.notes-select-trigger.active {
  opacity: 1;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
}
.notes-search-bar .memory-search-input {
  flex: 1;
  min-width: 0;
  height: 30px;
  min-height: 30px;
  font-size: 11px;
  padding: 0 10px 0 28px;
  background-color: var(--bg);
  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='%23e06c75' stroke-opacity='0.85' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='8'/><line x1='21' y1='21' x2='16.65' y2='16.65'/></svg>");
  background-repeat: no-repeat;
  background-position: 9px center;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  font-family: inherit;
  outline: none;
  transition: border-color 0.15s, background-color 0.15s;
}
.notes-search-bar .memory-search-input:focus {
  border-color: var(--red);
}
/* Bulk-bar appears under the search bar — inset its sides to match the
   search bar's own 8px horizontal padding so the two visually align. */
#notes-bulk-bar.memory-bulk-bar {
  margin: 0 8px 4px;
}

/* Grid view: true CSS Grid plus JS-computed row spans, so cards masonry-pack
   vertically without the jumpy rebalancing of CSS columns. */
.notes-pane.notes-view-grid .notes-pane-body {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  grid-auto-rows: 4px;
  grid-auto-flow: dense;
  gap: 0 8px;
  padding: 8px;
  overflow-x: hidden;
  align-items: start;
  align-content: start;
}
.notes-pane.notes-view-grid .note-card {
  margin: 0 0 8px;
  min-width: 0;
  max-width: 100%;
  overflow-wrap: anywhere;
  word-break: break-word;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.notes-pane.notes-view-grid .note-form,
.notes-pane.notes-view-grid .notes-quick-add {
  grid-column: 1 / -1;
  margin-bottom: 0;
  grid-row-end: span 16;
}
/* Edit form in grid view: span the full row and size naturally — same shape
   as the new-note form. The earlier max-width: 380px + max-height: 220px
   inner-scroll combo read as a tiny popup floating in a wider panel and
   didn't actually fit the form's content. */
.notes-pane.notes-view-grid .note-form {
  width: 100%;
  max-width: none;
  margin-left: 0;
  margin-right: 0;
  box-sizing: border-box;
}
/* Pinned section break: the first unpinned card jumps to column 1, leaving
   any leftover cell in the pinned row empty. Reads as a visual divider
   between pinned and unpinned without needing a separator element. */
.notes-pane.notes-view-grid .note-card-pinned + .note-card:not(.note-card-pinned) {
  grid-column-start: 1;
}
/* Mobile: 2-lane masonry via the CSS-Grid row-span trick. Each card spans
   N tiny rows (4px each) based on its measured height; the grid auto-places
   cards into the two columns so left/right lanes flow independently — no
   row alignment between them. JS in notes.js sets `grid-row-end: span N`
   on every card after render. Pure CSS multi-column was tried but its
   `column-span: all` interaction with sticky positioning broke clicks on
   the quick-add bar (Firefox mobile). */
@media (max-width: 768px) {
  .notes-pane.notes-view-grid .notes-pane-body {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-auto-rows: 4px;
    grid-auto-flow: dense;
    gap: 0 8px;
    align-content: start;
  }
  .notes-pane.notes-view-grid .note-card {
    margin: -8px 0 6px;
    transform: translateY(-12px);
    /* grid-row-end set inline by JS (_applyMasonry) */
  }
  /* Full-width quick-add + edit form. Quick-add stays sticky at top so
     the user can always reach it; it's NOT inside the masonry pack. */
  .notes-pane.notes-view-grid .notes-labels-bar,
  .notes-pane.notes-view-grid .notes-quick-add,
  .notes-pane.notes-view-grid .note-form {
    grid-column: 1 / -1;
  }
  .notes-pane.notes-view-grid .notes-labels-bar { grid-row: span 16; }
  .notes-pane.notes-view-grid .notes-quick-add { grid-row: span 12; }
  .notes-pane.notes-view-grid .note-form {
    width: 100%;
    max-width: none;
    margin-left: 0;
    margin-right: 0;
    box-sizing: border-box;
    grid-row: auto / span 64;
  }
  .notes-pane.notes-view-grid .note-form:has(.note-form-type-seg.is-draw) {
    grid-row: auto / span 152;
  }
  .notes-pane.notes-view-grid .note-form.note-form-new {
    transform: translateY(-28px);
  }
  .notes-pane.notes-view-grid .notes-labels-bar {
    padding-top: 0;
    margin-top: -6px;
    margin-bottom: 0;
  }
  .notes-pane.notes-view-grid .notes-quick-add {
    margin-top: -32px;
    margin-bottom: 4px;
  }
  /* Pinned-section break: first unpinned card jumps to column 1. */
  .notes-pane.notes-view-grid .note-card-pinned + .note-card:not(.note-card-pinned) {
    grid-column-start: 1;
  }
}

/* Label filter bar */
.notes-labels-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 0 8px 0;
  margin-top: -6px;
  margin-bottom: -2px;
}
.notes-label-chip {
  background: transparent;
  border: 1px solid var(--border);
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  font-size: 10px;
  padding: 3px 10px;
  border-radius: 10px;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.notes-label-chip:hover {
  color: var(--fg);
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
}
.notes-label-chip.active {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 60%, transparent);
  font-weight: 600;
}
.notes-label-chip-reminders {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.notes-label-chip-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 14px;
  height: 14px;
  padding: 0 4px;
  border-radius: 7px;
  background: color-mix(in srgb, var(--fg) 12%, transparent);
  color: inherit;
  font-size: 9px;
  font-weight: 600;
  margin-left: 3px;
}
.notes-label-chip-reminders.active .notes-label-chip-count {
  background: color-mix(in srgb, var(--accent) 30%, transparent);
  color: var(--accent);
}
.notes-label-chip-reminders.active.negated {
  background: color-mix(in srgb, var(--red, #e55) 14%, transparent);
  color: var(--red, #e55);
  border-color: color-mix(in srgb, var(--red, #e55) 50%, transparent);
}
.notes-label-chip-reminders.active.negated .notes-label-chip-count {
  background: color-mix(in srgb, var(--red, #e55) 25%, transparent);
  color: var(--red, #e55);
}
.notes-label-clear-past {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--red, #e55);
  border-color: color-mix(in srgb, var(--red, #e55) 45%, var(--border));
}
.notes-label-clear-past:hover {
  color: var(--red, #e55);
  background: color-mix(in srgb, var(--red, #e55) 12%, transparent);
  border-color: color-mix(in srgb, var(--red, #e55) 65%, var(--border));
}
/* Today + Goals filter chips — share styling, tint with a warm accent so
   they read as "long-term work" alongside the cool reminder chip. */
.notes-label-chip-today,
.notes-label-chip-goals {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.notes-label-chip-today.active,
.notes-label-chip-goals.active {
  background: color-mix(in srgb, var(--accent-warm) 22%, transparent);
  color: var(--accent-warm);
  border-color: color-mix(in srgb, var(--accent-warm) 60%, transparent);
}
.notes-label-chip-today.active .notes-label-chip-count,
.notes-label-chip-goals.active .notes-label-chip-count {
  background: color-mix(in srgb, var(--accent-warm) 30%, transparent);
  color: var(--accent-warm);
}

/* ── Goal cards ──────────────────────────────────────────────────────── */
/* A goal note is structurally a checklist (note_type='goal') but visually
   marked with a small Goal pill in the corner + a slightly warmer accent
   so users can spot it at a glance among regular todos. */
.note-card-goal {
  border-left: 3px solid color-mix(in srgb, var(--accent-warm) 80%, transparent);
}
.note-goal-pill {
  position: absolute;
  top: 6px;
  left: 8px;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 7px 2px 5px;
  background: color-mix(in srgb, var(--accent-warm) 18%, transparent);
  color: var(--accent-warm);
  border-radius: 999px;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1;
  z-index: 2;
}
.note-card-goal .note-card-header {
  /* Goal cards reserve space for the pill so the title doesn't collide. */
  padding-left: 56px;
}
/* In select mode the left-side checkbox takes the pill's spot — drop it
   so the row stays legible. */
.note-card-selectmode.note-card-goal .note-goal-pill { display: none; }
.note-card-selectmode.note-card-goal .note-card-header { padding-left: 0; }
/* Description blurb above the checklist on goal cards */
.note-goal-desc {
  font-size: 12px;
  opacity: 0.7;
  margin: 4px 0 6px;
  white-space: pre-wrap;
  line-height: 1.4;
}

/* ── Goal form (Break-down editor) ───────────────────────────────────── */
.note-form-goal {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.note-form-goal-desc {
  width: 100%;
  box-sizing: border-box;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg);
  font-family: inherit;
  font-size: 13px;
  padding: 8px 10px;
  resize: vertical;
  min-height: 60px;
}
.note-form-goal-desc:focus {
  outline: none;
  border-color: color-mix(in srgb, var(--accent-warm) 60%, var(--border));
}
.note-form-goal-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.note-form-goal-ai {
  display: inline-flex;
  align-items: center;
  background: color-mix(in srgb, var(--accent-warm) 16%, transparent);
  color: var(--accent-warm);
  border: 1px solid color-mix(in srgb, var(--accent-warm) 45%, transparent);
  border-radius: 6px;
  padding: 5px 10px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.note-form-goal-ai:hover:not(:disabled) {
  background: color-mix(in srgb, var(--accent-warm) 26%, transparent);
  border-color: color-mix(in srgb, var(--accent-warm) 65%, transparent);
}
.note-form-goal-ai:disabled,
.note-form-goal-ai.busy {
  opacity: 0.6;
  cursor: wait;
}
.note-form-goal-hint {
  font-size: 11px;
  opacity: 0.55;
}
/* Fresh goal form: hide the title input + tag input + reminder button so the
   user only sees the single "what do you want to achieve?" textarea. */
.note-form-goal-fresh ~ .note-form-actions-group { display: none; }
.note-form:has(.note-form-goal-fresh) .note-form-title,
.note-form:has(.note-form-goal-fresh) .note-form-label,
.note-form:has(.note-form-goal-fresh) .note-form-remind-btn,
.note-form-bespoke:has(.note-form-goal-fresh) .note-form-title,
.note-form-bespoke:has(.note-form-goal-fresh) .note-form-label,
.note-form-bespoke:has(.note-form-goal-fresh) .note-form-remind-btn {
  display: none !important;
}
.note-form-goal-fresh .note-form-goal-desc {
  font-size: 15px;
  min-height: 90px;
  padding: 12px 14px;
}
.note-form-goal-fresh.building .note-form-goal-desc {
  opacity: 0.55;
  pointer-events: none;
}
.note-form-goal-fresh.building::after {
  content: 'AI is planning your goal…';
  display: block;
  text-align: center;
  font-size: 12px;
  opacity: 0.7;
  margin-top: 6px;
  animation: note-goal-pulse 1.4s ease-in-out infinite;
}
@keyframes note-goal-pulse {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 1; }
}

/* ── Today view ──────────────────────────────────────────────────────── */
.notes-today-wrap {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 4px 0 12px;
}
.notes-today-header {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-warm);
  padding: 6px 10px 4px;
}
.notes-today-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.notes-today-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--panel, var(--bg));
  border: 1px solid var(--border);
  border-left: 3px solid color-mix(in srgb, var(--accent-warm) 70%, transparent);
  border-radius: 8px;
  transition: opacity 0.2s, transform 0.2s;
}
.notes-today-row.done {
  opacity: 0.35;
  transform: scale(0.98);
}
.notes-today-row .note-check-dot {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--fg) 40%, transparent);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.notes-today-row .note-check-dot:hover {
  border-color: var(--accent-warm);
  background: color-mix(in srgb, var(--accent-warm) 20%, transparent);
}
.notes-today-row.done .note-check-dot {
  background: var(--accent-warm);
  border-color: var(--accent-warm);
}
.notes-today-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.notes-today-title {
  font-size: 11px;
  font-weight: 600;
  color: var(--accent-warm);
  letter-spacing: 0.03em;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.notes-today-title:hover { text-decoration: underline; }
.notes-today-step {
  font-size: 14px;
  color: var(--fg);
  line-height: 1.35;
}
.notes-today-progress {
  flex-shrink: 0;
  font-size: 10px;
  font-weight: 600;
  opacity: 0.6;
  font-variant-numeric: tabular-nums;
}
.notes-empty {
  text-align: center;
  padding: 32px 16px;
  font-size: 13px;
  opacity: 0.55;
  font-style: italic;
}

/* Bulk action bar */
.notes-bulk-info {
  font-size: 10px;
  opacity: 0.6;
  margin-left: 4px;
}
.notes-bulk-btn {
  opacity: 0.6;
}
.notes-bulk-btn:hover { opacity: 1; }

/* Form label input */
.note-form-label {
  background: transparent;
  border: 1px dashed color-mix(in srgb, var(--border) 60%, transparent);
  color: var(--fg);
  font-size: 11px;
  padding: 3px 6px;
  border-radius: 4px;
  outline: none;
  width: 80px;
  font-family: inherit;
  opacity: 0.6;
  transition: opacity 0.15s, border-color 0.15s;
}
.note-form-label:hover, .note-form-label:focus { opacity: 1; border-color: var(--border); }
.note-form-label.flash-once {
  animation: noteLabelFlash 0.6s ease-out;
}
@keyframes noteLabelFlash {
  0%   { background: color-mix(in srgb, var(--accent) 35%, transparent); }
  100% { background: transparent; }
}
.note-form-label:not([value=""]) { opacity: 0.9; border-style: solid; }

.note-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 6px;
}
.note-card-title {
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.note-card-title:hover { opacity: 0.7; }
.note-card-title.empty::before {
  content: 'No title';
  font-weight: 500;
  opacity: 0.3;
  font-style: italic;
}

.note-content-preview {
  font-size: 12px;
  opacity: 0.6;
  line-height: 1.4;
  /* Roomy preview: up to ~14 lines. Content is also sliced to 600 chars
     client-side, so cards still stay reasonable in the list. */
  max-height: 240px;
  overflow: hidden;
  cursor: pointer;
  white-space: pre-wrap;
  word-break: break-word;
}

.note-pin-btn {
  background: none;
  border: none;
  cursor: pointer;
  padding: 2px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
}
.note-pin-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  border: 1.5px solid color-mix(in srgb, var(--fg) 25%, transparent);
  transition: all 0.15s;
}
.note-pin-btn:hover .note-pin-dot { border-color: var(--fg); }
.note-pin-btn.active .note-pin-dot {
  background: var(--accent);
  border-color: var(--accent);
}
.note-x-btn {
  background: none;
  border: none;
  cursor: pointer;
  padding: 2px;
  flex-shrink: 0;
  color: var(--fg);
  opacity: 0.25;
  display: flex;
  align-items: center;
  transition: opacity 0.15s, color 0.15s;
}
.note-x-btn:hover {
  opacity: 0.9;
  color: var(--red);
}
/* Inline due date next to title */
.note-due-inline {
  font-size: 9px;
  padding: 1px 6px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  color: color-mix(in srgb, var(--fg) 60%, transparent);
  opacity: 0.7;
  white-space: nowrap;
  flex-shrink: 0;
  align-self: center;
}
.note-due-inline.note-due-overdue {
  background: color-mix(in srgb, var(--red) 18%, transparent);
  color: var(--red);
  opacity: 1;
  font-weight: 600;
}
/* Note card drag */
.note-card { cursor: grab; }
.note-card:active { cursor: grabbing; }
/* Dragged card = "drop preview" — it already sits at the swap-target slot,
   so making it the most visible card on screen tells the user exactly where
   the note will land on release. */
.note-card.dragging {
  opacity: 0.92;
  cursor: grabbing;
  transform: scale(1.03) rotate(-0.6deg);
  box-shadow: 0 14px 32px rgba(0,0,0,0.4), 0 0 0 2px var(--accent, var(--red));
  border-color: var(--accent, var(--red)) !important;
  background: color-mix(in srgb, var(--accent, var(--red)) 6%, var(--panel)) !important;
  z-index: 10;
  position: relative;
  /* Let elementFromPoint see THROUGH the dragged card so the swap detector
     can find the sibling underneath the finger. Without this, in single-row
     (list view) the dragged card follows the finger and forever occludes
     anything else, so no swap ever fires. The body-level touchmove listener
     still receives events regardless. */
  pointer-events: none;
}
.notes-pane-body.drag-active .note-card:not(.dragging) {
  transition: transform 0.22s cubic-bezier(0.34, 1.2, 0.64, 1), border-color 0.15s, opacity 0.15s;
  opacity: 0.78;
}
.notes-pane-body.drag-active .note-card:not(.dragging):hover {
  border-color: color-mix(in srgb, var(--accent, var(--red)) 50%, var(--border));
  opacity: 1;
}

/* Checklist drag grip */
.note-cl-grip {
  cursor: grab;
  opacity: 0.2;
  font-size: 9px;
  letter-spacing: -2px;
  user-select: none;
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.15s;
}
.note-cl-row:hover .note-cl-grip { opacity: 0.6; }
.note-cl-row.dragging {
  opacity: 0.4;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border-radius: 4px;
}
.note-cl-row.drop-before {
  box-shadow: 0 -2px 0 0 var(--accent);
}
.note-cl-row.drop-after {
  box-shadow: 0 2px 0 0 var(--accent);
}

.note-card-footer {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
}

.note-delete-btn {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.2;
  cursor: pointer;
  padding: 2px;
  transition: opacity 0.15s;
}
.note-delete-btn:hover { opacity: 0.8; color: var(--red); }
.note-archive-btn {
  background: none; border: none; color: var(--fg); opacity: 0.2; cursor: pointer; padding: 2px; transition: opacity 0.15s;
}
.note-archive-btn:hover { opacity: 0.7; }

/* Checklist preview — cap height so cards with 30 items don't push the
   grid layout into oblivion; the inner list scrolls inside the card. */
.note-checklist-preview {
  display: flex;
  flex-direction: column;
  gap: 0;
  min-height: 18px;
  max-height: 240px;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Card has padding-right:30px reserved for the top-corner edit/done/pin
     buttons — but those sit above the checklist, not next to it. Pull the
     checklist back into that reserved gutter so todo text can extend
     further right. The copy button (bottom-right, 28×28) only appears on
     hover; leave just enough room (last row) so it doesn't hide the final
     item's tail. */
  margin-right: -22px;
  padding-right: 4px;
}
.note-checklist-preview > .note-checkbox:last-child .note-check-text {
  /* Reserve room on the LAST row only for the bottom-right copy button so it
     doesn't cover the final item's tail. Applied to the text — not to the
     row — so the X delete button still reaches the same edge it does on
     every other row. */
  padding-right: 30px;
}
.note-card.doclib-card-expanded .note-checklist-preview {
  /* When the card is the expanded one in the grid, let the list use the
     whole available height instead of the compact cap. */
  max-height: none;
}
.note-checklist-preview:empty::before {
  content: 'No todos';
  font-size: 10px;
  opacity: 0.3;
  font-style: italic;
}
.note-cl-quickadd {
  /* In flow now (was position:absolute), rendered AFTER the reminder badge
     so the "+ Add item" input sits underneath the reminder instead of
     overlapping it. Slight right inset keeps clear of the bottom-right
     copy button on hover. */
  display: block;
  padding-top: 2px;
  padding-right: 32px;
  margin-top: 2px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s;
}
.note-card:hover .note-cl-quickadd,
.note-cl-quickadd:focus-within {
  opacity: 1;
  pointer-events: auto;
}
.note-cl-quickadd-input {
  width: 100%;
  background: transparent;
  border: none;
  border-top: 1px dashed color-mix(in srgb, var(--border) 60%, transparent);
  color: var(--fg);
  font-size: 11px;
  font-family: inherit;
  padding: 4px 2px 2px;
  outline: none;
  opacity: 0.6;
  transition: opacity 0.15s, border-color 0.15s;
}
.note-cl-quickadd-input::placeholder { color: var(--fg); opacity: 0.5; }
.note-cl-quickadd-input:focus { opacity: 1; border-top-color: var(--border); }
.note-card-selectmode .note-cl-quickadd { display: none !important; }
.note-checkbox {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  cursor: pointer;
  /* Vertical padding stays 0 so short single-line rows pack tightly.
     Left padding bumped so the dot's hover-scale (1.15x) doesn't clip
     against the card edge. */
  padding: 0 4px 0 8px;
  line-height: 1.25;
  border-radius: 4px;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.note-checkbox:hover {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
}
.note-check-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.note-link {
  color: var(--accent-primary, var(--red));
  text-decoration: underline;
  word-break: break-all;
}
.note-link:hover { opacity: 0.8; }
.note-checkbox-rm {
  flex: 0 0 auto;
  background: transparent;
  border: none;
  color: var(--fg);
  opacity: 0;
  cursor: pointer;
  padding: 2px;
  border-radius: 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-left: auto;
  margin-right: 0;
  transition: opacity 0.12s, background 0.12s, color 0.12s;
}
.note-checkbox:hover .note-checkbox-rm { opacity: 0.55; }
.note-checkbox-rm:hover { opacity: 1 !important; color: var(--red); background: color-mix(in srgb, var(--red) 12%, transparent); }
.note-card-selectmode .note-checkbox-rm { display: none; }
.note-check-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 1.75px solid color-mix(in srgb, var(--fg) 35%, transparent);
  /* Solid panel fill so the dot reads as a tappable target even when an
     image or colored card background sits behind it. */
  background: var(--panel);
  flex-shrink: 0;
  position: relative;
  /* Anchor the hover/active scale to the LEFT edge so the dot grows
     rightward only. Default center origin pushed the scaled dot off the
     left of the card (the parent .note-checklist-preview has overflow:auto
     and clips horizontally as a side-effect of overflow-y:auto). */
  transform-origin: left center;
  transition: background 0.2s, border-color 0.2s, transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.note-check-dot::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 45%;
  width: 7px;
  height: 3.5px;
  border-left: 1.75px solid #fff;
  border-bottom: 1.75px solid #fff;
  transform: translate(-50%, -50%) rotate(-45deg) scale(0);
  transform-origin: center;
  transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.notes-select-mode .note-checkbox { cursor: default; pointer-events: none; }
.notes-select-mode .note-checkbox:hover { background: transparent; }
.notes-select-mode .note-checkbox:hover .note-check-dot { transform: none; border-color: color-mix(in srgb, var(--fg) 35%, transparent); }
.note-checkbox:hover .note-check-dot {
  border-color: var(--accent);
  transform: scale(1.15);
}
.note-checkbox:active .note-check-dot {
  transform: scale(0.9);
}
.note-checkbox.done .note-check-dot {
  background: var(--accent);
  border-color: var(--accent);
  animation: note-check-pop 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.note-checkbox.done .note-check-dot::after {
  transform: translate(-50%, -50%) rotate(-45deg) scale(1);
}
@keyframes note-check-pop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.35); }
  100% { transform: scale(1); }
}
.note-check-text {
  transition: opacity 0.25s;
  line-height: 1.3;
  padding-right: 4px;
  word-break: break-word;
}
.note-checkbox.done .note-check-text {
  position: relative;
  opacity: 0.4;
}
.note-checkbox.done .note-check-text::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: calc(50% - 1px);
  height: 1px;
  background: currentColor;
  animation: note-strike 0.3s ease-out forwards;
  transform-origin: left center;
}
@keyframes note-strike {
  0%   { transform: scaleX(0); }
  100% { transform: scaleX(1); }
}

/* Due date badge */
.note-due-badge {
  font-size: 10px;
  padding: 1px 6px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  color: var(--fg);
  opacity: 0.6;
}
.note-due-overdue {
  background: color-mix(in srgb, var(--red) 20%, transparent);
  color: var(--red);
  opacity: 1;
  font-weight: 600;
}

/* Color classes — theme-aware via color-mix with --panel */
.note-color-red    { background: color-mix(in srgb, var(--red) 18%, var(--panel)); border-color: color-mix(in srgb, var(--red) 30%, var(--border)); }
.note-color-orange { background: color-mix(in srgb, #d19a66 18%, var(--panel)); border-color: color-mix(in srgb, #d19a66 30%, var(--border)); }
.note-color-yellow { background: color-mix(in srgb, var(--hl-string) 18%, var(--panel)); border-color: color-mix(in srgb, var(--hl-string) 30%, var(--border)); }
.note-color-green  { background: color-mix(in srgb, #98c379 18%, var(--panel)); border-color: color-mix(in srgb, #98c379 30%, var(--border)); }
.note-color-blue   { background: color-mix(in srgb, var(--hl-function) 18%, var(--panel)); border-color: color-mix(in srgb, var(--hl-function) 30%, var(--border)); }
.note-color-purple { background: color-mix(in srgb, var(--hl-keyword) 18%, var(--panel)); border-color: color-mix(in srgb, var(--hl-keyword) 30%, var(--border)); }

/* 3D top-edge highlight — an inset 2px stripe inside every note card that
   reads as a lifted "glassy" lip. Each colored note gets a stronger stripe
   in its own hue so the colour reads even before you focus on the card. */
.note-card { box-shadow: inset 0 2px 0 0 color-mix(in srgb, var(--fg) 14%, transparent); }
.note-color-red    { box-shadow: inset 0 2px 0 0 color-mix(in srgb, var(--red) 55%, transparent); }
.note-color-orange { box-shadow: inset 0 2px 0 0 color-mix(in srgb, #d19a66 60%, transparent); }
.note-color-yellow { box-shadow: inset 0 2px 0 0 color-mix(in srgb, var(--hl-string) 60%, transparent); }
.note-color-green  { box-shadow: inset 0 2px 0 0 color-mix(in srgb, #98c379 60%, transparent); }
.note-color-blue   { box-shadow: inset 0 2px 0 0 color-mix(in srgb, var(--hl-function) 60%, transparent); }
.note-color-purple { box-shadow: inset 0 2px 0 0 color-mix(in srgb, var(--hl-keyword) 60%, transparent); }

/* Color picker dots */
.note-color-picker {
  display: flex;
  gap: 5px;
  align-items: center;
}
.note-color-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  cursor: pointer;
  border: 2px solid transparent;
  transition: border-color 0.15s, transform 0.15s;
  flex-shrink: 0;
}
.note-color-dot:hover { transform: scale(1.15); }
.note-color-dot.active {
  border-color: var(--fg);
}

/* Note form */
.note-form {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 10px;
  transition: background 0.2s, border-color 0.2s;
  /* Container so the action buttons can collapse to icons when the note
     card is narrow (see .note-form-collapsible). */
  container-type: inline-size;
  container-name: noteform;
}
@media (max-width: 768px) {
  .notes-pane:not(.notes-view-grid) .note-form.note-form-new {
    transform: translateY(6px);
  }
}
/* Label sits after the icon in each action button */
.note-form-text-btn .nft-label { margin-left: 5px; }
/* Never let the action buttons spill outside the card */
.note-form-actions-group { flex-wrap: wrap; row-gap: 6px; }
/* When the note card is narrow, collapse Archive / Delete / Cancel to
   icon-only (their text label hides; tooltip via title= remains). The
   Save/Update button is NOT collapsible — it always keeps its label and
   stays last. */
@container noteform (max-width: 360px) {
  .note-form-collapsible .nft-label { display: none; }
  .note-form-collapsible { padding-left: 9px; padding-right: 9px; }
}
.note-form.note-color-red    { background: color-mix(in srgb, var(--red) 18%, var(--panel)) !important; border-color: color-mix(in srgb, var(--red) 30%, var(--border)) !important; }
.note-form.note-color-orange { background: color-mix(in srgb, #d19a66 18%, var(--panel)) !important; border-color: color-mix(in srgb, #d19a66 30%, var(--border)) !important; }
.note-form.note-color-yellow { background: color-mix(in srgb, var(--hl-string) 18%, var(--panel)) !important; border-color: color-mix(in srgb, var(--hl-string) 30%, var(--border)) !important; }
.note-form.note-color-green  { background: color-mix(in srgb, #98c379 18%, var(--panel)) !important; border-color: color-mix(in srgb, #98c379 30%, var(--border)) !important; }
.note-form.note-color-blue   { background: color-mix(in srgb, var(--hl-function) 18%, var(--panel)) !important; border-color: color-mix(in srgb, var(--hl-function) 30%, var(--border)) !important; }
.note-form.note-color-purple { background: color-mix(in srgb, var(--hl-keyword) 18%, var(--panel)) !important; border-color: color-mix(in srgb, var(--hl-keyword) 30%, var(--border)) !important; }
.note-form-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-right: 0;
}
.note-form-header .note-form-remind-btn {
  margin-right: -7px; /* align bell center with X-button column on todo rows */
  position: relative;
  top: -2px;
}
.note-form-title {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--border);
  color: var(--fg);
  font-size: 14px;
  font-weight: 600;
  padding: 4px 0;
  outline: none;
}
.note-form-title:focus { border-color: var(--fg); }
.note-form-header { position: relative; }
.note-form-header .note-form-due {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
  border: 0;
  padding: 0;
  margin: 0;
  right: 0;
  bottom: 0;
}
.note-form-due-btn {
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg);
  border-radius: 4px;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0.85;
  transition: opacity 0.15s, border-color 0.15s, background 0.15s;
}
.note-form-due-btn:hover { opacity: 1; border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.note-form-due-btn.has-date {
  opacity: 1;
  border-style: solid;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
}

.note-type-toggle {
  display: flex;
  gap: 4px;
}
.note-type-btn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg);
  font-size: 11px;
  padding: 3px 10px;
  border-radius: 4px;
  cursor: pointer;
  opacity: 0.5;
  transition: opacity 0.15s, background 0.15s;
}
.note-type-btn.active {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
}
.note-type-btn:hover { opacity: 0.8; }

.note-form-content {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg);
  font-size: 12px;
  padding: 8px;
  border-radius: 4px;
  resize: vertical;
  /* Roomier default so editing a note isn't cramped; JS auto-grow takes
     it further as you type. */
  min-height: 120px;
  line-height: 1.5;
  font-family: inherit;
  outline: none;
}
.note-form-content:focus { border-color: var(--fg); }

.note-form-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.note-form-meta .note-form-label {
  width: 112px;
  flex: 0 1 140px;
}
.note-form-meta .note-color-picker { gap: 3px; }
.note-form-actions-group {
  display: flex;
  gap: 6px;
  align-items: center;
  /* Take the full width of the meta row so Archive/Delete can sit on the
     LEFT side and the spacer pushes Cancel + Update to the right edge. */
  flex: 1 1 auto;
  flex-shrink: 0;
  flex-wrap: nowrap;
}
.note-form-actions-spacer { flex: 1 1 auto; }
/* Unified text+icon action button — used for Archive, Delete, Cancel, Update
   so all 4 read as a matched set. Matches the existing .note-form-save
   sizing/padding via the rules at ~25023. */
.note-form-text-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border: 1px solid var(--border);
  color: var(--fg);
  border-radius: 6px;
  padding: 5px 12px;
  font-size: 12px;
  font-family: inherit;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
  white-space: nowrap;
}
.note-form-text-btn:hover {
  background: color-mix(in srgb, var(--fg) 14%, transparent);
  border-color: color-mix(in srgb, var(--fg) 28%, var(--border));
}
.note-form-text-btn.note-form-save {
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 45%, var(--border));
  color: var(--accent, var(--red));
  font-weight: 600;
}
.note-form-text-btn.note-form-save:hover {
  background: color-mix(in srgb, var(--accent, var(--red)) 28%, transparent);
}
.note-form-text-btn.danger:hover {
  background: color-mix(in srgb, var(--red) 14%, transparent);
  border-color: color-mix(in srgb, var(--red) 45%, var(--border));
  color: var(--red);
}
.note-form-icon-btn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg);
  cursor: pointer;
  border-radius: 4px;
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.65;
  transition: opacity 0.15s, border-color 0.15s, background 0.15s, color 0.15s;
}
/* Force the inner icons to render at the inline-attribute size — some other
   stylesheets clamp `svg { width: ... }` globally and that shrinks them. */
.note-form-icon-btn svg { width: 31px !important; height: 31px !important; }
.note-form-icon-btn:hover { opacity: 1; border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.note-form-delete-btn:hover { color: var(--red); border-color: color-mix(in srgb, var(--red) 50%, var(--border)); background: color-mix(in srgb, var(--red) 8%, transparent); }

/* Draw mode — canvas + small toolbar */
.note-form-draw-wrap {
  display: flex;
  flex-direction: column;
  gap: 6px;
  position: relative;
}
/* X overlay on the canvas — appears only when a photo is loaded as the
   background (via _wireCanvas). Clicking it wipes the canvas back to white. */
.note-form-draw-bg-rm {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 3;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.note-form-draw-bg-rm:hover { background: rgba(0, 0, 0, 0.8); }
/* Draw mode hides the note's background-color picker and the standalone
   image preview — they don't make sense alongside a canvas. Uses :has() so
   it kicks in whenever the type-seg flips to .is-draw, in addition to the
   JS that sets display:none. */
.note-form:has(.note-form-type-seg.is-draw) .note-color-picker,
.note-form:has(.note-form-type-seg.is-draw) .note-form-image-wrap {
  display: none !important;
}
.note-form-canvas {
  background: #ffffff;
  border: 1px solid var(--border);
  border-radius: 6px;
  touch-action: none;
  max-width: 100%;
  cursor: crosshair;
}
.note-form-draw-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.note-form-draw-tool {
  display: inline-flex;
  align-items: center;
  height: 26px;
}
/* Color picker rendered as a flat circular swatch — the native chrome around
   <input type=color> would clash with the rest of the toolbar. */
.note-form-draw-color {
  appearance: none;
  -webkit-appearance: none;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--fg) 25%, transparent);
  background: none;
  cursor: pointer;
  padding: 0;
  overflow: hidden;
  box-sizing: border-box;
  flex: 0 0 24px;
}
/* After attachColorPicker swaps the input to type=text + .cp-swatch-input,
   re-pin the swatch to a 24px circle (otherwise it inherits text-input
   sizing and becomes a giant rectangle). */
.note-form-draw-color.cp-swatch-input {
  width: 24px !important;
  height: 24px !important;
  min-width: 24px !important;
  max-width: 24px !important;
  border-radius: 50%;
  padding: 0;
  box-sizing: border-box;
}
.note-form-draw-color::-webkit-color-swatch-wrapper { padding: 0; border-radius: 50%; }
.note-form-draw-color::-webkit-color-swatch { border: none; border-radius: 50%; }
.note-form-draw-color::-moz-color-swatch { border: none; border-radius: 50%; }
.note-form-draw-size-wrap { width: 110px; display: inline-flex; align-items: center; }
.note-form-photo-plus {
  font-size: 16px;
  line-height: 1;
  font-weight: 600;
  opacity: 0.8;
}
/* Mirrors .gallery-editor-container input[type=range] — slim pill track that
   grows on interaction, red circular thumb. */
.note-form-draw-size {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 4px;
  background: color-mix(in srgb, var(--fg) 25%, transparent);
  border-radius: 999px;
  accent-color: var(--red);
  cursor: pointer;
  transition: height 0.15s ease;
}
.note-form-draw-size:hover,
.note-form-draw-size:focus,
.note-form-draw-size:active { height: 10px; }
.note-form-draw-size::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--red);
  border: none;
  cursor: pointer;
  transition: width 0.12s ease, height 0.12s ease;
}
.note-form-draw-size::-moz-range-thumb {
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--red);
  border: none;
  cursor: pointer;
  transition: width 0.12s ease, height 0.12s ease;
}
.note-form-draw-size:hover::-webkit-slider-thumb,
.note-form-draw-size:focus::-webkit-slider-thumb,
.note-form-draw-size:active::-webkit-slider-thumb { width: 18px; height: 18px; }
.note-form-draw-size:hover::-moz-range-thumb,
.note-form-draw-size:focus::-moz-range-thumb,
.note-form-draw-size:active::-moz-range-thumb { width: 18px; height: 18px; }
/* Brush | Eraser segmented toggle — sliding pill, same recipe as Note/Todo. */
.note-form-draw-be {
  display: inline-flex;
  height: 32px;
  /* Accent-colored box around the whole control so it's instantly readable
     as "switch is on", and the sliding pill inside indicates which side. */
  border: 2px solid var(--accent);
  border-radius: 10px;
  overflow: hidden;
  position: relative;
  flex-shrink: 0;
  background: color-mix(in srgb, var(--fg) 5%, transparent);
}
.note-form-draw-be::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 50%;
  height: 100%;
  background: var(--accent);
  border-radius: 9px;
  transition: transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1);
  z-index: 0;
}
.note-form-draw-be.is-eraser::before { transform: translateX(100%); }
.note-form-draw-be-btn {
  background: none;
  border: none;
  color: color-mix(in srgb, var(--fg) 50%, transparent);
  cursor: pointer;
  padding: 0 8px;
  width: 34px;
  height: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  z-index: 1;
  transition: color 0.18s;
}
.note-form-draw-be-btn:hover { color: var(--fg); }
/* Active side picks up the theme accent so brush vs eraser is unmistakable. */
.note-form-draw-be-btn.active { color: var(--accent-primary, var(--red)); }
.note-form-draw-be-btn:focus { outline: none; }

.note-form-draw-text,
.note-form-draw-line,
.note-form-draw-circle,
.note-form-draw-undo {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg);
  cursor: pointer;
  border-radius: 4px;
  height: 32px;
  width: 34px;
  font-family: inherit;
  font-size: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.7;
  position: relative;
  transition: opacity 0.15s, border-color 0.15s, background 0.15s;
}
.note-form-draw-text { font-weight: 700; font-family: Georgia, serif; font-size: 16px; }
/* Size badge in the bottom-right of T — empty until a size is chosen. */
.note-form-draw-text-badge {
  position: absolute;
  right: 3px;
  bottom: 1px;
  font-family: inherit;
  font-weight: 700;
  font-size: 8px;
  line-height: 1;
  letter-spacing: 0.3px;
  /* Bare --accent is undefined here, was rendering invisible — use the
     defined accent so the S/M/L size badge actually shows in theme colour. */
  color: var(--accent-primary, var(--red));
}
.note-form-draw-text-badge:empty { display: none; }
.note-form-draw-shape-badge {
  position: absolute;
  right: 3px;
  bottom: 1px;
  font-family: inherit;
  font-weight: 700;
  font-size: 8px;
  line-height: 1;
  letter-spacing: 0.3px;
  color: var(--accent-primary, var(--red));
}
.note-form-draw-shape-badge:empty { display: none; }
.note-form-draw-text:hover,
.note-form-draw-line:hover,
.note-form-draw-circle:hover,
.note-form-draw-undo:hover {
  opacity: 1;
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.note-form-draw-text.active,
.note-form-draw-line.active,
.note-form-draw-circle.active {
  opacity: 1;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
}
/* Reflect the chosen size in the icon itself, so the user sees at a glance
   which size (S/M/L) is active without needing the tiny badge. */
.note-form-draw-line.size-s svg line,
.note-form-draw-circle.size-s svg circle { stroke-width: 1.2; }
.note-form-draw-line.size-m svg line,
.note-form-draw-circle.size-m svg circle { stroke-width: 3; }
.note-form-draw-line.size-l svg line,
.note-form-draw-circle.size-l svg circle { stroke-width: 5; }
.note-form-draw-text.size-s { font-size: 13px; }
.note-form-draw-text.size-m { font-size: 18px; }
.note-form-draw-text.size-l { font-size: 23px; line-height: 1; }
/* Narrow widths: shrink the tag input to make room for the action group;
   the hashtag-in-content shortcut still works, so the input can be tiny. */
@media (max-width: 600px) {
  .note-form-meta .note-form-label { width: 78px; flex-shrink: 1; min-width: 0; }
}
.note-form-due {
  background: transparent;
  border: 1px dashed color-mix(in srgb, var(--border) 60%, transparent);
  color: var(--fg);
  font-size: 11px;
  padding: 3px 6px;
  border-radius: 4px;
  outline: none;
  opacity: 0.4;
  transition: opacity 0.15s, border-color 0.15s;
}
.note-form-due:hover, .note-form-due:focus { opacity: 0.9; border-color: var(--border); }
.note-form-due:not([value=""]) { opacity: 0.85; border-style: solid; }

.note-form-actions {
  display: flex;
  gap: 6px;
  justify-content: flex-end;
}
.note-form-save,
.note-form-cancel,
.note-form-archive {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--fg);
  font-size: 11px;
  padding: 4px 12px;
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.15s;
}
.note-form-save { background: color-mix(in srgb, var(--fg) 12%, transparent); font-weight: 600; }
.note-form-save:hover { background: color-mix(in srgb, var(--fg) 20%, transparent); }
.note-form-cancel:hover { background: color-mix(in srgb, var(--fg) 8%, transparent); }
.note-form-archive:hover { background: color-mix(in srgb, var(--fg) 8%, transparent); }

/* Checklist inputs in form */
.note-checklist-inputs {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.note-cl-row {
  display: flex;
  align-items: center;
  gap: 6px;
}
.note-cl-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1.5px solid color-mix(in srgb, var(--fg) 30%, transparent);
  flex-shrink: 0;
  cursor: pointer;
  transition: all 0.15s;
}
.note-cl-dot:hover { border-color: var(--fg); transform: scale(1.2); }
.note-cl-row.done .note-cl-dot {
  background: var(--accent);
  border-color: var(--accent);
}
.note-cl-row.done .note-cl-text {
  opacity: 0.4;
  background: linear-gradient(currentColor, currentColor) no-repeat;
  background-size: 0 1px;
  background-position: 0 calc(50% - 1px);
  animation: cl-strike 0.32s ease-out forwards;
  transition: opacity 0.2s ease;
}
/* Draws the strikethrough line left-to-right when the row is marked
   done, instead of snapping it in full-width on the same frame. */
@keyframes cl-strike {
  to { background-size: 100% 1px; }
}
.note-cl-text {
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--border);
  color: var(--fg);
  font-size: 12px;
  padding: 3px 0 3px 3px;
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  outline: none;
}
.note-cl-text:focus { border-color: var(--fg); }
.note-cl-rm {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.3;
  cursor: pointer;
  font-size: 14px;
  padding: 0;
  width: 24px;
  height: 14px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  line-height: 1;
}
.note-cl-rm:hover { opacity: 0.8; color: var(--red); }
.note-cl-add {
  background: none;
  border: none;
  color: var(--fg);
  opacity: 0.4;
  cursor: pointer;
  font-size: 11px;
  padding: 4px 0;
  text-align: left;
}
.note-cl-add:hover { opacity: 0.7; }

/* ── Calendar ── */
.cal-modal-content { width:min(680px, 94vw); max-height:88vh; overflow-x:hidden; }
/* Was overflow-y:auto with grid + day-detail flowing naturally. Now uses a
   true splitter layout: the body is a flex column that doesn't scroll
   itself; the grid takes the remaining space and scrolls if needed; the
   day-detail has an explicit height (driven by the splitter drag via
   --cal-detail-h) and shrinks the grid visually as it grows. */
#cal-body { display:flex; flex-direction:column; gap:8px; overflow:hidden; padding:0; min-height:0; }
#cal-body > .cal-grid { flex: 1 1 auto; min-height: 120px; overflow-y: auto; overflow-x: hidden; }
@media (max-width: 768px) {
  /* Let the calendar pane shrink to nothing on mobile so the day-detail
     splitter can be dragged all the way to the top, hiding the calendar
     entirely. Without min-height:0 the grid (or the week-wrap below)
     refuses to shrink past its built-in floor. */
  #cal-body > .cal-grid,
  #cal-body > .cal-wk-wrap {
    min-height: 0;
    max-height: none;
  }
  /* Let the week grid fill the calendar pane and scroll its hours internally
     instead of overflowing #cal-body (which clips it and feels cramped). */
  #cal-body > .cal-wk-wrap {
    flex: 1 1 auto;
    overflow: auto;
  }
}
.cal-toolbar { display:flex; align-items:center; gap:6px; margin-bottom:8px; flex-wrap:wrap; line-height:1; max-width:100%; row-gap:6px; }

/* Quick-add bar: natural-language event creation. Sits directly under
   the toolbar; focused with `Q`; opens the bespoke form pre-filled. */
.cal-quickadd-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  padding: 1px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: color-mix(in srgb, var(--fg) 3%, var(--bg));
  transition: border-color 0.15s, background 0.15s;
  position: relative;
}
/* Two-tone placeholder hint: "Quick add" in accent, the example in grey, both
   at one low opacity. Overlays the (empty) input; hidden once the user types. */
.cal-quickadd-hint {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 12px;
  color: var(--fg);
  opacity: 0.5;
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: calc(100% - 20px);
}
.cal-quickadd-hint .qa-hint-accent { color: var(--accent, var(--red)); }
/* ↵ enter glyph in the hint — makes it obvious the field submits on Enter. */
.cal-quickadd-hint .qa-hint-enter { vertical-align: -2px; color: var(--accent, var(--red)); opacity: 0.8; }
/* Matching ↵ hint at the right of the event title while it's empty; hidden once
   editing (the ✓ Done button takes over then). */
/* Title placeholder overlay: the prompt text with a ↵ enter glyph right after
   it (accent), so it's clear the field submits on Enter. Shown only while the
   title is empty; the native placeholder is hidden in favour of this. */
.cal-hero-title::placeholder { color: transparent; }
.cal-title-hint {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(calc(-50% - 2px));
  display: none;
  align-items: center;
  gap: 7px;
  font-size: 18px;
  line-height: 1.2;
  white-space: nowrap;
  pointer-events: none;
  color: color-mix(in srgb, var(--fg) 42%, transparent);
}
.cal-hero-title:placeholder-shown ~ .cal-title-hint { display: inline-flex; }
.cal-title-hint .cal-title-enter-ico { color: var(--accent, var(--red)); flex-shrink: 0; }
/* Pure-CSS toggle: hide the hint as soon as the field has real text (no
   per-keystroke JS, so typing never triggers a re-render / focus loss). */
.cal-quickadd-input:not(:placeholder-shown) ~ .cal-quickadd-hint { display: none; }
.cal-quickadd-row:focus-within {
  border-color: color-mix(in srgb, var(--accent, var(--fg)) 55%, var(--border));
  background: color-mix(in srgb, var(--fg) 5%, var(--bg));
}
.cal-quickadd-icon {
  color: color-mix(in srgb, var(--accent, var(--fg)) 80%, transparent);
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  opacity: 0.7;
}
.cal-quickadd-input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  color: var(--fg);
  font-family: inherit;
  font-size: 12px;
  height: 22px;
  padding: 0;
}
.cal-quickadd-input::placeholder { color: transparent; }
.cal-quickadd-status {
  font-size: 10px;
  opacity: 0.55;
  font-variant-numeric: tabular-nums;
}
.cal-toolbar > *, .cal-toolbar-nav > *, .cal-toolbar-right > * { vertical-align:middle; }
.cal-toolbar-nav { display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; }
.cal-toolbar-right { display:inline-flex; align-items:center; gap:6px; margin-left:auto; flex-wrap:wrap; margin-top:6px; }
.cal-title { font-size:13px; font-weight:600; white-space:nowrap; height:24px; line-height:30px; padding:0 6px; display:inline-block; vertical-align:middle; box-sizing:border-box; }
button.cal-nav { background:color-mix(in srgb, var(--fg) 6%, transparent); border:1px solid var(--border); color:var(--fg); border-radius:5px; padding:0 8px; height:24px; line-height:22px; cursor:pointer; font-size:11px; font-family:inherit; box-sizing:border-box; vertical-align:middle; }
button.cal-nav:hover { background:color-mix(in srgb, var(--fg) 12%, transparent); }
button.cal-today-btn { font-size:10px; opacity:0.5; }
button.cal-add-btn { background:var(--accent); color:#fff; border:none; border-radius:50%; width:24px; height:24px; line-height:22px; font-size:18px; cursor:pointer; flex-shrink:0; padding:0; box-sizing:border-box; font-family:inherit; vertical-align:middle; text-align:center; }
button.cal-add-btn.cal-add-btn-text {
  width:auto; min-width:0;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0 10px 0 6px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 24px;
  line-height: 1;
  margin-top: 0;
  transition: background 0.15s, border-color 0.15s;
}
/* Settings "+ Add server" matches the model-dir "+ Add" path button (22px). */
#cookbook-server-add.cal-add-btn-text { height: 21px; border-radius: 11px; position: relative; top: 3px; }
button.cal-add-btn.cal-add-btn-text:hover {
  background: color-mix(in srgb, var(--fg) 14%, transparent);
  border-color: var(--accent);
  opacity: 1;
}
.cal-add-plus {
  font-size: 16px;
  line-height: 1;
  color: var(--accent, var(--red));
  font-weight: 500;
  display: inline-block;
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
  padding-bottom: 2px;
}
button.cal-add-btn.cal-add-btn-text:hover .cal-add-plus {
  transform: rotate(180deg);
}
/* Mobile tap-spin: a gentle quarter-turn nudge before the form opens.
   `.cal-add-plus` has a `padding-bottom: 2px` (and a translateY on the
   small variant) that offsets the glyph for optical centering — those
   shift the rotation pivot off the visual centre. Force the box to
   match the glyph and pin the transform-origin during the spin. */
.cal-add-plus.cal-add-spinning {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  width: 1em;
  height: 1em;
  line-height: 1;
  transform-origin: 50% 50%;
  animation: cal-add-spin 0.28s ease-out forwards;
}
@keyframes cal-add-spin {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(90deg); }
}
.cal-add-label { font-size:11px; font-weight:600; line-height:1; opacity:0.85; }
/* Small variant of the toolbar's +New button. Inherits the .cal-add-btn-text
   styles (pill with "+" + "New" + spring-rotate on the +), so the
   animation is identical. Just shrunk a bit. */
button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm {
  height: 20px;
  border-radius: 10px;
  /* At rest: tight, equal padding around the "+" so it sits centred
     with no trailing gap. On hover the right pad and the flex gap grow
     to make room for the revealed "New". */
  padding: 0 5px;
  gap: 0;
  transition: padding 0.25s ease, gap 0.25s ease, background 0.15s, border-color 0.15s;
}
button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm:hover {
  padding: 0 8px 0 5px;
  gap: 3px;
}
/* Don't touch .cal-add-plus on the small variant — the toolbar +New is
   the reference and we want the same rotation centre / motion. The label
   is shrunk and hidden until hover so the pill is compact at rest. */
button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm .cal-add-label {
  font-size: 10px;
  max-width: 0;
  margin-left: 0;
  opacity: 0;
  overflow: hidden;
  white-space: nowrap;
  transition: max-width 0.25s ease, opacity 0.18s ease, margin-left 0.25s ease;
}
button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm:hover .cal-add-label {
  max-width: 40px;
  margin-left: 3px;
  opacity: 0.85;
}
.cal-add-btn:hover { opacity:0.85; }

/* Mobile: bump the +New pill and the +/- zoom buttons up to a tap-friendly
   size so they're easy to hit with a thumb. */
@media (max-width: 768px) {
  /* Toolbar +New (relocated by JS into the quickadd row on mobile) */
  button.cal-add-btn.cal-add-btn-text {
    height: 36px;
    border-radius: 18px;
    padding: 0 14px 0 12px;
    gap: 6px;
    flex-shrink: 0;
  }
  /* Day-detail +New: just a "+" on mobile — drop the label entirely. */
  button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    padding: 0;
    gap: 0;
    justify-content: center;
  }
  button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm .cal-add-label {
    display: none;
  }
  .cal-add-plus { font-size: 22px; padding-bottom: 1px; }
  .cal-add-label { font-size: 13px; }
  .cal-wk-zoom {
    width: 36px;
    height: 36px;
    font-size: 20px;
    border-radius: 8px;
    opacity: 0.85;
  }
  /* The +New button sits as a sibling of the quickadd row inside a flex
     wrapper, so it's clearly outside the input's bordered box. The row
     itself keeps its own styling. */
  .cal-quickadd-wrap {
    display: flex;
    align-items: stretch;
    gap: 8px;
    margin: 0 0 8px;
  }
  .cal-quickadd-wrap > #cal-quickadd-row {
    flex: 1;
    min-width: 0;
    margin: 0;
  }
  .cal-quickadd-wrap > .cal-add-btn-text {
    align-self: stretch;
    flex-shrink: 0;
  }
  /* Nudge the "+" glyph up one pixel on the round day-detail button so it
     sits visually centred inside the circle. */
  button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm .cal-add-plus {
    transform: translateY(-1px);
  }
  /* Event form: bigger Create / Cancel buttons so they're easy to tap. */
  .cal-form-actions button.cal-btn {
    height: 44px;
    padding: 0 20px;
    font-size: 14px;
    border-radius: 8px;
    min-width: 96px;
  }
  /* Left-align the day/month + tags row in the day-detail panel and
     the calendar/category chip row so they sit flush against the
     left edge on a narrow screen. */
  .cal-detail-header {
    justify-content: flex-start;
    gap: 12px;
  }
  .cal-filters {
    justify-content: flex-start;
  }
}

/* Refresh button spin — driven by JS toggling .cal-syncing on the button.
   transform-box + transform-origin keep the rotation pivoted at the SVG's
   own centre so it spins in place instead of orbiting / drifting up. */
#cal-sync svg,
#email-lib-refresh-btn svg { display: block; transform-box: fill-box; transform-origin: 50% 50%; }
/* Brief checkmark flash after a refresh completes. Lasts ~900ms in JS;
   the small bounce + accent color give a clear "done" cue. */
#cal-sync.cal-sync-done svg,
#email-lib-refresh-btn.email-lib-refresh-done svg {
  color: var(--accent, #2ea043);
  animation: refresh-check-pop 0.32s ease-out;
}
@keyframes refresh-check-pop {
  0%   { transform: scale(0.55); opacity: 0.4; }
  60%  { transform: scale(1.18); opacity: 1; }
  100% { transform: scale(1);    opacity: 1; }
}
#cal-sync.cal-syncing svg,
#email-lib-refresh-btn.email-lib-refreshing svg { animation: spin 0.6s linear infinite; }
.email-undone-toggle.active,
.email-attach-toggle.active {
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 50%, transparent);
  color: var(--accent, var(--red));
  font-weight: 600;
}
/* Hide the gallery-style reader-btn labels on desktop — only the mobile
   @media block re-displays them under each icon. Desktop keeps compact
   icon-only reader buttons. */
.reader-btn-label {
  display: inline-block;
  font-size: 9px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.02em;
  white-space: nowrap;
  opacity: 0.85;
}

/* Inline attachment-filter toggle nested inside the search input. The
   input has padding-right:34px set inline so its text doesn't slide
   under the button. */
.email-search-wrap { display: flex; align-items: center; }
/* Keep the email search input and the adjacent "Select" button identical in
   height regardless of base-vs-mobile overrides — pin both explicitly. */
.email-search-row .memory-search-input {
  height: 32px;
  min-height: 32px;
  box-sizing: border-box;
}
.email-search-row .email-search-select-btn {
  height: 32px;
  min-height: 32px;
  box-sizing: border-box;
  margin-top: 6px;
  flex-shrink: 0;
}
/* Select moved into the dropdown row — dock it to the right and match the
   selects' vertical nudge (.memory-sort-select has top:3px). */
.memory-category-filters .email-filter-select-btn,
.memory-category-filters .email-filter-refresh-btn {
  position: relative;
  top: 3px;
  flex-shrink: 0;
}
.memory-category-filters .email-filter-refresh-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 8px;
}
.memory-category-filters .email-reminders-clear-btn {
  position: relative;
  top: -2px;
  flex-shrink: 0;
}
.email-attach-toggle-inline {
  position: absolute !important;
  right: 4px !important;
  top: 50%;
  transform: translateY(calc(-50% - 3px));
  width: 26px !important;
  height: 26px !important;
  padding: 0 !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  border-radius: 6px !important;
  background: transparent !important;
  border: none !important;
  opacity: 0.55;
}
.email-attach-toggle-inline:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 10%, transparent) !important;
}
.email-attach-toggle-inline.active {
  opacity: 1;
  background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent) !important;
  color: var(--accent, var(--red)) !important;
}
/* Inline undone-toggle nested inside the search input, sits LEFT of the
   attach toggle. Same visual treatment. */
.email-undone-toggle-inline {
  position: absolute !important;
  right: 34px !important;
  top: 50%;
  transform: translateY(calc(-50% - 3px));
  width: 26px !important;
  height: 26px !important;
  padding: 0 !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  border-radius: 6px !important;
  background: transparent !important;
  border: none !important;
  opacity: 0.55;
}
.email-reminder-toggle-inline {
  position: absolute !important;
  right: 64px !important;
  top: 50%;
  transform: translateY(calc(-50% - 3px));
  width: 26px !important;
  height: 26px !important;
  padding: 0 !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  border-radius: 6px !important;
  background: transparent !important;
  border: none !important;
  opacity: 0.55;
}
.email-search-wrap.email-reminder-bell-hidden .memory-search-input {
  padding-right: 66px !important;
}
.email-reminder-toggle-inline:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 10%, transparent) !important;
}
.email-reminder-toggle-inline:hover svg {
  animation: none !important;
  transform: none !important;
}
.email-reminder-toggle-inline.active {
  opacity: 1;
  background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent) !important;
  color: var(--accent, var(--red)) !important;
}
.email-undone-toggle-inline:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 10%, transparent) !important;
}
.email-undone-toggle-inline.active {
  opacity: 1;
  background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent) !important;
  color: var(--accent, var(--red)) !important;
}
/* Round (circular) toggles in the search bar, matching the app's default icon
   buttons. Always fully visible (not dimmed until hover). */
.email-attach-toggle-inline,
.email-undone-toggle-inline,
.email-reminder-toggle-inline { border-radius: 50% !important; opacity: 1 !important; }
.email-attach-toggle:not(.email-attach-toggle-inline):hover svg {
  animation: email-undone-jiggle 0.45s ease-in-out;
  transform-origin: 50% 50%;
}
@keyframes email-undone-jiggle {
  0%   { transform: rotate(0deg); }
  20%  { transform: rotate(-14deg); }
  40%  { transform: rotate(10deg); }
  60%  { transform: rotate(-6deg); }
  80%  { transform: rotate(3deg); }
  100% { transform: rotate(0deg); }
}
.email-undone-toggle:not(.email-undone-toggle-inline):hover svg,
.email-compose-jiggle:hover svg {
  animation: email-undone-jiggle 0.45s ease-in-out;
  transform-origin: 50% 50%;
}
.email-accounts-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 0 6px;
}
.email-accounts-row .doclib-desc { flex-shrink: 0; }
/* Only the direct-child compose button gets pushed right; nested chips
   inside #email-lib-accounts pack to the left as normal flex items. */
.email-accounts-row > .memory-toolbar-btn { flex-shrink: 0; margin-left: auto; }
#email-lib-accounts { justify-content: flex-start; }
.email-accounts-loading-whirlpool {
  width: 14px;
  height: 14px;
  margin: 3px 4px 0 1px;
  display: inline-flex;
  flex: 0 0 auto;
}
.email-accounts-loading-label {
  font-size: 10px;
  opacity: 0.55;
  position: relative;
  top: 2px;
  white-space: nowrap;
}
.email-loading-with-label {
  min-height: 180px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
}
.email-loading-label {
  font-size: 11px;
  opacity: 0.6;
}

/* Refresh button now lives top-right in the modal header next to the close X.
   Borderless (matches the close X), and a fixed square box so the spin and the
   refresh→checkmark icon swap never change its size or nudge neighbours. */
.email-lib-header-actions #email-lib-refresh-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  border: none;
  background: none;
  padding: 0;
  width: 24px;
  height: 24px;
}
/* Bump the icon up on mobile for a bigger visual — but keep the button BOX at
   24px so it never exceeds the header's natural height (the title/close set
   that). A taller box would grow the sticky header and push the list down. */
@media (max-width: 768px) {
  .email-lib-header-actions #email-lib-refresh-btn svg {
    width: 17px;
    height: 17px;
  }
}
.cal-view-toggle {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--border);
  border-radius: 5px;
  overflow: hidden;
  vertical-align: middle;
  box-sizing: border-box;
  padding: 0;
  margin: 0;
  line-height: 1;
}
button.cal-view-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  color: var(--fg);
  font-size: 11px;
  font-family: inherit;
  font-weight: 500;
  padding: 2px 12px 2px 12px;
  margin: 0;
  cursor: pointer;
  opacity: 0.45;
  line-height: 1;
  height: 24px;
  box-sizing: border-box;
}
.cal-view-btn + .cal-view-btn { border-left: 1px solid var(--border); }
.cal-view-btn:hover { opacity: 0.75; }
.cal-view-btn.active {
  background: color-mix(in srgb, var(--fg) 12%, transparent);
  opacity: 1;
  font-weight: 600;
}
/* Toolbar holds only the toggle button; the chip row renders below the
   toolbar on its own line and wraps freely. */
.cal-filters { display:flex; gap:6px; margin:6px 0 8px; flex-wrap:wrap; }
/* Round the native color-input swatch in calendar settings. The outer
   <input> wraps a colored fill drawn by the browser; we need to round both
   the wrapper and the fill so it actually appears circular. */
.cal-s-color { border-radius: 50%; overflow: hidden; }
.cal-s-color::-webkit-color-swatch-wrapper { padding: 0; border-radius: 50%; }
.cal-s-color::-webkit-color-swatch { border: none; border-radius: 50%; }
.cal-s-color::-moz-color-swatch { border: none; border-radius: 50%; }
.cal-filter-item { display:inline-flex; align-items:center; gap:4px; cursor:pointer; font-size:10px; padding:1px 8px; line-height:1.4; border-radius:10px; background:color-mix(in srgb, var(--fg) 5%, transparent); border:1px solid var(--border); transition:opacity 0.15s; }
.cal-filter-item:hover { background:color-mix(in srgb, var(--fg) 10%, transparent); }
.cal-filter-item.cal-filter-off { opacity:0.25; text-decoration:line-through; }
.cal-filter-item.cal-filter-important { font-weight:700; color:#e5a33a; border-color:color-mix(in srgb, #e5a33a 40%, transparent); }
.cal-filter-item.cal-filter-important.cal-filter-active { background:color-mix(in srgb, #e5a33a 18%, transparent); border-color:#e5a33a; }
/* Match the toolbar's other buttons (cal-nav, cal-view-btn, cal-add-btn-text):
   24px tall, 11px font, 5px corners. */
.cal-filter-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border: 1px solid var(--border);
  color: var(--fg);
  cursor: pointer;
  font-size: 11px;
  font-family: inherit;
  font-weight: 500;
  padding: 0 10px;
  height: 24px;
  line-height: 1;
  border-radius: 5px;
  box-sizing: border-box;
  vertical-align: middle;
  opacity: 0.65;
  position: relative;
  top: -3px;
}
.cal-filter-toggle:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 12%, transparent); }
.cal-filter-dot { width:7px; height:7px; border-radius:50%; flex-shrink:0; }
/* The grid is now a flex column: one row of weekday headers + six
   .cal-week-row rows. Each row holds the 7 day cells in its own
   internal grid plus the absolute-positioned multi-day overlays.
   --bars on a week-row is the number of multi-day events overlapping
   it; cells in that row use it to reserve top padding so the bar
   overlays don't cover the day numbers or single-event rows. */
.cal-grid { display:flex; flex-direction:column; gap:1px; background:color-mix(in srgb, var(--fg) 8%, transparent); border-radius:6px; overflow:hidden; }
.cal-week-headers { display:grid; grid-template-columns:repeat(7,1fr); gap:1px; }
.cal-week-row { position:relative; display:grid; grid-template-columns:repeat(7,1fr); gap:1px; }
.cal-week-row > .cal-day { padding-top: calc(4px + var(--bars, 0) * 12px); }
@keyframes cal-slide-from-right {
  from { opacity:0; transform:translateX(20px); }
  to { opacity:1; transform:translateX(0); }
}
@keyframes cal-slide-from-left {
  from { opacity:0; transform:translateX(-20px); }
  to { opacity:1; transform:translateX(0); }
}
.cal-slide-in-right { animation: cal-slide-from-right 0.22s cubic-bezier(0.25, 0.8, 0.25, 1); }
.cal-slide-in-left { animation: cal-slide-from-left 0.22s cubic-bezier(0.25, 0.8, 0.25, 1); }
.cal-weekday { background:color-mix(in srgb, var(--fg) 5%, var(--bg)); text-align:center; font-size:10px; font-weight:600; opacity:0.4; padding:5px 0; }
.cal-day { background:var(--bg); min-height:78px; padding:3px; cursor:pointer; position:relative; transition:background 0.12s; overflow:hidden; }
.cal-day:hover { background:color-mix(in srgb, var(--fg) 5%, var(--bg)); }
.cal-day.cal-today { box-shadow:inset 0 0 0 2px var(--accent, var(--red)); background:color-mix(in srgb, var(--accent, var(--red)) 15%, var(--bg)); border-radius:8px; }
.cal-day.cal-today .cal-day-num { color:var(--bg); font-weight:800; background:var(--accent, var(--red)); border-radius:10px; padding:1px 6px; display:inline-block; opacity:1; line-height:1.3; }
/* Selected day — same square geometry as `.cal-today` so the two read as
   peers, but rendered as an outline + softer fill instead of a solid
   accent block. Today wins when both classes are on the same cell. */
.cal-day.cal-selected:not(.cal-today) {
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent, var(--fg)) 65%, transparent);
  background: color-mix(in srgb, var(--accent, var(--fg)) 12%, var(--bg));
  border-radius: 8px;
}
.cal-day.cal-selected:not(.cal-today) .cal-day-num {
  color: var(--accent, var(--fg));
  opacity: 1;
  font-weight: 700;
}
.cal-day.cal-other { opacity:0.25; }
.cal-day.cal-drag-over { background:color-mix(in srgb, var(--accent) 18%, var(--bg)); }
.cal-day.cal-range-select { background:color-mix(in srgb, var(--accent, var(--red)) 25%, var(--bg)); box-shadow:inset 0 0 0 1px var(--accent, var(--red)); }
.cal-day-num { font-size:10px; font-weight:600; display:block; margin-bottom:1px; opacity:0.7; line-height:1.2; }
.cal-dots { display:flex; flex-direction:row; align-items:center; gap:2px; flex-wrap:nowrap; margin-bottom:1px; height:6px; }
.cal-dot { width:5px; height:5px; border-radius:50%; display:inline-block; cursor:grab; flex-shrink:0; }
.cal-dot-more { font-size:7px; opacity:0.4; }
.cal-event-row { display:flex; align-items:center; gap:3px; padding:1px 2px; border-radius:2px; cursor:grab; line-height:1.15; margin-bottom:1px; }
.cal-event-row:hover { background:color-mix(in srgb, var(--fg) 8%, transparent); }
.cal-event-row-dot { width:4px; height:4px; border-radius:50%; flex-shrink:0; }
.cal-event-row-time { font-size:9px; opacity:0.5; font-variant-numeric:tabular-nums; flex-shrink:0; }
.cal-event-row-name { font-size:9px; opacity:0.8; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; flex:1; min-width:0; }
.cal-event-more { font-size:8px; opacity:0.4; padding-left:6px; }
/* Multi-day bar — positioned as an absolute overlay inside its
   .cal-week-row so it spans uninterrupted across every column it
   covers. --col is the starting column (0–6), --span is the number
   of days it covers within the row, --slot stacks parallel bars. */
.cal-multiday {
  position: absolute;
  left: calc(var(--col, 0) * (100% / 7));
  width: calc(var(--span, 1) * (100% / 7));
  top: calc(2px + var(--slot, 0) * 12px);
  height: 11px;
  font-size: 8px;
  line-height: 11px;
  padding: 0 4px;
  border-radius: 3px;
  color: var(--cal-event-fg, #fff);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: grab;
  opacity: 0.92;
  z-index: 2;
  pointer-events: auto;
}
.cal-dragging { opacity:0.3; }
/* Splitter between the month/week grid and the day-tasks panel — drag
   vertically to give the day panel more room. Height clamped by the
   `--cal-detail-h` variable set on #cal-body by the splitter drag JS. */
.cal-splitter {
  height: 8px;
  margin: 6px -10px 0;
  cursor: row-resize;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  user-select: none;
  touch-action: none;
}
/* Mobile: fat hitbox so the splitter is easy to grab with a thumb,
   without changing how the grip itself looks. */
@media (max-width: 768px) {
  .cal-splitter {
    height: 28px;
    margin-top: 0;
    margin-bottom: 0;
  }
  .cal-splitter-grip {
    width: 56px;
    height: 4px;
  }
}
.cal-splitter-grip {
  width: 42px;
  height: 3px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--fg) 25%, transparent);
  transition: background 0.12s;
}
.cal-splitter:hover .cal-splitter-grip,
.cal-splitter-dragging .cal-splitter-grip {
  background: var(--accent, var(--red));
}
.cal-day-detail {
  margin-top: 4px;
  border-top: 1px solid var(--border);
  padding-top: 8px;
  height: var(--cal-detail-h, 240px);
  overflow-y: auto;
  overscroll-behavior: contain;
  flex-shrink: 0;
}
.cal-detail-header { display:flex; justify-content:space-between; align-items:center; font-size:12px; font-weight:600; margin-bottom:6px; padding-right:8px; }
.cal-empty { font-size:11px; opacity:0.3; padding:4px 0; }
.cal-event-item { display:flex; gap:8px; padding:6px 8px; border-radius:5px; cursor:pointer; align-items:flex-start; }
.cal-event-item:hover { background:color-mix(in srgb, var(--fg) 6%, transparent); }
.cal-event-dot { width:7px; height:7px; border-radius:50%; flex-shrink:0; margin-top:4px; }
.cal-event-info { flex:1; min-width:0; }
.cal-event-name { font-size:12px; font-weight:500; }
.cal-event-tag {
  display: inline-block;
  font-size: 10px;
  font-weight: 500;
  padding: 1px 6px;
  border-radius: 8px;
  border: 1px solid;
  margin-left: 6px;
  vertical-align: 1px;
  opacity: 0.85;
  white-space: nowrap;
}
.cal-event-time { font-size:10px; opacity:0.4; }
.cal-event-loc { font-size:10px; opacity:0.3; }
.cal-event-loc a, .cal-event-time a { color:inherit; text-decoration:underline; text-decoration-color:color-mix(in srgb, currentColor 40%, transparent); }
.cal-event-loc a:hover, .cal-event-time a:hover { opacity:1; text-decoration-color:currentColor; }
/* ── Week view: hour grid ──────────────────────────────────────────
   Vertical hour rail on the left, 7 day columns on the right with their
   own all-day strip and an absolute-positioned hour grid below it. */
.cal-wk-wrap {
  display: flex;
  flex-direction: row;
  gap: 0;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  /* The wrap itself scrolls so the hour rail and day columns move
     together; column headers and the rail spacer are sticky-pinned. */
  overflow: auto;
  max-height: calc(100vh - 220px);
  min-height: 360px;
  position: relative;
}
.cal-wk-rail {
  flex: 0 0 56px;
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  background: color-mix(in srgb, var(--fg) 2%, var(--bg));
  position: sticky;
  left: 0;
  z-index: 5;
}
.cal-wk-rail-spacer {
  /* Lines up the rail's first hour cell with the column's grid origin
     (column header + all-day strip). 32 + 24 = 56 px. Doubles as the
     zoom-control corner since the toolbar is crowded. */
  height: 56px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  position: sticky;
  top: 0;
  background: color-mix(in srgb, var(--fg) 2%, var(--bg));
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 0 4px;
}
.cal-wk-zoom {
  width: 22px;
  height: 22px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  border-radius: 5px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  opacity: 0.55;
  transition: opacity 0.15s, background 0.15s, border-color 0.15s;
}
.cal-wk-zoom:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--fg) 8%, var(--bg));
  border-color: color-mix(in srgb, var(--accent, var(--fg)) 50%, var(--border));
}
.cal-wk-rail-cell {
  font-size: 11px;
  color: color-mix(in srgb, var(--fg) 55%, transparent);
  padding: 4px 8px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--fg) 5%, transparent);
  flex-shrink: 0;
  display: flex;
  align-items: flex-start;
  justify-content: flex-end;
  font-variant-numeric: tabular-nums;
}
.cal-wk-cols {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  position: relative;
}
.cal-wk-col {
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.cal-wk-col:first-child { border-left: none; }
.cal-wk-col-head {
  height: 32px;
  padding: 0 6px;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  flex-shrink: 0;
  background: color-mix(in srgb, var(--fg) 3%, var(--bg));
  border-bottom: 1px solid color-mix(in srgb, var(--fg) 5%, transparent);
  position: sticky;
  top: 0;
  z-index: 4;
}
.cal-wk-col.cal-wk-today .cal-wk-col-head { background: color-mix(in srgb, var(--accent, var(--red)) 12%, var(--bg)); }
.cal-wk-col.cal-wk-today .cal-wk-dn,
.cal-wk-col.cal-wk-today .cal-wk-dt { color: var(--accent, var(--red)); font-weight: 700; }
/* Sunday: a softer rest-day tint so the week edge reads visually, even
   when today is mid-week. Falls back gracefully under the .cal-wk-today
   rules above (today on a Sunday still gets the accent treatment). */
.cal-wk-col.cal-wk-sun .cal-wk-col-head { background: color-mix(in srgb, var(--fg) 7%, var(--bg)); }
.cal-wk-col.cal-wk-sun .cal-wk-grid { background: color-mix(in srgb, var(--fg) 2.5%, var(--bg)); }
.cal-wk-col.cal-wk-sun .cal-wk-allday { background: color-mix(in srgb, var(--fg) 3%, var(--bg)); }
.cal-wk-col.cal-wk-sun .cal-wk-dn,
.cal-wk-col.cal-wk-sun .cal-wk-dt { color: color-mix(in srgb, var(--fg) 60%, transparent); }
/* Today on a Sunday still wins the accent treatment. */
.cal-wk-col.cal-wk-today.cal-wk-sun .cal-wk-col-head { background: color-mix(in srgb, var(--accent, var(--fg)) 12%, var(--bg)); }
.cal-wk-col.cal-wk-today.cal-wk-sun .cal-wk-dn,
.cal-wk-col.cal-wk-today.cal-wk-sun .cal-wk-dt { color: var(--accent, var(--fg)); }
.cal-wk-dn { font-weight: 600; opacity: 0.6; }
.cal-wk-dt { font-variant-numeric: tabular-nums; opacity: 0.5; }
.cal-wk-allday {
  height: 24px;
  padding: 2px 4px;
  display: flex;
  flex-direction: row;
  gap: 2px;
  align-items: center;
  background: color-mix(in srgb, var(--fg) 1.5%, var(--bg));
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  position: sticky;
  top: 32px;
  z-index: 3;
}
.cal-wk-allday::-webkit-scrollbar { display: none; }
.cal-wk-allday-event {
  font-size: 10px;
  font-weight: 500;
  padding: 2px 5px;
  border-radius: 3px;
  color: var(--cal-event-fg, var(--fg));
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Hour grid body */
.cal-wk-grid {
  position: relative;
  flex: 0 0 auto;
  cursor: cell;
  user-select: none;
}
.cal-wk-cell {
  border-bottom: 1px solid color-mix(in srgb, var(--fg) 6%, transparent);
  box-sizing: border-box;
}
.cal-wk-cell:hover { background: color-mix(in srgb, var(--fg) 3%, transparent); }
/* "Now" line */
.cal-wk-now {
  position: absolute;
  left: 0; right: 0;
  height: 0;
  border-top: 2px solid color-mix(in srgb, var(--accent, var(--red, #f87171)) 90%, transparent);
  pointer-events: none;
  z-index: 4;
}
.cal-wk-now::before {
  content: '';
  position: absolute;
  left: -3px;
  top: -4px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent, var(--red, #f87171));
}
/* Event blocks */
.cal-wk-block {
  position: absolute;
  left: 2px;
  right: 2px;
  border-left: 3px solid var(--fg);
  border-radius: 4px;
  padding: 4px 7px;
  font-size: 11px;
  line-height: 1.25;
  cursor: pointer;
  overflow: hidden;
  z-index: 2;
  box-shadow: 0 1px 2px color-mix(in srgb, var(--fg) 8%, transparent);
  transition: filter 0.12s, transform 0.12s;
  box-sizing: border-box;
}
.cal-wk-block:hover { filter: brightness(1.05); transform: translateY(-0.5px); }
.cal-wk-block-name { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 11px; }
.cal-wk-block-time { font-size: 10px; opacity: 0.75; font-variant-numeric: tabular-nums; margin-top: 1px; }
@media (max-width: 768px) {
  /* Mobile week view: the hour rail already shows the time, so drop it from the
     card and let the title wrap to fill the freed space (more readable text). */
  .cal-wk-block-time { display: none; }
  .cal-wk-block-name {
    white-space: normal;
    line-height: 1.15;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 4;
    -webkit-box-orient: vertical;
  }
}
/* Resize handle: a 6 px hit zone along the block's bottom edge. The
   visible 2-px stripe only shows on hover so blocks at rest stay clean. */
.cal-wk-block-resize {
  position: absolute;
  left: 4px;
  right: 4px;
  bottom: 0;
  height: 6px;
  cursor: ns-resize;
  z-index: 3;
}
.cal-wk-block:hover .cal-wk-block-resize::after {
  content: '';
  position: absolute;
  left: 30%;
  right: 30%;
  bottom: 1px;
  height: 2px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--fg) 35%, transparent);
}
.cal-wk-block-resize:hover::after {
  background: color-mix(in srgb, var(--fg) 65%, transparent);
}
/* Body of a block while it's being repositioned: lifted shadow + above
   siblings so the drop target reads clearly. */
.cal-wk-block-ghost {
  z-index: 6 !important;
  box-shadow: 0 4px 12px color-mix(in srgb, var(--fg) 24%, transparent) !important;
  cursor: grabbing;
}
.cal-wk-block { cursor: grab; }
.cal-wk-block:active { cursor: grabbing; }
/* Drag-to-create ghost */
.cal-wk-ghost {
  position: absolute;
  left: 2px;
  right: 2px;
  background: color-mix(in srgb, var(--accent, var(--fg)) 28%, transparent);
  border: 1px dashed color-mix(in srgb, var(--accent, var(--fg)) 70%, transparent);
  border-radius: 4px;
  pointer-events: none;
  z-index: 3;
  font-size: 10px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent, var(--fg));
  font-variant-numeric: tabular-nums;
}
.cal-year { display:grid; grid-template-columns:repeat(4, 1fr); gap:10px; }
.cal-year-month { background:color-mix(in srgb, var(--fg) 3%, var(--bg)); border:1px solid var(--border); border-radius:6px; padding:6px; cursor:pointer; transition:background 0.15s, border-color 0.15s, transform 0.15s; }
.cal-year-month:hover { background:color-mix(in srgb, var(--fg) 8%, var(--bg)); border-color:var(--accent); transform:translateY(-1px); }
.cal-year-month-title { font-size:11px; font-weight:600; text-align:center; margin-bottom:4px; opacity:0.7; }
.cal-year-month:hover .cal-year-month-title { opacity:1; color:var(--accent); }
.cal-year-grid { display:grid; grid-template-columns:repeat(7, 1fr); gap:1px; }
.cal-year-wd { font-size:7px; text-align:center; opacity:0.3; padding:1px 0; }
.cal-year-cell { font-size:8px; text-align:center; padding:2px 0; min-height:14px; border-radius:2px; transition:transform 0.12s, background 0.12s; }
.cal-year-day { cursor:pointer; opacity:0.5; }
.cal-year-day:hover { background:var(--accent, var(--red)); color:#fff; opacity:1; transform:scale(1.15); font-weight:700; z-index:2; position:relative; }
.cal-year-today { color:var(--accent, var(--red)); font-weight:700; opacity:1; }
.cal-year-has { opacity:1; background:color-mix(in srgb, var(--accent) 15%, transparent); }
.cal-year-has.cal-year-today { background:color-mix(in srgb, var(--accent, var(--red)) 25%, transparent); }
.cal-loading { display:flex; justify-content:center; padding:40px 0; }
.cal-badge { background:var(--accent); border-radius:50%; width:6px; height:6px; margin-left:auto; display:inline-block; flex-shrink:0; }
/* Search input — in-panel variant lives inside the day-detail panel and
   spans its width; no width animation since growing/shrinking on every
   keystroke would jitter beside the events list. */
.cal-search-input { background:color-mix(in srgb, var(--fg) 5%, var(--bg)); border:1px solid var(--border); border-radius:5px; padding:0 8px; height:24px; color:var(--fg); font-size:11px; font-family:inherit; width:120px; box-sizing:border-box; transition:width 0.15s; }
.cal-search-input:focus { outline:none; border-color:var(--accent); width:180px; }
.cal-search-input.cal-day-search { width:100%; margin-bottom:0; transition:none; padding-left: 28px; }
.cal-search-input.cal-day-search:focus { width:100%; }
/* Wrap for the search input so the magnifying-glass icon can be absolute-
   positioned inside the field. */
.cal-search-wrap {
  position: relative;
  display: block;
  margin-bottom: 6px;
}
.cal-search-wrap .cal-search-icon {
  position: absolute;
  left: 8px;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  opacity: 0.45;
  color: var(--fg);
}
.cal-day-search-meta { font-size:10px; opacity:0.5; margin:0 2px 6px; }
.cal-search-results { display:flex; flex-direction:column; gap:4px; }
.cal-search-count { font-size:10px; opacity:0.5; padding:4px 2px 8px; }
/* Agenda view */
.cal-agenda { display:flex; flex-direction:column; gap:10px; flex:1 1 auto; min-height:0; overflow-y:auto; overscroll-behavior:contain; }
.cal-agenda-day { display:flex; flex-direction:column; gap:2px; }
.cal-agenda-date { font-size:11px; font-weight:600; opacity:0.6; padding:4px 2px 2px; border-bottom:1px solid var(--border); }
.cal-agenda-day.is-today .cal-agenda-date { opacity: 1; color: var(--accent, var(--red)); border-bottom-color: color-mix(in srgb, var(--accent, var(--red)) 35%, var(--border)); }
.cal-agenda-today-badge {
  display: inline-block;
  margin-left: 6px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
  color: var(--accent, var(--red));
  vertical-align: 1px;
}
.cal-agenda-empty { font-size:11px; opacity:0.4; padding:8px; font-style:italic; }
.cal-agenda-event { display:flex; gap:8px; padding:8px; border-radius:5px; cursor:pointer; align-items:center; position:relative; }
.cal-agenda-event:hover { background:color-mix(in srgb, var(--fg) 6%, transparent); }
.cal-agenda-event .cal-event-more,
.cal-event-item .cal-event-more { opacity:0; transition:opacity 0.15s; }
.cal-agenda-event:hover .cal-event-more,
.cal-event-item:hover .cal-event-more { opacity:0.6; }
.cal-event-item { position:relative; }

/* ── Personal Assistant ── */
.sidebar-assistant-entry { gap: 6px; min-height: 29px; box-sizing: border-box; align-items: center; }
.sidebar-assistant-entry .sidebar-action-icon { position: relative; left: -2px; }
#sidebar-assistant-gear:hover { opacity: 0.8 !important; }
.assistant-settings-form { display: flex; flex-direction: column; gap: 12px; padding: 4px 2px 2px; }
.assistant-field { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: color-mix(in srgb, var(--fg) 70%, transparent); }
.assistant-field > span { font-size: 11px; opacity: 0.6; }
.assistant-field input[type="text"],
.assistant-field input[type="time"],
.assistant-field select,
.assistant-field textarea {
  background: color-mix(in srgb, var(--fg) 5%, var(--bg));
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 7px 10px;
  color: var(--fg);
  font-size: 12px;
  font-family: inherit;
  box-sizing: border-box;
}
.assistant-field textarea { resize: vertical; min-height: 60px; }
.assistant-field input:focus,
.assistant-field select:focus,
.assistant-field textarea:focus { outline: none; border-color: var(--accent, var(--red)); }
.assistant-field-row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.assistant-field-row > .assistant-field { flex: 1 1 180px; }
.assistant-field-check { flex-direction: row !important; align-items: center; gap: 6px; padding-bottom: 7px; }
.assistant-checkins { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; padding-top: 8px; border-top: 1px solid var(--border); }
.assistant-checkins h5 { margin: 0 0 2px; font-size: 12px; font-weight: 600; opacity: 0.8; }
.assistant-checkin-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
}
.assistant-checkin-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.assistant-checkin-head input[type="text"],
.assistant-checkin-head input[type="time"] {
  background: color-mix(in srgb, var(--fg) 5%, var(--bg));
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 4px 8px;
  color: var(--fg);
  font-size: 12px;
  font-family: inherit;
}
.assistant-checkin-head input[type="text"] { flex: 1; min-width: 120px; }
.assistant-checkin-head input[type="time"] { width: 90px; }
.assistant-checkin-run {
  background: none;
  border: 1px solid var(--border);
  color: var(--fg);
  font-family: inherit;
  font-size: 11px;
  padding: 4px 8px;
  border-radius: 4px;
  cursor: pointer;
  opacity: 0.7;
  transition: opacity 0.15s;
}
.assistant-checkin-run:hover { opacity: 1; }
.assistant-checkin-prompt {
  background: color-mix(in srgb, var(--fg) 5%, var(--bg));
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 6px 8px;
  color: var(--fg);
  font-size: 12px;
  font-family: inherit;
  resize: vertical;
  min-height: 48px;
}
.assistant-checkin-prompt:focus { outline: none; border-color: var(--accent, var(--red)); }
.assistant-checkin-meta { font-size: 10px; opacity: 0.45; }
.assistant-settings-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}
.assistant-tools-grid {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 240px;
  overflow-y: auto;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-top: 4px;
}
.assistant-tool-group {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: center;
}
.assistant-tool-group-label {
  font-size: 10px;
  font-weight: 600;
  opacity: 0.5;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  width: 100%;
  margin-bottom: 1px;
}
.assistant-tool-item {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 11px;
  padding: 2px 6px;
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.1s, border-color 0.1s;
  user-select: none;
}
.assistant-tool-item:hover {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
}
.assistant-tool-item:has(input:checked) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.assistant-tool-item input[type="checkbox"] {
  width: 12px;
  height: 12px;
  margin: 0;
}

button.cal-event-more {
  background:transparent;
  border:none;
  color:var(--fg);
  width:20px; height:20px;
  cursor:pointer;
  padding:0;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  font-family:inherit;
  flex-shrink:0;
  margin-left:auto;
  align-self:center;
  transform:translateY(-4px);
  transition:opacity 0.15s;
}
button.cal-event-more:hover { opacity:1 !important; }
/* Empty state */
.cal-empty-state { display:flex; flex-direction:column; align-items:center; justify-content:center; padding:60px 20px; text-align:center; gap:10px; }
.cal-empty-title { font-size:14px; font-weight:600; opacity:0.8; }
.cal-empty-msg { font-size:12px; opacity:0.5; max-width:320px; line-height:1.5; margin-bottom:8px; }
.cal-allday-switch { margin:0; flex-shrink:0; }
/* Stack the "All day" label above its toggle so it doesn't get squeezed /
   truncated next to the date inputs in the row. */
.cal-allday-ctrl { display:flex; flex-direction:column; align-items:center; gap:3px; flex-shrink:0; }
.cal-allday-label { font-size:10px; opacity:0.5; white-space:nowrap; line-height:1; }
.cal-form { display:flex; flex-direction:column; gap:8px; }
.cal-form-title { font-size:13px; font-weight:600; margin-bottom:2px; }
.cal-title-wrap { position: relative; }
/* Calendar-picker select sits 2px higher; its border-left/background tint is
   set in JS to the chosen calendar's colour. */
.cal-f-cal-select { position: relative; top: -4px; }
/* Day-detail "+ New" button nudged down 2px. */
#cal-add-day { position: relative; top: 2px; }
/* Accent the native date/time picker glyphs. We recolor the webkit indicator
   by masking an accent fill through a calendar/clock SVG (same technique as the
   mono icon picker). accent-color covers the spinner parts where supported. */
.cal-form-bespoke input[type="date"],
.cal-form-bespoke input[type="time"],
.cal-form-bespoke input[type="datetime-local"] { accent-color: var(--accent, var(--red)); }
.cal-form-bespoke input[type="date"]::-webkit-calendar-picker-indicator,
.cal-form-bespoke input[type="time"]::-webkit-calendar-picker-indicator,
.cal-form-bespoke input[type="datetime-local"]::-webkit-calendar-picker-indicator {
  background-color: var(--accent, var(--red));
  background-image: none;
  cursor: pointer;
  width: 14px; height: 14px;
}
.cal-form-bespoke input[type="date"]::-webkit-calendar-picker-indicator,
.cal-form-bespoke input[type="datetime-local"]::-webkit-calendar-picker-indicator {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2'/%3E%3Cline x1='16' y1='2' x2='16' y2='6'/%3E%3Cline x1='8' y1='2' x2='8' y2='6'/%3E%3Cline x1='3' y1='10' x2='21' y2='10'/%3E%3C/svg%3E") center/contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2'/%3E%3Cline x1='16' y1='2' x2='16' y2='6'/%3E%3Cline x1='8' y1='2' x2='8' y2='6'/%3E%3Cline x1='3' y1='10' x2='21' y2='10'/%3E%3C/svg%3E") center/contain no-repeat;
}
.cal-form-bespoke input[type="time"]::-webkit-calendar-picker-indicator {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpolyline points='12 6 12 12 16 14'/%3E%3C/svg%3E") center/contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpolyline points='12 6 12 12 16 14'/%3E%3C/svg%3E") center/contain no-repeat;
}

/* ── Bespoke event form ────────────────────────────────────────────
   Big clock-face date/time hero with the title under it. Details panel
   stays collapsed until the title gets focus or "Add details" is clicked.
*/
.cal-form-bespoke {
  position: relative;
  width: min(720px, calc(100% - 24px));
  max-width: none;
  margin: 12px auto;
  padding: 18px 28px 14px;
  box-sizing: border-box;
  background: var(--bg);
  /* Optional per-event tint set by JS via --ev-color. When unset, falls
     back to the theme's neutral border so the card looks like normal. */
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 8px 24px color-mix(in srgb, var(--fg) 6%, transparent);
  transition: border-color 0.18s, box-shadow 0.18s;
}
/* When a colour is picked, lift the card with a soft tinted border + glow
   so the choice is unmistakable. */
.cal-form-bespoke[style*="--ev-color"] {
  border-color: color-mix(in srgb, var(--ev-color) 55%, var(--border));
  box-shadow: 0 8px 28px color-mix(in srgb, var(--ev-color) 22%, transparent);
}
/* Title underline + clock focus ring + primary button track --ev-color
   when set; default to --accent / --fg otherwise. */
.cal-form-bespoke .cal-input.cal-hero-title:focus { border-bottom-color: var(--ev-color, var(--accent, color-mix(in srgb, var(--fg) 60%, transparent))); }
.cal-form-bespoke[style*="--ev-color"] .cal-input.cal-hero-title { border-bottom-color: color-mix(in srgb, var(--ev-color) 35%, var(--border)); }
.cal-form-bespoke .cal-hero-time:focus-visible,
.cal-form-bespoke .cal-hero-date:focus-visible { outline-color: var(--ev-color, var(--accent, color-mix(in srgb, var(--fg) 50%, transparent))); }
.cal-form-bespoke[style*="--ev-color"] .cal-btn-primary {
  background: var(--ev-color);
  border-color: var(--ev-color);
}
/* Custom-BG events paint the form card with the uploaded image. Make sure
   the action row + buttons stay clearly readable on top of it. */
.cal-form-bespoke.cal-form-bg-image .cal-form-actions {
  position: relative;
  z-index: 2;
  /* Solid backing strip so the Save/Cancel/Delete buttons don't fade into
     the photo behind them. */
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  margin: 8px -14px -14px;
  padding: 10px 14px;
  border-top: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
  border-radius: 0 0 8px 8px;
}
.cal-form-bespoke.cal-form-bg-image .cal-btn-primary {
  background: var(--accent, var(--red));
  border-color: var(--accent, var(--red));
  color: var(--bg);
}
.cal-form-close {
  position: absolute; top: 10px; right: 12px;
  background: none; border: none; color: var(--fg);
  opacity: 0.35; cursor: pointer; font-size: 20px; line-height: 1;
  padding: 4px 8px; border-radius: 6px;
}
.cal-form-close:hover { opacity: 0.9; background: color-mix(in srgb, var(--fg) 8%, transparent); }
.cal-form-mobile-cancel { display: none; }

/* "Today is …" header pinned at the top of the form. Keeps you oriented
   even when the event date you're picking is way off in the future. */
.cal-form-today {
  text-align: center;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.45;
  margin-bottom: 0;
}
.cal-form-today span { letter-spacing: 0.04em; text-transform: none; opacity: 0.85; font-weight: 500; }

/* Hero — clock face + date label. Spacing between the two lives entirely
   on the clock's bottom margin so it's predictable. */
.cal-hero {
  display: flex; flex-direction: column; align-items: center;
  gap: 0; margin-bottom: 12px;
}
.cal-hero-time {
  display: inline-flex; align-items: baseline; gap: 8px;
  font-family: ui-monospace, SFMono-Regular, "Menlo", "Consolas", monospace;
  font-variant-numeric: tabular-nums;
  font-size: 56px;
  letter-spacing: 0.02em;
  font-weight: 200;
  color: var(--fg);
  line-height: 1;
  /* Behaves like a button — strip native chrome but keep it tappable. */
  background: none;
  border: none;
  /* Pill that reads as a real hit target for a 56-px clock face.
     Top padding kept tight so the clock sits close to the "Today is" line. */
  padding: 6px 38px 18px;
  margin: 0 0 24px;
  border-radius: 18px;
  cursor: pointer;
  line-height: 1;
  transition: background 0.15s, color 0.15s;
}
.cal-hero-time .cal-hero-clock,
.cal-hero-time .cal-hero-ampm { line-height: 1; }
/* The clock is a `<button>` and a global `button:hover` rule paints
   every button's background on hover. Suppress it explicitly. */
.cal-hero-time:hover { background: transparent; border-color: transparent; }
.cal-hero-time:focus-visible { outline: 2px solid var(--accent, color-mix(in srgb, var(--fg) 50%, transparent)); outline-offset: 2px; }
.cal-hero-clock { font-feature-settings: "tnum"; display: inline-flex; align-items: baseline; }
/* Per-segment hover so it's clear hh vs mm are individually clickable. */
.cal-hero-clock-hh, .cal-hero-clock-mm {
  display: inline-block;
  border-radius: 6px;
  padding: 0 2px;
  transition: background 0.12s;
}
.cal-hero-clock-hh:hover, .cal-hero-clock-mm:hover {
  background: color-mix(in srgb, var(--fg) 9%, transparent);
}
.cal-hero-sep { padding: 0 2px; opacity: 0.55; pointer-events: none; }
.cal-hero-ampm {
  font-size: 14px;
  letter-spacing: 0.12em;
  opacity: 0.55;
  font-weight: 500;
  text-transform: uppercase;
}
.cal-hero-date {
  font-size: 16px;
  opacity: 0.6;
  letter-spacing: 0.04em;
  background: none;
  border: none;
  color: inherit;
  font-family: inherit;
  padding: 4px 10px;
  border-radius: 6px;
  cursor: pointer;
  margin: 7px 0 0;          /* extra nudge below the clock */
  transition: background 0.15s, opacity 0.15s;
}
.cal-hero-date:hover { background: color-mix(in srgb, var(--fg) 6%, transparent); opacity: 0.85; }
.cal-hero-date:focus-visible { outline: 2px solid var(--accent, color-mix(in srgb, var(--fg) 50%, transparent)); outline-offset: 2px; }

/* Title input — flat, large, no chrome until focused */
.cal-input.cal-hero-title {
  font-size: 18px;
  text-align: left;
  padding: 10px 12px 10px 0;
  background: transparent;
  border: 1px solid transparent;
  border-bottom: 1px solid color-mix(in srgb, var(--fg) 18%, transparent);
  border-radius: 0;
  margin-bottom: 4px;
}
.cal-input.cal-hero-title:focus {
  border-color: transparent;
  border-bottom-color: var(--accent, color-mix(in srgb, var(--fg) 60%, transparent));
}

/* Details panel — animated reveal via max-height + opacity. */
.cal-form-bespoke .cal-form-details {
  display: block;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-height 240ms ease, opacity 180ms ease, margin 220ms ease;
  margin-top: 0;
}
.cal-form-bespoke.is-expanded .cal-form-details {
  max-height: 720px;
  opacity: 1;
  margin-top: 4px;
}
/* The detail children themselves keep the existing flex/gap rules from
   the regular .cal-form, so we don't restyle inputs/rows here. */
.cal-form-bespoke .cal-form-details > * + * { margin-top: 8px; }

.cal-form-bespoke .cal-form-actions { margin-top: 8px; justify-content: flex-end; }
.cal-form-bespoke .cal-form-row { margin-bottom: 8px; }

/* Location row: input + Apple Maps pin link. Pin only lights up when the
   field has content; clicking opens maps.apple.com (native Maps app on
   Apple devices, web fallback elsewhere). All theme vars. */
.cal-loc-row {
  display: flex;
  align-items: stretch;
  gap: 6px;
}
.cal-loc-row > input { flex: 1; min-width: 0; }
.cal-loc-map {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  flex-shrink: 0;
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--accent, var(--red));
  background: color-mix(in srgb, var(--fg) 5%, var(--bg));
  text-decoration: none;
  transition: background 0.15s, border-color 0.15s, opacity 0.15s;
}
.cal-loc-map:hover { background: color-mix(in srgb, var(--accent, var(--red)) 14%, var(--bg)); border-color: var(--accent, var(--red)); }
.cal-loc-map.is-disabled { opacity: 0.3; pointer-events: none; cursor: default; }

@media (max-width: 520px) {
  .cal-hero-time { font-size: 44px; }
  .cal-form-bespoke { margin: 16px 12px; padding: 32px 16px 14px; }
  .cal-form-mobile-cancel {
    display: inline-flex;
    position: absolute;
    top: 8px;
    right: 8px;
    width: 30px;
    height: 30px;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid color-mix(in srgb, var(--border) 80%, transparent);
    border-radius: 999px;
    background: color-mix(in srgb, var(--bg) 82%, transparent);
    color: var(--fg);
    opacity: 0.72;
    cursor: pointer;
    z-index: 3;
    -webkit-tap-highlight-color: transparent;
  }
  .cal-form-mobile-cancel:active {
    opacity: 1;
    background: color-mix(in srgb, var(--accent, var(--red)) 14%, var(--bg));
    border-color: color-mix(in srgb, var(--accent, var(--red)) 55%, var(--border));
  }
}
.cal-input { background:color-mix(in srgb, var(--fg) 5%, var(--bg)); border:1px solid var(--border); border-radius:5px; padding:7px 10px; color:var(--fg); font-size:12px; width:100%; box-sizing:border-box; }
.cal-input:focus { outline:none; border-color:var(--accent); }
.cal-input-time { width:auto; flex:1; }
select.cal-input { appearance:auto; }
textarea.cal-input { resize:vertical; font-family:inherit; }
.cal-form-row { display:flex; gap:8px; align-items:center; }
.cal-form-actions { display:flex; gap:8px; justify-content:flex-end; margin-top:4px; }
button.cal-btn { background:color-mix(in srgb, var(--fg) 8%, transparent); border:1px solid var(--border); color:var(--fg); border-radius:5px; padding:0 14px; height:28px; font-size:11px; font-weight:500; cursor:pointer; font-family:inherit; display:inline-flex; align-items:center; justify-content:center; box-sizing:border-box; transition:background 0.15s, border-color 0.15s; }
button.cal-btn:hover { background:color-mix(in srgb, var(--fg) 14%, transparent); }
button.cal-btn.cal-btn-primary { background:var(--accent, var(--red)); color:var(--bg); border-color:var(--accent, var(--red)); }
button.cal-btn.cal-btn-primary:hover { opacity:0.9; background:var(--accent, var(--red)); }
button.cal-btn.cal-btn-danger { color:var(--accent, var(--red)); border-color:var(--accent, var(--red)); background:transparent; }
button.cal-btn.cal-btn-danger:hover { background:var(--accent, var(--red)); color:var(--bg); }
@media (max-width: 600px) {
  .cal-modal-container { max-width:100vw; width:100vw; border-radius:0; max-height:100vh; height:100vh; }
  .cal-day { min-height:44px; padding:2px; }
  .cal-day-num { font-size:9px; }
  .cal-event-preview { display:none; }
  .cal-multiday { font-size:7px; padding:0 2px; }
  /* Two-row toolbar on mobile:
        Row 1 — Title (full width)
        Row 2 — [← Today →] [view toggle] [settings/sync/filters/+New]
     Title gets its own row so it isn't squeezed to nothing when the
     right-side group is wide. The .cal-toolbar-nav (which now only
     contains the title since JS moved the date-nav to the right) is
     width:100% so it forces a wrap before .cal-toolbar-right. */
  .cal-toolbar {
    gap: 4px;
    flex-wrap: wrap;
    row-gap: 4px;
  }
  .cal-toolbar-nav {
    flex-wrap: nowrap;
    flex: 1 1 100%;
    min-width: 0;
    justify-content: flex-start;
  }
  .cal-toolbar-right {
    margin-top: 8px;
    margin-left: 0;
    flex-wrap: nowrap;
    flex-shrink: 1;
    overflow-x: auto;
    scrollbar-width: none;
    width: 100%;
  }
  .cal-toolbar-right::-webkit-scrollbar { display: none; }
  .cal-title {
    font-size: 13px;
    padding: 0 4px;
  }
  .cal-nav { padding: 2px 5px; font-size: 10px; }
  .cal-view-btn { padding: 2px 5px; font-size: 9px; }
  .cal-filters { gap: 3px; margin-top: 4px; }
  .cal-filter-item { font-size: 9px; padding: 1px 5px; }
  .cal-year { grid-template-columns:repeat(3, 1fr); gap:6px; }
  .cal-year-cell { font-size:7px; }
}

/* ═══ Research Panel ═══ */
body.research-panel-view #research-divider { display:none; }
/* .research-overlay inherits from .modal — no extra layout needed */
.research-pane {
  display:flex; flex-direction:column;
  padding:10px; box-sizing:border-box;
  font-size:12px; letter-spacing:-0.015em;
  position: relative;
  isolation: isolate;
  overflow: hidden;
}
/* Synapse signal traveling around the outer edge of the pane. The pane keeps a
   flat background; only this thin border ring remains animated. */
.research-pane::after {
  content: '';
  position: absolute; inset: 0;
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
  padding: 2px;
  background: conic-gradient(
    from var(--research-orbit-angle, 0deg),
    transparent 0deg,
    transparent 300deg,
    color-mix(in srgb, var(--accent, var(--red)) 70%, transparent) 335deg,
    var(--accent, var(--red)) 350deg,
    color-mix(in srgb, var(--accent, var(--red)) 70%, transparent) 358deg,
    transparent 360deg
  );
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
}
@media (prefers-reduced-motion: reduce) {
  .research-pane::after { animation: none; opacity: 0.4; }
}
.research-pane-header {
  display:flex; justify-content:space-between; align-items:center;
  margin-bottom:6px; cursor:grab; user-select:none;
}
.research-pane-header:active { cursor:grabbing; }
.research-pane-header h4 {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--red);
}
/* Match Library's inline SVG alignment inside the h4 */
.research-pane-header h4 svg {
  vertical-align: -2px;
  margin-right: 6px;
}
.research-pane-header-actions {
  display: flex; align-items: center; gap: 2px;
  margin-left: auto;
}
.research-pane-body {
  flex: 1; min-height: 0; overflow: hidden;
  display: flex; flex-direction: column;
  /* Flat surface — matches Library's .modal-body (no inset sub-window).
     The outer .research-pane already provides the 10px padding + border. */
  padding: 0;
  margin: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  font-size: 12px;
  color: var(--fg);
}
/* Hide the in-body "Research" subtitle on mobile to save vertical space —
   the toolbar/header carries enough context already. */
/* Match the .admin-card h2 styling used in Documents/Library so the
   "Research" sub-title reads the same as those modals' titles. */
.research-new-job h2 {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.03em;
}
@media (max-width: 600px) {
  /* Keep the "Research" title visible on mobile (matches the Cookbook tab
     titles, which show on mobile). It used to be hidden here. */
  .research-new-job h2 {
    font-size: 14px;
  }
}
/* Match the .admin-card surface used in Cookbook (Download / Serve sections):
   var(--panel) bg with var(--border) and rounded corners. */
.research-new-job {
  /* Match the Cookbook download block (.admin-card) exactly. */
  padding: 12px;
  margin-bottom: 10px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  flex-shrink: 0;
}
.research-query {
  width:100%; resize:vertical; min-height:80px; max-height:240px;
  /* Match the rest of the app's inputs (var(--bg) page tone) rather than
     the darker panel tone — keeps the input readable on the dark sub-window. */
  background: var(--bg); color: var(--fg);
  border:1px solid var(--border); border-radius:6px;
  padding:8px 10px; font-size:12px; font-family:inherit;
  box-sizing:border-box;
  margin-top: 6px;
}
.research-query:focus { outline:none; border-color:var(--accent-primary, var(--red)); }
.research-settings-row {
  display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap;
  padding: 8px 10px;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.research-setting {
  display:flex; flex-direction:column; flex:1; min-width:90px;
}
.research-setting-label {
  font-size:9px; text-transform:uppercase; letter-spacing:0.5px;
  opacity:0.5; margin-bottom:2px;
}
.research-setting select {
  font-size: 11px; padding: 4px 6px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border); border-radius: 4px;
}
.research-controls-row {
  display:flex; align-items:center; gap:10px; margin-top:10px;
}
.research-start-btn {
  margin-left:auto;
  display:flex; align-items:center; gap:5px;
  padding:6px 16px; border:none; border-radius:6px;
  background:var(--accent-primary, var(--red)); color:#fff;
  font-size:12px; font-weight:600; cursor:pointer;
  transition:opacity 0.15s;
}
.research-start-btn:hover { opacity:0.85; }
.research-start-btn:disabled, .research-start-btn.research-start-busy {
  opacity:0.7; cursor:wait;
}
.research-start-spinner {
  display:inline-block; width:11px; height:11px; vertical-align:-1px; margin-right:4px;
  border:2px solid currentColor; border-right-color:transparent; border-radius:50%;
  animation: spin 0.7s linear infinite;
}
.research-category-row {
  display:flex; gap:4px; margin-top:8px; flex-wrap:wrap;
}
/* Mobile: keep the category chips on ONE row (scroll horizontally) instead of
   wrapping to two rows. */
@media (max-width: 600px) {
  .research-category-row {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .research-category-row::-webkit-scrollbar { display: none; }
  .research-cat { flex-shrink: 0; white-space: nowrap;   position: relative; top: -8px;
}
}
/* Match .doclib-chip exactly so categories feel like document filter tags. */
.research-cat {
  padding: 2px 10px;
  border-radius: 12px;
  font-size: 10px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--fg-muted);
  cursor: pointer;
  user-select: none;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
  position: relative;
  top: -4px;
}
.research-cat:hover { border-color: var(--red); }
.research-cat.active {
  background: color-mix(in srgb, var(--red) 15%, transparent);
  border-color: color-mix(in srgb, var(--red) 40%, transparent);
  color: var(--red);
}
/* Match the Cookbook "Trending models" toggle (a left-aligned memory-toolbar-btn
   with an arrow + label) instead of the old tiny-uppercase borderless text. */
.research-settings-toggle {
  display:flex; align-items:center; gap:6px;
  width:100%; text-align:left;
  height:26px; padding:0 8px; margin-top:23px;
  background:none; border:1px solid var(--border); border-radius:4px;
  color:color-mix(in srgb, var(--fg) 60%, transparent);
  font-size:11px; font-family:inherit; cursor:pointer;
  transition:all 0.15s;
}
.research-settings-toggle:hover { color:var(--fg); border-color:var(--fg); }
.research-settings-chevron { display:inline-flex; transition:transform 0.2s; margin-left:auto; }
.research-settings-toggle.collapsed .research-settings-chevron { transform:rotate(-90deg); }
.research-add-btn {
  padding:6px 14px; border:1px solid var(--border); border-radius:6px;
  background:transparent; color:var(--fg); font-size:12px; cursor:pointer;
  transition:background 0.15s;
}
.research-add-btn:hover { background:color-mix(in srgb, var(--border) 30%, transparent); }

/* Popover anchored to the Start-All button — pick parallel vs sequential
   without the heavy "Run N jobs" modal. Drops down by default, flips to
   drop-up when there's no room below (the .rrm-up modifier is added by
   the JS that positions it). */
.research-run-mode-popover {
  position: fixed;
  z-index: 12000;
  min-width: 200px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.35);
  padding: 5px;
  font-size: 13px;
  animation: rrm-pop-in 0.12s ease-out both;
  transform-origin: top right;
}
.research-run-mode-popover.rrm-up { transform-origin: bottom right; }
@keyframes rrm-pop-in {
  from { opacity: 0; transform: scale(0.96) translateY(-2px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}
.research-run-mode-popover .research-run-mode-row {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  color: var(--fg);
  font-family: inherit;
  padding: 10px 13px;
  border-radius: 6px;
  cursor: pointer;
}
.research-run-mode-popover .research-run-mode-row svg { flex-shrink: 0; opacity: 0.8; }
.research-run-mode-popover .research-run-mode-row:hover {
  background: color-mix(in srgb, var(--accent-primary, var(--red)) 14%, transparent);
}
.research-run-mode-popover .rrm-title {
  font-weight: 600;
  font-size: 13px;
  color: var(--fg);
}
.research-jobs-list {
  flex:1; min-height:0; overflow-y:auto; padding:6px 0;
  display: flex; flex-direction: column; gap: 6px;
}
.research-empty {
  text-align:center; padding:30px 14px;
  font-size:12px; opacity:0.4;
}
/* Cards match Library's .doclib-card: subtle fg 3% tint over var(--bg). */
.research-job-card {
  margin: 0;
  padding: 8px 10px;
  background: color-mix(in srgb, var(--fg) 3%, transparent);
  border: 1px solid var(--border);
  border-radius: 8px;
  transition: background 0.15s, border-color 0.15s;
}
.research-job-card:hover {
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  border-color: color-mix(in srgb, var(--fg) 16%, transparent);
}
.research-job-card.running { border-left:3px solid var(--accent-primary, var(--red)); }
.research-job-card.queued  { border-left:3px solid var(--fg-dim, #888); }
/* No colored left-accent bar on done/past research — it read as a generic
   "AI default" card. The category/standard badge carries the meaning instead. */
.research-job-card.done    { border-left:1px solid var(--border); cursor:pointer; }
/* Past (library) research used to be dimmed to 0.65 which read as "greyed out".
   They now look the same as the rest — folding them under "Past research"
   handles the de-clutter instead. */
.research-job-card.done.from-library { opacity:1; }
.research-job-card.error,
.research-job-card.cancelled { border-left:3px solid #f44336; }
/* Per-category theming — picks up a category accent, badge, and soft tint */
.research-job-card[data-category] { --cat-color: var(--accent, var(--red)); }
.research-job-card[data-category="product"]    { --cat-color: #5b8abf; }
.research-job-card[data-category="comparison"] { --cat-color: #e5a33a; }
.research-job-card[data-category="howto"]      { --cat-color: #82c882; }
.research-job-card[data-category="landscape"]  { --cat-color: #a07ae0; }
.research-job-card[data-category="factcheck"]  { --cat-color: var(--red); }
.research-job-card.done[data-category] {
  background: color-mix(in srgb, var(--cat-color) 4%, transparent);
}
.research-job-card.done[data-category]:hover {
  background: color-mix(in srgb, var(--cat-color) 7%, transparent);
}
/* Inline category label — sits beside the title in low-opacity colored
   text instead of a separate pill. Picks up the per-card --cat-color. */
/* Uncategorized (default-format) research = "standard", shown in the same
   green as its left border so the green reads as a labelled category. */
.research-cat-badge.research-cat-standard { color: var(--color-success); opacity: 0.75; }
.research-cat-badge {
  font-size: 10px;
  font-weight: 500;
  text-transform: lowercase;
  letter-spacing: 0;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  color: var(--cat-color, var(--accent));
  opacity: 0.55;
  flex-shrink: 0;
  margin-left: 2px;
  position: relative;
  top: -1px;
}
.research-job-report-body h1, .research-job-report-body h2, .research-job-report-body h3 {
  color: var(--cat-color, var(--accent, var(--fg)));
}

/* Category hero banner */
.research-hero {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 18px; margin: 8px 0 14px;
  border-radius: 10px;
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--cat-color, var(--accent)) 18%, transparent) 0%,
    color-mix(in srgb, var(--cat-color, var(--accent)) 5%, transparent) 100%
  );
  border-left: 4px solid var(--cat-color, var(--accent));
  position: relative; overflow: hidden;
}
.research-hero::after {
  content: ''; position: absolute; right: -40px; top: -40px;
  width: 160px; height: 160px; border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--cat-color, var(--accent)) 15%, transparent) 0%, transparent 60%);
  pointer-events: none;
}
.research-hero-icon {
  flex-shrink: 0; width: 32px; height: 32px;
  color: var(--cat-color, var(--accent));
  display: flex; align-items: center; justify-content: center;
  filter: drop-shadow(0 2px 4px color-mix(in srgb, var(--cat-color, var(--accent)) 40%, transparent));
}
.research-hero-icon svg { width: 100%; height: 100%; }
.research-hero-text { flex: 1; min-width: 0; }
.research-hero-label {
  font-size: 10px; text-transform: uppercase; letter-spacing: 1.2px;
  font-weight: 700; opacity: 0.7; margin-bottom: 3px;
  color: var(--cat-color, var(--accent));
}
.research-hero-query {
  font-size: 15px; font-weight: 600;
  line-height: 1.3; color: var(--fg);
}

/* Product: callout-style bullets */
.research-body-product ul { list-style: none; padding-left: 0; }
.research-body-product ul li {
  padding: 6px 10px 6px 28px; margin: 4px 0;
  border-left: 2px solid color-mix(in srgb, #5b8abf 40%, transparent);
  background: color-mix(in srgb, #5b8abf 4%, transparent);
  border-radius: 0 4px 4px 0; position: relative;
}
.research-body-product ul li::before {
  content: '▸'; position: absolute; left: 10px;
  color: #5b8abf; font-weight: bold;
}

/* Comparison: styled table */
.research-body-comparison table {
  width: 100%; border-collapse: collapse; margin: 12px 0;
  border: 1px solid color-mix(in srgb, #e5a33a 25%, transparent);
  border-radius: 6px; overflow: hidden;
}
.research-body-comparison th {
  background: color-mix(in srgb, #e5a33a 18%, transparent);
  color: #e5a33a; font-weight: 700;
  padding: 8px 12px; text-align: left;
  border-bottom: 2px solid color-mix(in srgb, #e5a33a 40%, transparent);
}
.research-body-comparison td {
  padding: 8px 12px;
  border-bottom: 1px solid color-mix(in srgb, #e5a33a 12%, transparent);
}
.research-body-comparison tr:nth-child(even) td {
  background: color-mix(in srgb, #e5a33a 3%, transparent);
}

/* How-to: big numbered steps */
.research-body-howto ol { counter-reset: howto-step; list-style: none; padding-left: 0; }
.research-body-howto ol > li {
  counter-increment: howto-step; position: relative;
  padding: 10px 12px 10px 52px; margin: 8px 0;
  background: color-mix(in srgb, #82c882 5%, transparent);
  border-radius: 8px; border-left: 2px solid #82c882;
}
.research-body-howto ol > li::before {
  content: counter(howto-step);
  position: absolute; left: 10px; top: 14px;
  width: 30px; height: 30px; border-radius: 50%;
  background: #82c882; color: white;
  font-weight: 700; font-size: 13px;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 6px color-mix(in srgb, #82c882 40%, transparent);
}

/* Landscape: section banners */
.research-body-landscape h3 {
  padding: 8px 14px;
  background: linear-gradient(90deg, color-mix(in srgb, #a07ae0 15%, transparent), transparent);
  border-left: 3px solid #a07ae0;
  border-radius: 0 6px 6px 0;
  margin: 14px 0 8px;
}

/* Fact-check: verdict emphasis */
.research-body-factcheck blockquote {
  border-left: 3px solid var(--red);
  background: color-mix(in srgb, var(--red) 6%, transparent);
  padding: 10px 14px; margin: 10px 0;
  border-radius: 0 6px 6px 0;
}
.research-body-factcheck strong {
  color: var(--red);
  padding: 1px 6px; border-radius: 4px;
  background: color-mix(in srgb, var(--red) 12%, transparent);
}

.research-job-header {
  display:flex; align-items:center; gap:8px;
}
/* Active (running) card: nudge the title + text assets up 4px so they line up
   with the chevron / Clear button on the right. */
.research-job-card.running .research-job-query,
.research-job-card.running .research-cat-badge,
.research-job-card.running .research-job-model,
.research-job-card.running .research-job-time {
  position: relative;
  top: -4px;
}
.research-job-query {
  flex:1; font-size:11px; font-weight:600;
  letter-spacing: -0.01em;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  color: var(--fg);
}
.research-job-time,
.research-job-meta {
  font-size:10px; opacity:0.5; white-space:nowrap; font-family:monospace;
}
.research-job-status {
  font-size:10px; text-transform:uppercase; opacity:0.6;
}
.research-job-cancel,
.research-job-remove,
.research-job-report-link {
  background:transparent; border:none; color:var(--fg);
  opacity:0.4; cursor:pointer; padding:2px; display:flex;
  transition:opacity 0.15s;
}
.research-job-cancel:hover,
.research-job-remove:hover,
.research-job-report-link:hover { opacity:1; }
/* Minimize toggle for the per-job synapse "tree" visual. */
.research-synapse-toggle {
  background:transparent; border:none; color:var(--fg);
  opacity:0.4; cursor:pointer; padding:2px; display:flex;
  transition:opacity 0.15s;
}
.research-synapse-toggle:hover,
.research-synapse-toggle.active { opacity:1; }
.research-job-synapse-host.synapse-collapsed { display:none; }
.research-job-model {
  font-size:9px; opacity:0.4; white-space:nowrap;
  max-width:120px; overflow:hidden; text-overflow:ellipsis;
}
.research-job-actions {
  display:flex; gap:4px; margin-top:6px;
}
/* Push the first dim (dismiss/delete) button — and everything after it — to
   the right edge of the actions row. `:first-of-type` would match the very
   first <button> in the row regardless of class; this immediate-sibling
   combinator targets only a dim button that follows a non-dim one. */
.research-job-actions .research-job-action:not(.research-job-action-dim)
  + .research-job-action-dim { margin-left: auto; }
/* Edge case: all buttons in the row are dim — push first dim right. */
.research-job-actions > .research-job-action-dim:first-child { margin-left: auto; }
/* Match .doclib-toolbar-btn — same font, padding, hover */
.research-job-action {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 5px 10px;
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg-muted);
  font-size: 11px;
  font-family: inherit;
  white-space: nowrap;
  cursor: pointer;
  transition: all 0.15s;
}
.research-job-action:hover {
  color: var(--fg);
  border-color: var(--fg);
}
.research-job-action-dim { opacity: 0.5; border-color: transparent; }
.research-job-action-dim:hover { opacity: 1; }
.research-job-action-copied {
  color: var(--color-success) !important;
  border-color: color-mix(in srgb, var(--color-success) 45%, var(--border)) !important;
  opacity: 1 !important;
}
.research-job-phase {
  font-size:11px; opacity:0.6; margin-top:4px;
}
.research-job-queued-meta {
  font-size:10px; opacity:0.4; margin-top:2px;
}
.research-section-divider {
  display:flex; align-items:center; gap:10px;
  padding:6px 14px; font-size:10px; opacity:0.4;
  text-transform:uppercase; letter-spacing:0.5px;
}
.research-section-divider::before,
.research-section-divider::after {
  content:''; flex:1; height:1px;
  background:var(--border);
}
/* Foldable job sections (Active / Past research) */
/* Collapsible section styled like the Cookbook download sub-blocks (.cookbook-card):
   clean var(--bg) card, 8px radius, a real 13px/600 title. Chevron on the LEFT,
   then title, count, and a status-color dot on the right (the dot stays visible
   when folded so you get status at a glance). Keeps every modal consistent. */
.research-section {
  margin-top: 6px;
  /* Same surface as the research "window" (.research-new-job -> var(--panel)). */
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}
.research-section-body { display: flex; flex-direction: column; gap: 8px; padding: 12px; }
.research-section.collapsed .research-section-body { display: none; }
.research-section-header {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 12px; cursor: pointer; user-select: none;
  transition: background 0.15s;
}
.research-section:not(.collapsed) > .research-section-header { border-bottom: 1px solid var(--border); }
.research-section-header:hover { background: color-mix(in srgb, var(--fg) 4%, transparent); }
.research-section-title { font-size: 14px; font-weight: 600; letter-spacing: -0.03em; }
.research-section-chevron { flex-shrink: 0; opacity: 0.55; transition: transform 0.2s ease; }
.research-section.collapsed .research-section-chevron { transform: rotate(-90deg); }
.research-section-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; opacity: 0.9; }
/* Active section dot: pulsing accent glow (work in progress). */
.research-section-dot.pulsing { animation: research-dot-pulse 1.5s ease-in-out infinite; }
@keyframes research-dot-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent, var(--red)) 55%, transparent); }
  70%      { box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent, var(--red)) 0%, transparent); }
}
/* The top "Research" heading is a plain card-style title — no bar/chevron/dot. */
.research-main-header {
  cursor: default; background: none; border: none;
  padding: 6px 2px; font-size: 13px; font-weight: 600;
}
.research-main-header:hover { background: none; }
.research-section-count {
  /* Match the panel header's "N research" chip — small, muted, no weight. */
  font-size: 10px; opacity: 0.6; font-weight: normal;
  font-variant-numeric: tabular-nums;
}
.research-job-error {
  font-size:11px; color:#f44336; margin-top:4px; line-height:1.4;
  word-break:break-word;
}
.research-progress-bar {
  height:3px; background:var(--border); border-radius:2px; margin-top:6px;
  overflow:hidden;
}
.research-progress-fill {
  height:100%; background:var(--accent-primary, var(--red));
  border-radius:2px; transition:width 0.4s ease;
}
.research-job-result {
  margin-top:10px; border-top:1px solid var(--border); padding-top:10px;
}
.research-job-sources {
  display:flex; flex-wrap:wrap; gap:4px; margin-bottom:8px;
}
.research-source-link {
  font-size:10px; padding:2px 6px;
  background:color-mix(in srgb, var(--accent-primary, var(--red)) 10%, transparent);
  border-radius:3px; color:var(--accent-primary, var(--red));
  text-decoration:none; white-space:nowrap;
  max-width:200px; overflow:hidden; text-overflow:ellipsis;
}
.research-source-link:hover { text-decoration:underline; }
.research-source-more { font-size:10px; opacity:0.5; padding:2px 4px; }
.research-job-report-body {
  font-size:12px; line-height:1.55; max-height:400px; overflow-y:auto;
}
.research-job-report-body h1,
.research-job-report-body h2,
.research-job-report-body h3 { font-size:13px; margin:12px 0 4px; }
.research-job-report-body p { margin:4px 0; }
.research-job-report-body pre { font-size:11px; }
.research-job-loading { font-size:11px; opacity:0.5; padding:8px 0; }
.research-library-section {
  border-top:1px solid var(--border); flex-shrink:0;
}
.research-library-toggle {
  width:100%; background:transparent; border:none; color:var(--fg);
  padding:8px 14px; font-size:11px; text-align:left;
  opacity:0.6; cursor:pointer; font-weight:500;
}
.research-library-toggle:hover { opacity:1; }
.research-library-list {
  max-height:300px; overflow-y:auto;
}
.research-library-item {
  display:flex; align-items:center; gap:6px; flex-wrap:wrap;
  padding:8px 14px; font-size:11px;
  border-bottom:1px solid color-mix(in srgb, var(--border) 50%, transparent);
}
.research-library-item:hover { background:color-mix(in srgb, var(--border) 20%, transparent); }
.research-lib-query {
  flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:120px;
}
.research-lib-actions {
  display:flex; gap:4px; margin-left:auto;
}
.research-lib-meta {
  font-size:9px; opacity:0.4; white-space:nowrap; font-family:monospace;
}
.research-lib-open,
.research-lib-delete {
  background:transparent; border:none; color:var(--fg);
  opacity:0.3; cursor:pointer; padding:2px; display:flex;
  transition:opacity 0.15s;
}
.research-lib-open:hover { opacity:1; }
.research-lib-delete:hover { opacity:1; color:#f44336; }
.research-badge {
  background:var(--accent-primary, var(--red)); border-radius:50%;
  width:6px; height:6px; margin-left:auto; display:inline-block; flex-shrink:0;
  position: relative; left: -4px;
  /* Breathe when research has finished — gentle scale + glow pulse to
     draw the eye to the unread result without being a hard blink. */
  animation: research-badge-breathe 2.4s ease-in-out infinite;
}
@keyframes research-badge-breathe {
  0%, 100% {
    transform: scale(1);
    opacity: 0.85;
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent-primary, var(--red)) 55%, transparent);
  }
  50% {
    transform: scale(1.35);
    opacity: 1;
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent-primary, var(--red)) 0%, transparent);
  }
}
@media (prefers-reduced-motion: reduce) {
  .research-badge { animation: none; }
}

/* Sidebar Deep Research running indicator — text then dot, styled the same
   as Cookbook's running status (no glow). */
.research-sb-running {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.research-sb-status {
  font-size: 8px;
  opacity: 0.5;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  position: relative;
  top: 0;
  left: -12px;
}
.research-sb-dot {
  width: 6px;
  height: 6px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--color-success, #4caf50);
  animation: cookbook-notif-pulse 2s ease-in-out infinite;
  position: relative;
  top: -1px;
  left: -4px;
}
@media (prefers-reduced-motion: reduce) {
  .research-sb-dot { animation: none; }
}

/* ── In-house color picker ── */
/* A color-picker swatch input shows the chosen colour as its background; hide
   the underlying hex text/caret so it doesn't read as "#a1b2c3" in the box.
   (The themed/gallery variants set this too; this is the generic fallback for
   swatches outside those scopes, e.g. the calendar-settings color dots.) */
input.cp-swatch-input {
  color: transparent;
  font-size: 0;
  text-shadow: none;
  caret-color: transparent;
  user-select: none;
}
input.cp-swatch-input::selection { background: transparent; }
.cp-popover {
  position: fixed; z-index: 10000;
  display: none;
  width: 240px;
  background: var(--panel, #1a1a1a);
  border: 1px solid var(--border, #333);
  border-radius: 8px;
  padding: 10px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.45);
  font-family: inherit; color: var(--fg, #eee); font-size: 12px;
  user-select: none;
}
.cp-sl {
  position: relative;
  width: 100%; height: 160px;
  border-radius: 6px;
  cursor: crosshair;
  overflow: hidden;
  touch-action: none;
}
.cp-sl-white {
  position: absolute; inset: 0;
  background: linear-gradient(to right, #fff, rgba(255,255,255,0));
  pointer-events: none;
}
.cp-sl-black {
  position: absolute; inset: 0;
  background: linear-gradient(to top, #000, rgba(0,0,0,0));
  pointer-events: none;
}
.cp-sl-handle {
  position: absolute;
  width: 12px; height: 12px;
  margin: -6px 0 0 -6px;
  border: 2px solid #fff;
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.6), 0 1px 3px rgba(0,0,0,0.5);
  pointer-events: none;
}
.cp-hue {
  position: relative;
  margin-top: 10px;
  height: 14px;
  border-radius: 7px;
  background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
  cursor: pointer;
  touch-action: none;
}
.cp-hue-handle {
  position: absolute; top: 50%;
  width: 10px; height: 18px;
  margin: -9px 0 0 -5px;
  border: 2px solid #fff;
  border-radius: 3px;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.6), 0 1px 3px rgba(0,0,0,0.5);
  pointer-events: none;
}
.cp-row {
  display: flex; align-items: center; gap: 6px; margin-top: 10px;
}
.cp-preview {
  width: 24px; height: 24px;
  border-radius: 50%;
  border: 1px solid var(--border, #333);
  flex-shrink: 0;
}
.cp-hex {
  flex: 1; min-width: 0;
  padding: 4px 6px;
  background: var(--bg, #000); color: var(--fg, #eee);
  border: 1px solid var(--border, #333);
  border-radius: 4px;
  font-family: ui-monospace, 'Fira Code', monospace;
  font-size: 12px; text-transform: lowercase;
}
.cp-hex:focus { outline: none; border-color: var(--red, #e06c75); }
.cp-eyedropper {
  width: 26px; height: 26px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border, #333);
  border-radius: 4px;
  color: var(--fg, #eee);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  transition: background 0.1s;
}
.cp-eyedropper:hover:not(:disabled) { background: rgba(255,255,255,0.08); }
.cp-section-label {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.6px;
  opacity: 0.5; margin-top: 10px; margin-bottom: 4px;
}
.cp-swatches {
  display: flex; flex-wrap: wrap; gap: 4px;
}
.cp-swatch {
  width: 20px; height: 20px;
  border-radius: 50%;
  border: 1px solid var(--border, #333);
  cursor: pointer;
  padding: 0;
  transition: transform 0.08s;
}
.cp-swatch:hover { transform: scale(1.15); }
.cp-recent-empty {
  font-size: 11px; opacity: 0.4; padding: 2px 0;
}

/* PDF form export modal + signature modals — match the app theme via the
   existing modal/confirm-btn classes. Only a few atom-level styles below. */
.pdf-export-overlay .modal-content { padding: 12px 14px; }
.pdf-export-overlay #pdf-export-body { padding-right: 4px; }
.pdf-export-overlay .pdf-export-section {
  margin-bottom: 14px;
}
.pdf-export-overlay .pdf-export-section-title {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--red);
  opacity: 0.85;
  margin-bottom: 6px;
}
.pdf-export-overlay .pdf-export-row {
  display: grid;
  grid-template-columns: 38% 62%;
  gap: 8px;
  align-items: center;
  padding: 3px 0;
}
.pdf-export-overlay .pdf-export-row label {
  font-size: 0.78rem;
  color: var(--fg);
  opacity: 0.85;
}
.pdf-export-input,
.pdf-export-overlay select.pdf-export-input,
.pdf-export-overlay input.pdf-export-input {
  padding: 4px 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  border-radius: 4px;
  font-size: 0.78rem;
  font-family: inherit;
}
.pdf-export-input:focus {
  outline: none;
  border-color: var(--fg);
}
.sig-modal-overlay .modal-content { padding: 12px 14px; }
.sig-modal-overlay .sig-canvas {
  display: block;
  width: 100%;
  height: 240px;
  background: #fff;
  cursor: crosshair;
  border: 1px dashed var(--border);
  border-radius: 5px;
  /* Drawing uses pointer events; without this, a touch-drag on the canvas pans
     the page / sheet instead of drawing ("the whole window moves"). */
  touch-action: none;
}
.sig-modal-overlay .sig-name,
.sig-modal-overlay .sig-smoothness {
  font-family: inherit;
}
.sig-modal-overlay .sig-name {
  width: 100%;
  padding: 6px 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  border-radius: 4px;
  font-size: 0.85rem;
}
.sig-modal-overlay .sig-tile {
  position: relative;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px;
  cursor: pointer;
  background: var(--bg);
}
.sig-modal-overlay .sig-tile:hover { border-color: var(--fg); }
.sig-modal-overlay .sig-tile img {
  display: block;
  width: 100%;
  height: 80px;
  object-fit: contain;
  background: #fff;
  border-radius: 3px;
}
.sig-modal-overlay .sig-tile-del {
  position: absolute;
  top: 1px;
  right: 6px;
  width: 20px;
  height: 20px;
  /* Center the × glyph inside the round bg — without this the button's default
     text metrics push it past the right edge of the circle. */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0 0 1px;
  box-sizing: border-box;
  font-size: 0.85rem;
  line-height: 1;
  border: 1px solid var(--accent, var(--red));
  background: #fff;
  color: var(--accent, var(--red));
  border-radius: 50%;
  cursor: pointer;
  opacity: 1;
}
.sig-modal-overlay .sig-tile-del:hover {
  background: color-mix(in srgb, var(--accent, var(--red)) 10%, #fff);
}
.sig-modal-overlay .sig-new-tile {
  border: 2px dashed var(--border);
  border-radius: 6px;
  padding: 8px;
  cursor: pointer;
  background: var(--bg);
  color: var(--fg);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 106px;
  font-weight: 600;
  opacity: 0.85;
}
.sig-modal-overlay .sig-new-tile:hover {
  opacity: 1;
  border-color: var(--fg);
}

/* Form checkboxes — render as a circular dot, theme-aware. Used in the
   Export PDF modal and the inline PDF view overlay. */
.pdf-export-overlay input[type="checkbox"],
#doc-pdf-view input[type="checkbox"] {
  -webkit-appearance: none;
  appearance: none;
  margin: 0;
  cursor: pointer;
  border-radius: 50%;
  background: #fff;
  position: relative;
}
.pdf-export-overlay input[type="checkbox"] {
  width: 14px;
  height: 14px;
  border: 1.5px solid var(--border);
  background: var(--bg);
}
.pdf-export-overlay input[type="checkbox"]:checked {
  background: var(--fg);
  border-color: var(--fg);
}
/* PDF view overlays on a white page render — keep colors theme-independent
   so the dot is always visible against the rendered PDF. */
#doc-pdf-view input[type="checkbox"] {
  border: 1.5px solid #444;
}
#doc-pdf-view input[type="checkbox"]:checked {
  background: #111;
  border-color: #111;
}

/* ─────────────────────────────────────────────────────────────────────────
   Frosted glass theme — applied when the user enables the "Glass" toggle
   in theme settings (writes `body.theme-frosted`). Layers a semi-translucent
   tint over every panel/modal/sidebar/dropdown and blurs whatever is
   behind it, so the UI reads like layered glass.
   ───────────────────────────────────────────────────────────────────────── */
body.theme-frosted #sidebar,
body.theme-frosted .modal-content,
body.theme-frosted .doclib-modal-content,
body.theme-frosted .gallery-modal-content,
body.theme-frosted .notes-pane,
body.theme-frosted .research-pane,
body.theme-frosted .calendar-pane,
body.theme-frosted .cookbook-pane,
body.theme-frosted .doc-pane,
body.theme-frosted .doc-pane-floating,
body.theme-frosted .cp-popover,
body.theme-frosted .dropdown,
body.theme-frosted .overflow-menu,
body.theme-frosted .doc-tab-dropdown,
body.theme-frosted .email-reader,
body.theme-frosted .admin-card,
body.theme-frosted .gallery-detail-menu,
body.theme-frosted #theme-popup .modal-content {
  /* Tinted base color (set separately so the shorthand can't swallow it);
     much more transparent than before so what's behind actually shows
     through. The blur catches whatever pixels are below. */
  background-color: color-mix(in srgb, var(--panel, var(--bg)) 32%, transparent) !important;
  background-image: linear-gradient(180deg,
    color-mix(in srgb, var(--fg, #fff) 14%, transparent) 0%,
    color-mix(in srgb, var(--fg, #fff)  4%, transparent) 26%,
    transparent 55%) !important;
  backdrop-filter: blur(24px) saturate(170%) !important;
  -webkit-backdrop-filter: blur(24px) saturate(170%) !important;
  border-color: color-mix(in srgb, var(--fg) 22%, transparent) !important;
}
/* The sidebar header + user bar paint their own opaque --sidebar-bg/--panel.
   Under the frosted theme the sidebar is translucent glass, so those solid
   bands stood out as a dark (near-black) strip over the title/avatar area.
   Make them transparent so they blend into the frosted sidebar. */
body.theme-frosted .sidebar-header,
body.theme-frosted .sidebar-user-bar {
  background: transparent !important;
}
/* Same problem on tool modals: the sticky .modal-header paints a solid
   var(--panel) bar (so scrolled content doesn't bleed through). Over a
   frosted-glass modal body that solid bar reads as a dark band across the
   title area (Calendar, Tasks, Cookbook, Memory, Email…). Give the header
   its own matching frosted glass — translucent tint + blur — so it stays
   readable while scrolling but blends into the panel instead of a black band. */
body.theme-frosted .modal-header {
  /* Transparent — NOT another panel tint. The header sits on top of the
     already-frosted modal body, so any added tint stacks and the band
     reads darker than its surroundings. Keep just the blur (which doesn't
     darken) so scrolled content stays legible under the sticky header. */
  background-color: transparent !important;
  background-image: none !important;
  backdrop-filter: blur(24px) !important;
  -webkit-backdrop-filter: blur(24px) !important;
}
/* Deep Research's header isn't sticky (content doesn't scroll under it) and
   its pane carries extra frosting — saturate + a top gradient highlight +
   the synapse glow. The generic header blur above adds its OWN glass layer,
   which renders a different shade than the pane behind it. Drop the header's
   blur so the pane's frosted background shows through the header uniformly. */
body.theme-frosted .research-pane-header {
  background-color: transparent !important;
  background-image: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
body.theme-frosted .notes-mobile-grabber {
  background-color: transparent !important;
  background-image: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
/* Inner admin-cards inside frosted modals stack their transparency on top
   of the modal's, which kills the see-through. Make the inner card nearly
   fully transparent so the outer modal's blur dominates. */
body.theme-frosted .modal-content .admin-card,
body.theme-frosted .doclib-modal-content .admin-card,
body.theme-frosted .gallery-modal-content .admin-card,
body.theme-frosted #theme-popup .admin-card {
  background-color: color-mix(in srgb, var(--panel, var(--bg)) 10%, transparent) !important;
  background-image: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
/* Subtle inner light highlight + softer shadow on the heavier surfaces */
body.theme-frosted .modal-content,
body.theme-frosted .doclib-modal-content,
body.theme-frosted .gallery-modal-content,
body.theme-frosted .notes-pane,
body.theme-frosted .research-pane,
body.theme-frosted .calendar-pane,
body.theme-frosted .cookbook-pane,
body.theme-frosted .doc-pane,
body.theme-frosted .doc-pane-floating {
  box-shadow:
    0 14px 36px rgba(0, 0, 0, 0.5),
    inset 0 1px 0 color-mix(in srgb, var(--fg, #fff) 14%, transparent),
    inset 0 -1px 0 color-mix(in srgb, #000 14%, transparent) !important;
}
/* Backdrops should be MUCH less opaque so the blur effect reads through to
   the actual page content behind the modal. */
body.theme-frosted .modal {
  background: color-mix(in srgb, #000 12%, transparent) !important;
}

/* Mobile only: lift the Dependencies "Server" dropdown's selected text up ~2px. */
@media (max-width: 768px) {
  #hwfit-deps-server { padding-bottom: 4px; line-height: 1; }
}

/* Emoji rendered as monochrome line icons (OpenMoji-black via /api/emoji proxy)
   instead of colorful system glyphs — project rule: never colorful emoji. The
   SVG is used as a mask filled with the current text color, so it tints to the
   theme. Sized to sit on the text baseline. */
.emoji {
  height: 1.15em;
  width: 1.15em;
  vertical-align: -0.18em;
  margin: 0 0.05em;
  display: inline-block;
  background-color: currentColor;
  -webkit-mask: var(--em) center / contain no-repeat;
          mask: var(--em) center / contain no-repeat;
}

/* Nudge the X icon in the "Clear all" button down 2px. */
#research-clear-all svg { position: relative; top: 2px; }

/* RESEARCH SCROLL FORCE — guarantee the jobs list scrolls inside the bounded
   pane (the pane is height-capped: 85vh desktop, 100dvh mobile). Each ancestor
   needs min-height:0 for a nested flex scroll to engage. */
#research-pane { min-height: 0; }
/* The BODY is the single scroller — the list flows into it and the whole body
   scrolls. (A nested list-scroller kept clipping instead of scrolling.) */
#research-pane .research-pane-body {
  flex: 1 1 auto !important; min-height: 0 !important;
  display: flex !important; flex-direction: column !important;
  overflow-y: auto !important; -webkit-overflow-scrolling: touch;
}
#research-pane .research-jobs-list {
  flex: 0 0 auto !important; min-height: 0 !important;
  overflow: visible !important; max-height: none !important;
}

/* RESEARCH FULLSCREEN MOBILE — the pane wasn't opening to full height on mobile,
   so its content overflowed with nowhere to scroll. Pin it to the full viewport
   (id selector + !important beats the inherited modal-content sizing). */
@media (max-width: 768px) {
  /* Bottom-sheet: 90dvh tall (a touch smaller than full-screen) anchored to the
     bottom, so there's a small gap at the top and it reads as a sheet. The body
     still scrolls within the bounded height. */
  #research-overlay { align-items: flex-end !important; justify-content: stretch !important; }
  #research-pane {
    height: 90dvh !important;
    max-height: 90dvh !important;
    width: 100vw !important;
    max-width: 100vw !important;
    border-radius: 14px 14px 0 0 !important;
  }
  /* Bottom breathing room so the expanded Settings + Start button clear the
     browser's bottom toolbar / safe area and can always be scrolled into view
     (otherwise the form fits the viewport and there's no room to reach them). */
  #research-pane .research-pane-body {
    padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 40px) !important;
    touch-action: pan-y;
    overscroll-behavior: contain;
  }
}

/* Quick fade + up-nudge. */
.research-start-btn { position: relative; top: -6px; transition: opacity 0.16s ease; }

/* Right-side group of the section header: clear button (past), status dot, chevron. */
.research-section-right { display: flex; align-items: center; gap: 8px; margin-left: auto; }
/* "Clear all" in the Past header — styled like the cookbook-running clear btn. */
.research-section-clear {
  font-size: 10px; padding: 0 8px; height: 22px;
  border-radius: 6px; border: 1px solid var(--border);
  color: color-mix(in srgb, var(--fg) 45%, transparent);
  background: none; cursor: pointer; display: inline-flex; align-items: center; gap: 4px;
  font-family: inherit; white-space: nowrap; transition: all 0.15s;
  position: relative; top: -2px;
}
.research-section-clear:hover { color: var(--red); border-color: var(--red); background: color-mix(in srgb, var(--red) 8%, transparent); }
.research-section-clear svg { width: 11px; height: 11px; }

/* Visual Report button tinted with the research type colour (--cat-color),
   falling back to the accent for "standard" (uncategorized) research. */
.research-job-action.research-job-action-report {
  color: var(--cat-color, var(--accent, var(--red)));
  border-color: color-mix(in srgb, var(--cat-color, var(--accent, var(--red))) 45%, var(--border));
}
.research-job-action.research-job-action-report:hover {
  color: var(--cat-color, var(--accent, var(--red)));
  border-color: var(--cat-color, var(--accent, var(--red)));
  background: color-mix(in srgb, var(--cat-color, var(--accent, var(--red))) 10%, transparent);
}

/* Nudge the "+ Queue" button up 5px. */
#research-add-btn { position: relative; top: -5px; }
/* Nudge the Start play-icon and Queue plus-sign up 1px so they sit visually
   centered with the button label text (their glyph baselines drop low). */
.research-start-btn svg { position: relative; top: -1px; }
.research-add-plus { position: relative; top: -1px; display: inline-block; }

/* Footer hint under Past research linking to the Library's Research tab. */
.research-library-hint {
  /* full-width line in the header, pulled up with negative MARGIN (collapses
     the gap so it moves up without making the header taller). */
  width: 100%; flex-basis: 100%; margin: -22px 0 0; line-height: 1.2;
}
.research-library-link {
  background: none; border: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--accent, var(--red)); text-decoration: underline;
}
.research-library-link:hover { opacity: 0.8; }

/* Nudge the Delete button 4px left. */
.research-job-action[data-action="delete"] { position: relative; right: 2px; }

/* "Standard" (uncategorized) research uses green everywhere — set its --cat-color
   to the success green so the Visual Report button matches the green badge. */
.research-job-card.done:not([data-category]) { --cat-color: var(--color-success); }

/* Failed research (0 sources) — red flag + actionable note. */
.research-job-card.research-job-failed { border-color: color-mix(in srgb, #f44336 40%, var(--border)); }
.research-cat-badge.research-cat-failed { color: #f44336; display: inline-flex; align-items: center; gap: 3px; }
.research-cat-badge.research-cat-failed svg { width: 10px; height: 10px; }
.research-job-failnote { font-size: 11px; color: #f44336; opacity: 0.9; margin: 4px 0 6px; line-height: 1.35; }

/* Shared popup-menu row feedback. Many small menus use their own item class;
   keep the hover light and consistent instead of giving one action a special
   glow. */
:is(
  .overflow-menu-item,
  .dropdown-item,
  .dropdown-item-compact,
  .export-dropdown-item,
  .sort-dropdown-item,
  .note-reminder-menu-item,
  .research-run-mode-popover .research-run-mode-row,
  .ge-fx-menu-item
) {
  transition:
    background-color 0.14s ease,
    color 0.14s ease,
    opacity 0.14s ease,
    transform 0.14s ease;
}
:is(
  .overflow-menu-item,
  .dropdown-item,
  .dropdown-item-compact,
  .export-dropdown-item,
  .sort-dropdown-item,
  .note-reminder-menu-item,
  .research-run-mode-popover .research-run-mode-row,
  .ge-fx-menu-item
):hover:not(:disabled) {
  opacity: 1;
  color: var(--fg);
  background-color: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent);
  transform: translateX(1px);
}

/* Slash command autocomplete popup, anchored to the message composer */
.slash-autocomplete-popup {
  position: fixed;
  z-index: 9000;
  background: var(--panel, var(--bg));
  border: 1px solid var(--border, rgba(255,255,255,0.08));
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.35);
  font-size: 13px;
  color: var(--fg, #e6e6e6);
  overflow-y: auto;
  padding: 4px 0;
  display: none;
}
.slash-ac-cat {
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted, #888);
  padding: 6px 10px 2px;
  opacity: 0.7;
}
.slash-ac-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 5px 10px;
  cursor: pointer;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
}
.slash-ac-row:hover { background-color: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent); }
.slash-ac-row-sel  { background-color: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent); }
.slash-ac-token {
  font-family: 'Fira Code', ui-monospace, monospace;
  color: var(--accent, var(--red));
  font-weight: 600;
  flex-shrink: 0;
}
.slash-ac-help {
  color: var(--fg);
  opacity: 0.85;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.slash-ac-usage {
  color: var(--fg-muted, #888);
  font-family: 'Fira Code', ui-monospace, monospace;
  font-size: 11px;
  opacity: 0.55;
  flex-shrink: 0;
}
.slash-ac-empty {
  padding: 10px;
  color: var(--fg-muted, #888);
  font-style: italic;
}
.slash-ac-empty code {
  font-family: 'Fira Code', ui-monospace, monospace;
  color: var(--accent, var(--red));
}

/* ══ iOS focus-zoom fix — touch devices only; desktop sizes untouched ══
   16px is the threshold below which iOS Safari auto-zooms on focus.
   Selects and date/time inputs are excluded on purpose — they open native
   pickers and never zoom. */
@media (hover: none) and (pointer: coarse) {

  /* 1 ── Catch-all: every text-entry control NOT pinned with its own
     !important. !important here beats any non-important rule regardless of
     specificity, so this clears the long tail (settings, admin, memory,
     notes, calendar, email, gallery, tasks, model picker, etc.). */
  input[type="text"],
  input[type="search"],
  input[type="email"],
  input[type="url"],
  input[type="tel"],
  input[type="password"],
  input[type="number"],
  input:not([type]),
  textarea {
    font-size: 16px !important;
  }

  /* 2 ── Fields that pin their own !important at specificity our catch-all
     can't beat. Each is matched at equal-or-higher specificity and, being
     later in the file, wins the tie. */
  #message { font-size: 16px !important; }                                  /* chat composer (was 13px !important) */
  .cookbook-dl-repo,
  .hwfit-search { font-size: 16px !important; }                             /* cookbook repo path + hardware search */
  .ge-topbar input { font-size: 16px !important; }                          /* image-editor topbar input */
  .ge-transform-field > input.ge-transform-popup-input {                    /* image-editor transform values */
    font-size: 16px !important;
  }
}

@media (hover: none) and (pointer: coarse) {
  /* Only the sub-16px tiers need bumping; large lands ABOVE 16 so it
     stays zoom-safe AND visibly larger than medium (otherwise L collapses
     onto M on touch). All three editor layers move together so the
     highlight/line-number overlay stays metrically aligned with the textarea. */
  .doc-font-m .doc-editor-textarea, .doc-font-m .doc-editor-highlight, .doc-font-m .doc-line-numbers {
    font-size: 16px !important;   /* was 13px */
  }
  .doc-font-l .doc-editor-textarea, .doc-font-l .doc-editor-highlight, .doc-font-l .doc-line-numbers {
    font-size: 18px !important;   /* was 15px — keep L > M */
  }
  /* Email compose rich-body. Medium (15px) zooms, so bump it; large (17px)
     is already ≥16px and never zoomed — leave it so we don't shrink it. */
  .doc-email-richbody.doc-font-m { font-size: 16px !important; }
}
