/* Fragment 3/9 of the former static/style.css. * * This is a mechanical, order-preserving slice: rules were moved verbatim * and this file's position in the order of static/index.html is what * keeps the cascade identical. Reordering the links, or moving a rule between * fragments, can change which of two competing declarations wins. * * static/index.html and the PRECACHE list in static/sw.js must list every * fragment in the same order. tests/test_static_stylesheet_manifest.py and * tests/test_css_computed_style_snapshot.py both fail if that drifts. */ .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; } .session-loading-state { min-height: 100%; display: flex; flex-direction: column; justify-content: flex-start; gap: 12px; color: var(--fg); font-size: 12px; pointer-events: none; position: relative; padding: 16px 14px 24px; box-sizing: border-box; overflow: hidden; } .session-loading-state::before { content: none; } .session-skeleton-bubble { width: min(78%, 680px); border: 1px solid color-mix(in srgb, var(--border) 74%, transparent); border-radius: 10px; background: color-mix(in srgb, var(--panel, var(--bg)) 72%, transparent); padding: 10px 12px 12px; box-shadow: 0 8px 22px rgba(0, 0, 0, 0.14); } .session-skeleton-bubble.is-user { align-self: flex-end; width: min(64%, 520px); } .session-skeleton-bubble.is-ai { align-self: flex-start; } .session-skeleton-line { position: relative; overflow: hidden; background: color-mix(in srgb, var(--fg) 10%, transparent); border: 1px solid color-mix(in srgb, var(--border) 82%, transparent); border-radius: 999px; } .session-skeleton-line { height: 10px; margin-top: 8px; opacity: 0.62; } .session-skeleton-line::after { content: ''; position: absolute; inset: -1px; transform: translateX(-120%); background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--fg) 14%, transparent), transparent); animation: session-skeleton-shimmer 1.25s ease-in-out infinite; } .session-skeleton-bubble:nth-child(2) .session-skeleton-line::after { animation-delay: 0.12s; } .session-skeleton-bubble:nth-child(3) .session-skeleton-line::after { animation-delay: 0.24s; } .session-skeleton-bubble:nth-child(4) .session-skeleton-line::after { animation-delay: 0.36s; } @keyframes session-skeleton-shimmer { 100% { transform: translateX(120%); } } @media (max-width: 768px) { .session-loading-state { padding: 12px 10px 18px; gap: 10px; } .session-skeleton-bubble { width: 86%; padding: 9px 10px 11px; border-radius: 9px; } .session-skeleton-bubble.is-user { width: 72%; } .session-skeleton-line { height: 9px; margin-top: 7px; } } @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; } .memory-modal-content, .tasks-modal-content, .preset-modal-content, #cookbook-modal .modal-content, #theme-popup, .doclib-modal-content, .gallery-modal-content { container-type: inline-size; } .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 opts out of the sticky header on mobile; the title bar scrolls away with the content instead of following. Background is inherited from the shared .modal-header rule so frosted/glass themes stay consistent. */ #cookbook-modal .modal-header { 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); } /* Headers with an optional Peek control already have the title's auto-margin grouping all right-side controls. A second auto-margin on the injected minimize button splits the group and makes it look different from the other modal headers. */ .modal-header .theme-opacity-wrap + .modal-minimize-btn { margin-left: 0 !important; } .close-btn, .modal-close { background:transparent; color:color-mix(in srgb, var(--fg) 58%, transparent); border:1px solid transparent; font-size:0; 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:999px; line-height:1; flex-shrink:0; position:relative; } .close-btn > svg, .modal-close > svg { display:none; } .close-btn::before, .modal-close::before { content:""; width:10px; height:10px; display:block; background:currentColor; -webkit-mask: url("data:image/svg+xml,%3Csvg width='10' height='10' viewBox='0 0 10 10' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2 2L8 8M8 2L2 8' stroke='black' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg width='10' height='10' viewBox='0 0 10 10' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2 2L8 8M8 2L2 8' stroke='black' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat; } .close-btn:hover, .modal-close:hover { background:color-mix(in srgb, var(--fg) 10%, transparent); border-color:color-mix(in srgb, var(--fg) 12%, transparent); color:var(--fg); } /* Minimize button — sits beside the close button on every modal */ .minimize-btn { background:transparent; color:color-mix(in srgb, var(--fg) 58%, transparent); border:1px solid transparent; font-size:0; font-weight:700; width:24px; height:24px; padding:0; display:inline-flex; align-items:center; justify-content:center; line-height:1; cursor:pointer; border-radius:999px; flex-shrink:0; margin-left:auto; /* push to the right so it docks next to .close-btn */ margin-right:4px; } .minimize-btn::before { content:""; width:9px; height:2px; border-radius:999px; background:currentColor; display:block; transform:translateY(2px); } .modal-minimize-btn.minimize-btn::before { content:none; display:none; } .minimize-btn:hover { background:color-mix(in srgb, var(--fg) 10%, transparent); border-color:color-mix(in srgb, var(--fg) 12%, transparent); color:var(--fg); } @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; } .email-attachment-warning-modal { z-index: 99999 !important; } .email-attachment-warning-modal .modal-content { position: relative; z-index: 100001; } .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); } #styled-confirm-cancel .styled-confirm-cancel-label { position:relative; top:2px; } .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); } .cookbook-gguf-delete-box { width:560px; max-width:92vw; } .cookbook-gguf-delete-list { display:flex; flex-direction:column; gap:6px; max-height:42vh; overflow:auto; padding:2px 2px 4px; } .cookbook-gguf-delete-row { display:grid; grid-template-columns:18px minmax(0,1fr); gap:7px 8px; align-items:start; padding:7px 8px; border:1px solid var(--border); border-radius:7px; background:color-mix(in srgb, var(--panel, var(--bg)) 92%, var(--fg) 8%); cursor:pointer; } .cookbook-gguf-delete-row:hover { border-color:color-mix(in srgb, var(--accent-primary, var(--fg)) 45%, var(--border)); } .cookbook-gguf-delete-cb { -webkit-appearance:none; appearance:none; width:8px !important; height:8px !important; min-width:8px; min-height:8px; padding:0; margin:4px 0 0; border:1px solid var(--border); border-radius:50%; background:transparent; box-sizing:content-box; cursor:pointer; transition:background 0.15s, border-color 0.15s, transform 0.12s; } .cookbook-gguf-delete-cb:hover { border-color:var(--accent, var(--red)); transform:scale(1.12); } .cookbook-gguf-delete-cb:checked { background:var(--accent, var(--red)); border-color:var(--accent, var(--red)); } .cookbook-gguf-delete-main, .cookbook-gguf-delete-path { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } .cookbook-gguf-delete-main { font-size:0.86rem; color:var(--fg); } .cookbook-gguf-delete-path { grid-column:2; margin-top:-2px; font-size:0.74rem; opacity:0.58; } /* 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-nav-btn.agent-working::after, body.chat-agent-working #scroll-bottom-btn.show::after { content: ''; position: absolute; inset: 0; border-radius: 10px; padding: 1px; background: conic-gradient( from 0deg, transparent 0deg, transparent 245deg, color-mix(in srgb, var(--accent, var(--red)) 78%, transparent) 315deg, transparent 360deg ); -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; opacity: 0.65; pointer-events: none; animation: scrollAgentWorkingRing 1.05s linear infinite; } @keyframes scrollAgentWorkingRing { to { transform: rotate(360deg); } } #scroll-bottom-btn.show { opacity:1; pointer-events:auto; } .scroll-nav-caret { display: inline-block; font-family: Arial, Helvetica, sans-serif; font-size: 16px; line-height: 1; transform: translateY(-1px); pointer-events: none; } @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); } .persona-display-name-row .vis-label { position: relative; top: -4px; } .persona-display-name-row .vis-switch { position: relative; left: 4px; } .md-attach-paperclip-icon { position: relative; top: 1px; } /* 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: var(--search-overlay-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; } .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; position: relative; } .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%; } #theme-bg-effect-color { position: relative; top: 2px; } .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; position: absolute; right: 6px; top: 50%; transform: translateY(-50%); } .color-row input[type="color"], .color-row input.cp-swatch-input { margin-left: auto; } /* Keep the harmony hex readout on the left, with the swatch dot anchored to the right like the other color controls. */ #theme-harmony-card #harmony-accent-hex { order: -1; margin-right: auto; } .color-row:has(.color-reset-btn.changed) input[type="color"], .color-row:has(.color-reset-btn.changed) input.cp-swatch-input { margin-right: 28px; } .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); } #memory-import-btn svg, #memory-export-btn svg { color: var(--accent, var(--red)); } .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); } @media (max-width: 768px) { /* The switch is too cramped beside the Font and Density selects on narrow screens; give Frosted a dedicated row instead. */ .theme-fd-row:first-of-type { flex-wrap: wrap; } .theme-fd-row:first-of-type #theme-frosted-group { flex: 1 1 100%; flex-direction: row; align-items: center; justify-content: space-between; min-width: 0; } } .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; } /* Customization is an editor, so keep its sections visually related while preserving the existing controls and their order. */ #theme-tab-customize { display: flex; flex-direction: column; gap: 10px; } #theme-tab-customize > .admin-card, #theme-tab-customize > .theme-adv-toggle, #theme-tab-customize > .theme-adv-section { margin-top: 0; margin-bottom: 0; } #theme-tab-customize > .admin-card { padding: 12px; } #theme-tab-customize > .admin-card > h2 { display: flex; align-items: center; min-height: 20px; margin: 0 0 10px; padding-bottom: 7px; border-bottom: 1px solid color-mix(in srgb, var(--border) 65%, transparent); font-size: 13px; } #theme-tab-customize .theme-custom { gap: 6px 14px; } #theme-tab-customize .color-row { min-height: 30px; padding: 3px 6px; box-sizing: border-box; border: 1px solid color-mix(in srgb, var(--border) 55%, transparent); background: color-mix(in srgb, var(--fg) 2%, transparent); } #theme-tab-customize .color-row label { font-size: 12px; } #theme-tab-customize .theme-adv-toggle { flex-shrink: 0; margin: 0; background: color-mix(in srgb, var(--fg) 2%, transparent); } #theme-tab-customize .theme-adv-section { padding: 10px 12px 2px; border: 1px solid color-mix(in srgb, var(--border) 75%, transparent); border-radius: 7px; background: color-mix(in srgb, var(--panel) 55%, transparent); } #theme-tab-customize .theme-adv-group { margin: 0; padding: 0 0 9px; border-bottom: 1px solid color-mix(in srgb, var(--border) 48%, transparent); } #theme-tab-customize .theme-adv-group + .theme-adv-group { padding-top: 9px; } #theme-tab-customize .theme-adv-group:last-of-type { border-bottom: 0; } #theme-tab-customize .theme-adv-group-label { margin-bottom: 6px; color: var(--red); opacity: 0.8; font-weight: 600; } #theme-tab-customize .theme-fd-row, #theme-tab-customize .theme-harmony-row { gap: 12px; margin-bottom: 10px; } #theme-tab-customize .theme-fd-row:last-child, #theme-tab-customize .theme-harmony-row:last-child { margin-bottom: 0; } #theme-tab-customize .theme-fd-group { min-width: 0; } #theme-tab-customize .theme-fd-select { min-height: 30px; } #theme-tab-customize #theme-harmony-card .theme-fd-label { font-size: 11px; } #theme-tab-customize .theme-save-row, #theme-tab-customize .theme-io-row { gap: 8px; } #theme-tab-customize #theme-reset-btn { margin-top: 8px; } @media (max-width: 560px) { #theme-tab-customize .theme-custom { grid-template-columns: 1fr; } #theme-tab-customize .theme-fd-row, #theme-tab-customize .theme-harmony-row { flex-direction: column; align-items: stretch; } #theme-tab-customize .theme-fd-group, #theme-tab-customize .theme-harmony-row .theme-fd-group { width: 100%; } } /* 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; } /* Keep the tall tool drawers on a shared mobile height ceiling. The final mobile drawer rule below makes this content-sized so shorter tools remain compact while tall ones leave a visible top gap. */ #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: 90vh !important; max-height: 90dvh !important; height: auto !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 use the same bottom-sheet sizing as the other tools. The shared mobile drawer rule below provides the top breathing room. */ #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: 90vh !important; max-height: 90dvh !important; height: auto !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; flex-wrap: wrap; padding: 2px 0; transition: margin-left 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.15s, border-color 0.15s; } .cmp-model-row-duplicate { margin-left: -4px; margin-right: -4px; padding: 4px; border-radius: 6px; background: color-mix(in srgb, var(--color-error) 8%, transparent); border: 1px solid color-mix(in srgb, var(--color-error) 20%, transparent); } .cmp-model-row-swap-target { margin-left: -4px; margin-right: -4px; padding: 4px; border-radius: 6px; background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent, var(--red)) 42%, transparent); } .cmp-model-row-swap-target .cmp-row-label { color: var(--accent, var(--red)); opacity: 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-model-meta { order: 3; flex: 1 0 calc(100% - 28px); margin-left: 28px; margin-top: -4px; display: flex; align-items: center; gap: 5px; min-width: 0; font-size: 10px; line-height: 1.2; } .cmp-model-meta-chip, .cmp-model-meta-warning, .cmp-model-meta-muted { display: inline-flex; align-items: center; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border-radius: 999px; padding: 1px 6px; letter-spacing: 0; } .cmp-model-meta-chip { color: color-mix(in srgb, var(--fg) 60%, transparent); background: color-mix(in srgb, var(--fg) 5%, transparent); border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); } .cmp-model-meta-warning { color: var(--color-error); background: color-mix(in srgb, var(--color-error) 10%, transparent); border: 1px solid color-mix(in srgb, var(--color-error) 24%, transparent); font-weight: 600; } .cmp-model-meta-muted { color: color-mix(in srgb, var(--fg) 36%, transparent); } .cmp-rm-btn { background: none; border: none; color: var(--accent, var(--red)); 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: -2px; order: 2; margin-left: auto; align-self: center; } .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: relative; grid-column: 2; grid-row: 1; top: -2px; 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-clear { display: inline-flex; align-items: center; gap: 4px; margin-left: 4px; border: 0; background: transparent; color: var(--fg); opacity: .55; padding: 3px 5px; font: inherit; font-size: 10px; cursor: pointer; } .cmp-eval-clear:hover { opacity: 1; } .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; padding: 12px; box-sizing: border-box; } .compare-probe-card { width: min(460px, calc(100% - 16px)); max-height: min(620px, calc(100dvh - 24px)); box-sizing: border-box; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 14px; display: flex; flex-direction: column; align-items: stretch; gap: 10px; min-width: 280px; box-shadow: 0 8px 28px rgba(0,0,0,0.28); overflow: hidden; font-family: inherit; } .compare-probe-title { font-size: 13px; font-weight: 400; color: var(--fg); } .compare-probe-title-label { font-weight: 600; } .compare-probe-title-status { font-weight: 400; } .compare-probe-list { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; min-width: 0; width: 100%; overflow-y: auto; } .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 { display: inline-flex; align-items: center; gap: 5px; min-height: 25px; padding: 3px 8px; background: transparent; color: var(--fg); border: 1px solid var(--border); border-radius: 5px; cursor: pointer; font-size: 10px; font-family: inherit; opacity: 0.78; transition: opacity 0.15s, border-color 0.15s, background 0.15s; white-space: nowrap; flex-shrink: 0; } .compare-probe-action-btn svg, .compare-probe-footer-btn svg { width: 12px; height: 12px; flex: 0 0 auto; color: var(--accent, var(--red)); } .compare-probe-action-btn:hover { opacity: 1; border-color: var(--accent, var(--red)); background: color-mix(in srgb, var(--fg) 5%, transparent); } .compare-probe-feedback { display: flex; flex-direction: column; gap: 5px; } .compare-probe-feedback:empty { display: none; } .compare-probe-detail { display: flex; align-items: center; gap: 6px; min-width: 0; padding: 6px 8px; font-size: 10px; background: color-mix(in srgb, var(--color-error, #f44) 6%, transparent); border-left: 2px solid color-mix(in srgb, var(--color-error, #f44) 72%, transparent); border-radius: 4px; } .compare-probe-detail-icon { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 14px; width: 14px; height: 14px; color: var(--accent, var(--red)); font-family: inherit; font-size: 11px; font-weight: 700; line-height: 1; } .compare-probe-detail-message { flex: 1 1 auto; min-width: 0; line-height: 1.35; color: color-mix(in srgb, var(--fg) 72%, transparent); overflow-wrap: anywhere; } .compare-probe-footer { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; margin-top: 2px; } .compare-probe-footer-btn { display: inline-flex; align-items: center; gap: 6px; min-height: 30px; padding: 5px 10px; font-family: inherit; font-size: 11px; font-weight: 600; line-height: 1; } .compare-probe-start-anyway, .compare-probe-start-anyway:hover:not(:disabled) { background: var(--accent, var(--red)); border: 1px solid var(--accent, var(--red)); color: var(--bg); } .compare-probe-start-anyway svg { color: currentColor; } .compare-probe-start-anyway span { font-weight: 700; } @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; position: relative; } .compare-mobile-tabs { display: none; } .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; } .compare-shuffle-notice { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0.94); z-index: 30; display: inline-flex; align-items: center; gap: 8px; padding: 10px 14px; border: 1px solid color-mix(in srgb, var(--red) 28%, var(--border)); border-radius: 8px; background: color-mix(in srgb, var(--panel, var(--bg)) 92%, transparent); color: var(--fg); box-shadow: 0 10px 30px color-mix(in srgb, #000 24%, transparent); font-size: 12px; font-weight: 700; line-height: 1; opacity: 0; pointer-events: none; transition: opacity 0.14s ease, transform 0.14s ease; } .compare-shuffle-notice.show { opacity: 1; transform: translate(-50%, -50%) scale(1); } .compare-shuffle-notice-icon { display: inline-flex; color: var(--red); animation: compare-shuffle-dice 0.56s ease both; } .compare-shuffle-notice-icon svg { display: block; width: 18px; height: 18px; } @keyframes compare-shuffle-dice { 0% { transform: rotate(-18deg) scale(0.9); } 55% { transform: rotate(380deg) scale(1.08); } 100% { transform: rotate(360deg) scale(1); } } /* 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; position: relative; transition: border-color 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease; } .compare-pane::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 2px; z-index: 2; pointer-events: none; background: transparent; transform-origin: left center; } .compare-pane.is-streaming { border-color: color-mix(in srgb, var(--accent, var(--red)) 34%, var(--border)); box-shadow: inset 0 1px 0 color-mix(in srgb, var(--accent, var(--red)) 12%, transparent); } .compare-pane.is-streaming::before { background: linear-gradient(90deg, transparent 0%, var(--accent, var(--red)) 35%, var(--red) 50%, var(--accent, var(--red)) 65%, transparent 100%); animation: compare-pane-progress 1.25s linear infinite; } .compare-pane.is-awaiting-input { border-color: color-mix(in srgb, var(--red) 42%, var(--border)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--red) 12%, transparent); } .compare-pane.is-awaiting-input::before { background: var(--red); animation: none; } .compare-pane.is-done { border-color: color-mix(in srgb, var(--green, #50fa7b) 20%, var(--border)); } .compare-pane.is-failed { border-color: color-mix(in srgb, var(--color-error) 42%, var(--border)); } .compare-pane.is-failed::before { background: var(--color-error); } @keyframes compare-pane-progress { from { transform: translateX(-100%); } to { transform: translateX(100%); } } .compare-pane .pane-header { display: flex; flex-direction: column; align-items: stretch; 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: visible; min-width: 0; flex-wrap: nowrap; } .pane-header-row { display: flex; align-items: center; width: 100%; min-width: 0; } .pane-header-primary { min-height: 24px; } .pane-header-secondary { min-height: 20px; } .pane-primary-actions, .pane-stats { display: flex; align-items: center; gap: 4px; min-width: 0; } .pane-primary-actions { margin-left: auto; flex: 0 0 auto; } .pane-stats { flex: 1 1 auto; overflow: hidden; } .compare-header-bar button:hover, .compare-header-bar button:focus-visible { background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent) !important; border-color: color-mix(in srgb, var(--accent, var(--red)) 55%, var(--border)) !important; color: var(--fg) !important; opacity: 1 !important; outline: none; } .compare-header-bar button:active { background: color-mix(in srgb, var(--accent, var(--red)) 24%, transparent) !important; transform: scale(0.96); } .compare-more-wrap { position: relative; display: inline-flex; } .compare-more-btn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 24px; padding: 0 !important; border: 1px solid transparent; border-radius: 5px; background: none; color: var(--fg); cursor: pointer; opacity: 0.5; transition: opacity .15s, background .15s; } .compare-header-bar .compare-close-btn { width: 24px; height: 24px; min-width: 24px; padding: 0; border-radius: 999px; font-size: 0; display: inline-flex; align-items: center; justify-content: center; } .compare-header-bar .compare-close-btn:hover, .compare-header-bar .compare-close-btn:focus-visible { background: color-mix(in srgb, var(--fg) 10%, transparent) !important; border-color: transparent !important; color: var(--fg) !important; opacity: 1 !important; outline: none; } .compare-header-bar .compare-more-btn:hover, .compare-header-bar .compare-more-btn:focus-visible, .compare-header-bar .compare-more-btn[aria-expanded="true"] { background: color-mix(in srgb, var(--fg) 10%, transparent) !important; border-color: transparent !important; color: var(--fg) !important; opacity: 1 !important; outline: none; } .compare-more-menu { position: absolute; top: calc(100% + 5px); right: 0; z-index: 10002; display: none; flex-direction: column; gap: 3px; min-width: 132px; padding: 5px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel, var(--bg)); box-shadow: 0 8px 24px rgba(0,0,0,.3); } .compare-more-menu.is-open { display: flex; } .compare-more-menu > .compare-export-wrap, .compare-more-menu > button { width: 100%; display: flex; align-items: center; } .compare-more-menu #compare-export-btn, .compare-more-menu #compare-shuffle-btn, .compare-more-menu #compare-check-btn, .compare-more-menu #compare-add-menu-btn { width: 100%; min-height: 32px; height: 32px; box-sizing: border-box; justify-content: flex-start; gap: 7px; padding: 7px 9px !important; border: 1px solid transparent !important; border-radius: 6px; background: transparent !important; color: var(--fg) !important; font-size: 11px; font-weight: 500; opacity: .72; transition: background .12s ease, opacity .12s ease, color .12s ease; } .compare-more-menu #compare-export-btn:hover, .compare-more-menu #compare-shuffle-btn:hover, .compare-more-menu #compare-check-btn:hover, .compare-more-menu #compare-add-menu-btn:hover, .compare-more-menu #compare-export-btn:focus-visible, .compare-more-menu #compare-shuffle-btn:focus-visible, .compare-more-menu #compare-check-btn:focus-visible, .compare-more-menu #compare-add-menu-btn:focus-visible { opacity: 1; background: color-mix(in srgb, var(--red) 10%, transparent) !important; border-color: transparent !important; color: var(--fg) !important; outline: none; } .compare-more-menu #compare-export-btn svg, .compare-more-menu #compare-shuffle-btn svg, .compare-more-menu #compare-check-btn svg, .compare-more-menu #compare-add-menu-btn svg { flex: 0 0 auto; opacity: .65; } .compare-more-menu #compare-export-btn span, .compare-more-menu #compare-shuffle-btn span, .compare-more-menu #compare-check-btn span, .compare-more-menu #compare-add-menu-btn span { font-size: 11px !important; margin-left: 0 !important; color: var(--fg) !important; opacity: 1 !important; font-weight: 500 !important; } .compare-more-menu #compare-export-btn, .compare-more-menu #compare-shuffle-btn, .compare-more-menu #compare-check-btn, .compare-more-menu #compare-add-menu-btn { color: var(--fg) !important; opacity: 1 !important; font-weight: 500 !important; } .compare-more-menu #compare-export-btn svg, .compare-more-menu #compare-shuffle-btn svg, .compare-more-menu #compare-check-btn svg, .compare-more-menu #compare-add-menu-btn svg { color: var(--fg) !important; opacity: .65 !important; } .compare-export-item:hover, .compare-export-item:focus-visible { background: color-mix(in srgb, var(--red) 10%, transparent) !important; border-color: transparent !important; outline: none; } .compare-export-item { display: flex; align-items: center; gap: 8px; } .compare-export-item > svg { flex: 0 0 auto; opacity: .65; } .compare-export-menu .compare-export-item, .compare-export-menu .compare-export-item > span { color: var(--fg) !important; opacity: 1 !important; } .compare-add-flap { position: absolute; z-index: 20; right: -8px; top: 50%; width: 34px; min-width: 34px; height: 30px; box-sizing: border-box; padding: 0 !important; display: inline-flex; flex-direction: row; align-items: center; justify-content: center; gap: 0; border: 1px solid var(--border); border-radius: 0 7px 7px 0; background: var(--panel, var(--bg)); color: var(--fg); cursor: pointer; opacity: .7; box-shadow: 3px 2px 9px color-mix(in srgb, var(--fg) 12%, transparent); transform: translate(0, -50%); transition: width .18s ease, opacity .15s, background .15s, box-shadow .15s; } .compare-add-flap:hover, .compare-add-flap:focus-visible { width: 70px; opacity: 1; background: color-mix(in srgb, var(--fg) 8%, var(--panel)); box-shadow: 4px 3px 12px color-mix(in srgb, var(--fg) 18%, transparent); outline: none; } .compare-add-flap span { max-width: 0; overflow: hidden; white-space: nowrap; margin-left: 0 !important; font-size: 10px; opacity: 0; transition: max-width .18s ease, margin-left .18s ease, opacity .12s; } .compare-add-flap:hover span, .compare-add-flap:focus-visible span { max-width: 32px; margin-left: 3px !important; opacity: 1; } .compare-add-flap svg { flex-shrink: 0; position: relative; left: -3px; } @media (max-width: 768px) { .compare-add-flap { top: 8px; right: 8px; width: auto; min-width: 32px; height: 32px; padding: 5px 8px !important; flex-direction: row; transform: none; border-right: 1px solid var(--border); border-radius: 6px; } .compare-add-flap span { opacity: 1; } } .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; } .pane-mode-badge { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; height: 18px; padding: 0 6px; border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); border-radius: 999px; background: color-mix(in srgb, var(--fg) 3%, var(--bg)); color: color-mix(in srgb, var(--fg) 76%, transparent); font-size: 9px; font-weight: 600; line-height: 1; letter-spacing: 0; white-space: nowrap; } .pane-mode-dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; opacity: 0.75; flex: 0 0 auto; } .pane-mode-detail { opacity: 0.52; font-weight: 500; } .pane-mode-badge.pane-mode-icon-only { width: 20px; padding: 0; justify-content: center; } .pane-mode-settings { opacity: 0.65; cursor: pointer; } .pane-settings-btn { width: 20px; height: 20px; justify-content: center; } .pane-settings-btn[aria-expanded="true"] { opacity: 1; background: color-mix(in srgb, var(--fg) 9%, transparent); } .response-metrics[data-action="settings"] { cursor: pointer; } .response-thinking-state { color: color-mix(in srgb, var(--fg) 55%, transparent); } .pane-mode-settings:hover, .pane-mode-settings:focus-visible, .pane-mode-settings[aria-expanded="true"] { opacity: 1; color: var(--fg); background: color-mix(in srgb, var(--fg) 9%, var(--bg)); border-color: color-mix(in srgb, var(--accent, var(--fg)) 45%, var(--border)); outline: none; } .pane-mode-icon, .pane-mode-icon svg { display: block; width: 12px; height: 12px; } .pane-mode-agent { color: color-mix(in srgb, var(--fg) 76%, transparent); background: color-mix(in srgb, var(--fg) 3%, var(--bg)); border-color: color-mix(in srgb, var(--border) 70%, transparent); } .pane-mode-search { color: color-mix(in srgb, var(--accent, var(--red)) 82%, var(--fg)); background: color-mix(in srgb, var(--accent, var(--red)) 10%, var(--bg)); border-color: color-mix(in srgb, var(--accent, var(--red)) 28%, var(--border)); } .pane-mode-research { color: color-mix(in srgb, #36c48f 82%, var(--fg)); background: color-mix(in srgb, #36c48f 10%, var(--bg)); border-color: color-mix(in srgb, #36c48f 28%, var(--border)); } .compare-check-label { position: relative; top: -2px; font-size: 11px; margin-left: 3px; } .compare-check-icon { position: relative; top: 1px; } #compare-check-btn > .spinner-whirlpool { position: relative; top: -2px; transform: none; } .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%; box-sizing: border-box; padding: 8px 10px; font-size: 11px; line-height: 1.3; text-align: left; background: none; border: none; border-radius: 6px; color: var(--fg); cursor: pointer; transition: background 0.12s ease; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .pane-model-item:hover, .pane-model-item:focus-visible { background: color-mix(in srgb, var(--red) 10%, transparent); outline: none; } .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; } .pane-summary { display: inline-flex; align-items: center; position: relative; top: 1px; min-height: 16px; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0 5px; border-radius: 999px; background: color-mix(in srgb, var(--fg) 4%, transparent); color: color-mix(in srgb, var(--fg) 58%, transparent); font-size: 9px; font-weight: 500; line-height: 1; font-variant-numeric: tabular-nums; cursor: pointer; transition: max-width .15s ease, white-space .15s ease; } .pane-summary:empty { display: none; } .pane-stats-popup { position: fixed; z-index: 10040; width: 210px; padding: 6px; background: var(--panel, var(--bg)); color: var(--fg); border: 1px solid var(--border); border-radius: 7px; box-shadow: 0 8px 24px rgba(0,0,0,.3); font-size: 11px; } .pane-stats-popup-title { padding: 4px 6px 7px; font-weight: 600; border-bottom: 1px solid var(--border); } .pane-stats-popup-row { display: flex; justify-content: space-between; gap: 12px; padding: 6px; } .pane-stats-popup-row span { color: color-mix(in srgb, var(--fg) 55%, transparent); } .pane-stats-popup-row strong { font-weight: 500; font-variant-numeric: tabular-nums; text-align: right; } /* Metrics stay on the lower row even in dense comparisons. */ .compare-grid[data-cols="4"] .pane-timer, .compare-grid[data-cols="5"] .pane-timer, .compare-grid[data-cols="6"] .pane-timer { width: auto; order: initial; margin-top: 0; padding-bottom: 0; padding-left: 0; } .pane-finish-badge { font-weight: 600; color: var(--green, #50fa7b); position: relative; top: 2px; } .compare-pane.winner .pane-header { background: color-mix(in srgb, var(--green, #50fa7b) 12%, transparent); border-bottom-color: color-mix(in srgb, var(--green, #50fa7b) 30%, var(--border)); } .compare-pane.winner .pane-title { color: var(--green, #50fa7b); } .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-type-tabs .compare-mode-tab > svg, .compare-type-current > svg { color: var(--accent, var(--red)); } .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: 38px; 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 > svg { width: 13px; height: 13px; flex: 0 0 13px; } .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-score-label { position: relative; top: -2px; display: inline-block; } .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(--accent, var(--red)) 60%, var(--border)); background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent); color: var(--fg); opacity: 1; font-weight: 600; } .compare-rematch-btn:hover { border-color: var(--accent, var(--red)); background: color-mix(in srgb, var(--accent, var(--red)) 24%, transparent); } /* 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: 10px; max-height: 300px; overflow-y: auto; min-width: 220px; box-shadow: 0 8px 24px rgba(0,0,0,0.3), 0 0 0 1px color-mix(in srgb, var(--fg) 5%, transparent); padding: 6px; backdrop-filter: blur(12px); animation: dropdown-in 0.15s ease-out; } .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(--accent, var(--red)); border-color: var(--accent, var(--red)); background: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent); } .compare-blind-toggle.active > svg { color: var(--accent, var(--red)); } .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: var(--accent, var(--red)); border-color: var(--accent, var(--red)); background: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent); } @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; } .compare-pane .pane-header { align-items: stretch; row-gap: 3px; } .compare-pane .pane-title-btn { flex: 1 1 auto; width: auto; padding-right: 2px; } /* Compare is already visibly in chat mode; the Chat speech-bubble badge adds a redundant second row on narrow screens. */ .compare-pane .pane-mode-chat { display: none; } .compare-pane .pane-timer, .compare-pane .pane-summary, .compare-pane .pane-finish-badge, .compare-pane .pane-mode-badge, .compare-pane .pane-actions { order: 1; align-self: center; } .compare-pane .pane-finish-badge { top: 0; } .compare-pane .pane-summary { max-width: 110px; } .compare-pane .pane-mode-detail { display: none; } .compare-grid[data-cols="4"] .compare-pane .pane-timer, .compare-grid[data-cols="5"] .compare-pane .pane-timer, .compare-grid[data-cols="6"] .compare-pane .pane-timer { width: auto; order: 1; margin-top: 0; padding-bottom: 0; padding-left: 0; } .compare-pane .pane-actions { margin-left: auto; } /* 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: 11px; 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; } a.chat-link[href^="#email-"] { display: inline; padding: 1px 5px; border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 34%, transparent); border-radius: 5px; background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent); color: var(--fg); font-weight: 600; border-bottom-style: solid; } a.chat-link[href^="#email-"]:hover { opacity: 1; background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent); border-color: color-mix(in srgb, var(--accent, var(--red)) 55%, transparent); } a.chat-link[href^="#notes-more-"], a.chat-link[href^="#skills-more-"], a.chat-link[href^="#memories-more-"], a.chat-link[href^="#events-more-"], a.chat-link[href^="#sessions-more-"] { color: var(--muted); border-bottom: 1px dotted color-mix(in srgb, var(--muted) 55%, transparent); font-size: 11px; font-weight: 500; } a.chat-link[href^="#notes-more-"]:hover, a.chat-link[href^="#skills-more-"]:hover, a.chat-link[href^="#memories-more-"]:hover, a.chat-link[href^="#events-more-"]:hover, a.chat-link[href^="#sessions-more-"]:hover { color: var(--fg); opacity: 1; border-bottom-style: solid; } .more-list-expanded { display: block; max-height: 0; overflow: hidden; opacity: 0; color: inherit; font: inherit; line-height: inherit; transition: max-height 220ms ease, opacity 160ms ease; } .more-list-expanded.is-open { opacity: 1; } .more-list-expanded-content { color: inherit; font: inherit; line-height: inherit; } /* 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: var(--bg); 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); } .msg a.is-loading { opacity: 0.65; border-bottom-color: var(--accent, #bd93f9); cursor: progress; } /* Tables */ .msg table { border-collapse: collapse; margin: 0.5em 0; font-size: 0.9em; display: block; width: max-content; min-width: 100%; max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; table-layout: 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; min-width: 9ch; word-break: normal; overflow-wrap: break-word; } .msg td { padding: 5px 12px; border: 1px solid var(--border); min-width: 9ch; word-break: normal; overflow-wrap: break-word; } /* 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 !important; 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(--sidebar-bg, var(--panel)); 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(--sidebar-bg, var(--panel)); 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: 4px; 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; } @media (max-width: 768px) { .thumb.thumb-image .thumb-img { cursor: pointer; touch-action: manipulation; } } .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); } .attach-crop-overlay { position: fixed; inset: 0; z-index: var(--attach-crop-z, 100001); background: rgba(0, 0, 0, 0.72); display: flex; align-items: center; justify-content: center; padding: 14px; box-sizing: border-box; } .attach-crop-panel { width: min(94vw, 520px); max-height: min(86vh, 720px); background: var(--panel, #111); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 18px 50px rgba(0,0,0,0.45); overflow: hidden; display: flex; flex-direction: column; } .attach-crop-stage { position: relative; min-height: 280px; height: min(66vh, 560px); background: #050505; display: flex; align-items: center; justify-content: center; overflow: hidden; touch-action: none; } .attach-crop-img { max-width: 100%; max-height: 100%; object-fit: contain; user-select: none; pointer-events: none; } .attach-crop-box { position: absolute; border: 2px solid var(--accent, var(--red)); box-shadow: 0 0 0 9999px rgba(0,0,0,0.42); cursor: move; touch-action: none; box-sizing: border-box; } .attach-crop-box::before, .attach-crop-box::after { content: ''; position: absolute; inset: 33.333% 0 auto; border-top: 1px solid rgba(255,255,255,0.38); } .attach-crop-box::after { inset: 66.666% 0 auto; } .attach-crop-handle { position: absolute; right: -9px; bottom: -9px; width: 20px; height: 20px; border-radius: 50%; background: var(--accent, var(--red)); border: 2px solid var(--bg); box-sizing: border-box; cursor: nwse-resize; } .attach-crop-actions { display: flex; gap: 8px; padding: 10px; background: color-mix(in srgb, var(--bg) 78%, transparent); } .attach-crop-btn { flex: 1; height: 34px; border: 1px solid var(--border); border-radius: 5px; background: var(--bg); color: var(--fg); font: inherit; font-size: 12px; } .attach-crop-primary { background: var(--accent, var(--red)); color: #fff; border-color: var(--accent, var(--red)); } @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 5px; font-size: 0.95em; 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; } /* Widen chat area via settings toggle */ body.fullwidth-chat .chat-history { padding-left: 0 !important; padding-right: 12px !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 > .token-new { display: none; } .thinking-header > div:last-child { flex-shrink: 0; } .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 { display: block; 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; } .research-context-continue-hint { display: inline-flex; align-items: center; gap: 4px; margin: 5px 2px 0; color: var(--fg-muted, var(--fg)); font-size: 10px; opacity: 0.55; } .research-context-continue-hint svg { flex: 0 0 auto; 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; } .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: none; } .session-run-state { flex: 0 0 auto; margin-left: 5px; font-size: 10px; line-height: 1; color: var(--fg-dim, #888); opacity: 0.9; } .session-run-state.is-working { color: var(--accent, var(--red)); } .session-run-state .session-run-whirlpool { width: 12px !important; height: 12px !important; margin: 0 !important; display: inline-flex; align-items: center; } .session-run-state .session-run-whirlpool .ai-spinner-whirlpool { display: inline-flex; width: 12px; height: 12px; } .session-run-state.is-done { color: var(--color-success, #4caf50); font-weight: 600; } .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: 5px; 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; } .dropdown-divider { height: 1px; margin: 3px 6px; background: color-mix(in srgb, var(--border) 60%, transparent); } .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; } .agent-thread.has-ask-user-bottom::before { bottom: -12px; } /* 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; /* Centre the 6px dot on the rail: the rail sits at left:5px + 2px wide, so its centre is 6px from the thread's left edge and the dot's left edge belongs at 3px — i.e. 3px - padding-left. Keep this in step with .agent-thread-dot and with the mobile padding override below. */ left: -19px; 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; } /* Supervisor ladder cards — same chrome as tool cards but tinted so the user can tell at a glance "this is the agent recovering" vs "this is the agent doing work". Stop rung gets the red accent. */ .agent-thread-node.supervisor-step .agent-thread-tool { color: color-mix(in srgb, var(--accent, #c08a3e) 80%, var(--fg)); font-style: italic; } .agent-thread-node.supervisor-step .agent-thread-dot { background: color-mix(in srgb, var(--accent, #c08a3e) 60%, transparent); } .agent-thread-node.supervisor-step[data-rung="stop"] .agent-thread-tool { color: var(--red, #d65a5a); } .agent-thread-node.supervisor-step[data-rung="stop"] .agent-thread-dot { background: color-mix(in srgb, var(--red, #d65a5a) 60%, transparent); } .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-tool-icon { width: 14px; height: 14px; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 14px; color: color-mix(in srgb, var(--fg) 64%, transparent); opacity: 0.85; } .agent-thread-tool-icon svg { display: block; width: 12px; height: 12px; } .agent-thread-tool-icon-link { color: inherit; text-decoration: none; cursor: pointer; } .agent-thread-tool-icon-link:hover { color: var(--red); opacity: 1; } .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-header-link { width: 17px; height: 17px; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 17px; margin-left: -3px; color: var(--red); opacity: 0.72; text-decoration: none; border: 0; background: transparent; } .agent-thread-header-link:hover { opacity: 1; color: var(--red); } .agent-thread-header-link svg { display: block; } .agent-thread-header-link-calendar { transform: translateY(-2px); } .msg-ai .body a[href^="#event-"], .msg-ai .body a[href^="#email-"], .msg-ai .body a[href^="#document-"], .msg-ai .body a[href^="#note-"], .msg-ai .body a[href^="#task-"], .msg-ai .body a[href^="#skill-"], .msg-ai .body a[href^="#memory-"], .msg-ai .body a[href^="#session-"] { display: inline-flex; align-items: baseline; max-width: 100%; padding: 1px 5px; border-radius: 5px; background: color-mix(in srgb, var(--red) 12%, transparent); color: var(--red); font-weight: 600; text-decoration: none; box-decoration-break: clone; -webkit-box-decoration-break: clone; } .msg-ai .body a[href^="#event-"], .msg-ai .body a[href^="#note-"], .msg-ai .body a[href^="#skill-"], .msg-ai .body a[href^="#memory-"], .msg-ai .body a[href^="#session-"] { font-size: 11px; line-height: 1.35; } .msg-ai .body p:has(> .emoji:first-child):has(> a.chat-link[href^="#note-"]) { margin-top: 0.15em; margin-bottom: 0.15em; line-height: 1.35; } .msg-ai .body a[href^="#event-"]:hover, .msg-ai .body a[href^="#email-"]:hover, .msg-ai .body a[href^="#document-"]:hover, .msg-ai .body a[href^="#note-"]:hover, .msg-ai .body a[href^="#task-"]:hover, .msg-ai .body a[href^="#skill-"]:hover, .msg-ai .body a[href^="#memory-"]:hover, .msg-ai .body a[href^="#session-"]:hover { background: color-mix(in srgb, var(--red) 18%, transparent); text-decoration: underline; } .agent-thread-status { font-size: 0.85em; opacity: 0.5; } .agent-thread-chevron { width: 12px; height: 12px; flex: 0 0 12px; display: inline-grid; place-items: center; opacity: 0.4; } .agent-thread-chevron::before { content: ""; width: 0; height: 0; border-top: 4px solid transparent; border-bottom: 4px solid transparent; border-left: 6px solid currentColor; transform-origin: 50% 50%; transition: transform 0.2s ease; } .agent-thread-node.open .agent-thread-chevron::before { transform: rotate(90deg); } .agent-thread-node.browser-preview-node .agent-thread-chevron { display: none; } .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; } .agent-thread-summary { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; margin: 4px 0; padding: 4px 8px; border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 26%, var(--border)); border-radius: 6px; background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent); color: color-mix(in srgb, var(--fg) 84%, var(--accent, var(--red))); font-size: 12px; font-variant-numeric: tabular-nums; line-height: 1.35; } .agent-thread-summary-link { cursor: pointer; text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .agent-thread-summary-link:hover { color: var(--accent, var(--red)); border-color: color-mix(in srgb, var(--accent, var(--red)) 42%, var(--border)); background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent); } .agent-image-progress { margin: 6px 0 0; } .agent-image-progress-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 11px; line-height: 1.2; color: color-mix(in srgb, var(--fg) 72%, transparent); } .agent-image-progress-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .agent-image-progress-value { flex-shrink: 0; font-variant-numeric: tabular-nums; color: color-mix(in srgb, var(--accent, var(--red)) 75%, var(--fg)); } .agent-image-progress-track { display: none !important; } .private-browser-preview { width: 100%; max-height: min(300px, 36vh); margin: 6px 0 2px; display: flex; flex-direction: column; overflow: hidden; border: 1px solid color-mix(in srgb, var(--red) 20%, transparent); border-radius: 8px; background: color-mix(in srgb, var(--red) 5%, transparent); color: var(--fg); box-shadow: none; } .private-browser-preview-header { height: 32px; min-height: 32px; display: flex; align-items: center; gap: 8px; padding: 0 6px 0 10px; border-bottom: 1px solid color-mix(in srgb, var(--red) 15%, transparent); background: color-mix(in srgb, var(--red) 10%, transparent); font-size: 0.85em; font-weight: 500; cursor: pointer; } .private-browser-preview-title { flex-shrink: 0; color: var(--red); font-size: inherit; font-weight: inherit; line-height: 1; letter-spacing: 0; } .private-browser-preview-status { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: color-mix(in srgb, var(--fg) 68%, transparent); font-size: inherit; line-height: 1; } .private-browser-preview-fold { width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; position: relative; left: 2px; flex-shrink: 0; padding: 0; border: 0; border-radius: 0; background: transparent; color: var(--red); cursor: pointer; font-size: 0.95em; font-family: inherit; font-weight: 500; line-height: 1; text-shadow: none; -webkit-text-stroke: 0; } .private-browser-preview-fold:hover { background: transparent; color: var(--red); } .private-browser-preview-frame { position: relative; min-height: 130px; display: flex; align-items: center; justify-content: center; overflow: hidden; background: color-mix(in srgb, #000 72%, var(--bg)); box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 8%, transparent), inset 0 0 0 1px color-mix(in srgb, #000 35%, transparent), 0 8px 18px color-mix(in srgb, #000 18%, transparent); } .private-browser-preview[data-browser-url] .private-browser-preview-frame { cursor: zoom-in; } .private-browser-preview[data-browser-url] .private-browser-preview-frame::after { content: ""; position: absolute; inset: 0; pointer-events: none; opacity: 0.55; transition: opacity 150ms ease; background: radial-gradient(circle at center, transparent 42%, color-mix(in srgb, #000 30%, transparent) 100%), color-mix(in srgb, var(--bg) 10%, transparent); } .private-browser-preview[data-browser-url] .private-browser-preview-frame:hover::after { opacity: 0.9; } .private-browser-preview.folded .private-browser-preview-frame { display: none; } .private-browser-preview.folded .private-browser-preview-header { border-bottom: 0; } .private-browser-preview-frame img { width: 100%; height: auto; max-height: calc(min(300px, 36vh) - 32px); display: block; object-fit: contain; transform: scale(1.1); transform-origin: center center; animation: private-browser-image-in 180ms ease-out both; } .private-browser-preview-frame img:not([src]), .private-browser-preview-frame img[src=""] { display: none; } .private-browser-preview-empty { padding: 22px 14px; width: auto; display: inline-flex; align-items: center; justify-content: center; gap: 7px; color: color-mix(in srgb, var(--fg) 48%, transparent); font-size: 11px; line-height: 1.35; } .private-browser-preview-spinner { width: 13px; height: 13px; flex: 0 0 13px; display: inline-flex; align-items: center; justify-content: center; } .private-browser-preview-spinner .spinner-whirlpool, .private-browser-preview-spinner .ai-spinner-whirlpool { margin: 0 !important; } @keyframes private-browser-image-in { from { opacity: 0; transform: scale(1.08); } to { opacity: 1; transform: scale(1.1); } } @media (max-width: 700px) { .private-browser-preview-frame img { max-height: calc(36vh - 32px); } } /* 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; } /* padding-left drops to 18px here, so the terminating dot needs its own offset (3px - 18px) to stay centred on the rail. */ .agent-thread:not(.has-bottom) .agent-thread-node.open:last-child::after { left: -15px; } } /* ===== 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; } /* File-write diff — neutral chrome (not the red error tint) + colored lines */ .agent-tool-diff { background: color-mix(in srgb, var(--fg) 4%, transparent); border-color: color-mix(in srgb, var(--fg) 18%, transparent); } .agent-tool-diff summary { color: var(--fg); background: color-mix(in srgb, var(--fg) 7%, transparent); border-bottom-color: color-mix(in srgb, var(--fg) 12%, transparent); } .agent-tool-diff .diff-stat { font-weight: 600; opacity: 0.7; font-family: var(--mono, monospace); } /* Collapsed diff summary: filename + +adds/−dels (theme green/red). */ .agent-tool-diff summary { display: flex; align-items: center; gap: 8px; } .agent-tool-diff .diff-file { font-family: var(--mono, monospace); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .agent-tool-diff .diff-summary-stats { margin-left: auto; font-family: var(--mono, monospace); font-weight: 600; flex-shrink: 0; } .agent-tool-diff .diff-summary-stats .diff-stat-add { color: var(--green, #2ecc71); } .agent-tool-diff .diff-summary-stats .diff-stat-del { color: var(--red, #e74c3c); } .agent-tool-diff .diff-summary-stats .diff-stat-new { color: var(--accent, var(--red)); opacity: 0.85; } .diff-pre { margin: 0; padding: 8px 10px; overflow-x: auto; font-family: var(--mono, monospace); font-size: 0.82em; line-height: 1.45; } .diff-pre span { display: block; white-space: pre; } .diff-pre .diff-add { background: color-mix(in srgb, #2ecc71 22%, transparent); } .diff-pre .diff-del { background: color-mix(in srgb, #e74c3c 22%, transparent); } .diff-pre .diff-hunk { color: var(--accent); opacity: 0.85; } .diff-pre .diff-meta { opacity: 0.55; } .diff-pre .diff-ctx { opacity: 0.8; } /* 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); } /* Compare pane close controls use the same shared circular close treatment as modal/title close buttons. */ .compare-pane .pane-close-btn.close-btn { width: 24px; height: 24px; min-width: 24px; padding: 0; border: 1px solid transparent; border-radius: 999px; background: transparent; color: color-mix(in srgb, var(--fg) 58%, transparent); font-size: 0; opacity: 0.5; display: inline-flex; align-items: center; justify-content: center; transition: background 0.15s, color 0.15s, opacity 0.15s; } .compare-pane .pane-close-btn.close-btn:hover, .compare-pane .pane-close-btn.close-btn:focus-visible { background: color-mix(in srgb, var(--fg) 10%, transparent); border-color: color-mix(in srgb, var(--fg) 12%, transparent); color: var(--fg); opacity: 1; outline: none; } .compare-pane .pane-action-btn[data-action="expand"] { width: 24px; height: 24px; min-width: 24px; padding: 0; border: 1px solid transparent; border-radius: 999px; justify-content: center; transition: background 0.15s, color 0.15s, opacity 0.15s; } .compare-pane .pane-action-btn[data-action="expand"]:hover, .compare-pane .pane-action-btn[data-action="expand"]:focus-visible { background: color-mix(in srgb, var(--fg) 10%, transparent); border-color: color-mix(in srgb, var(--fg) 12%, transparent); color: var(--fg); opacity: 1; outline: none; } /* ============================================ */ /* 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.68em; 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; }