mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-06 06:52:20 +02:00
Integrate lab fff55a78 into PR #40 (cc25d5ba). Lab's modular email backend/frontend, modular settings, split stylesheets (static/style.css stays deleted), procfs compatibility, and request-scoped TurnContract authority win; PR #40's routing classifiers, editor/email/task features, and style.css changes are ported into lab's module and stylesheet homes. Integration fixes: - settings/api.js imports ui.js under its canonical versioned URL - browser observations keep legacy CAPTCHA/access-block evidence - artifact turns do not re-trigger broad-web research recovery - env reference documents PR test-tool variables; page regenerated PR #40 defects surfaced by lab gates and fixed here: - web_fetch generic schema drops top-level anyOf (OpenAI contract); the compact preview contract still requires url or urls - get_weather registered as a brokered network read - new lazy editor modules precached for offline use - SearXNG pin mirrored into GPU standalone compose files - image model picker again skips offline endpoints Tests updated where PR #40 changed behaviour on purpose, and PR tests moved onto lab's document_source helpers.
7843 lines
264 KiB
CSS
7843 lines
264 KiB
CSS
/* 01-agent-chat - part of the former static/style.css.
|
||
*
|
||
* The stylesheet is split by area, but the split is mechanical: rules were
|
||
* moved verbatim and NOT regrouped. The numeric prefix is the load order, and
|
||
* that order is what preserves the cascade. A rule sits in the file its
|
||
* original position fell into, so an area's rules are not all in its file and
|
||
* a file is not only that area's rules.
|
||
*
|
||
* Do not reorder the <link> tags in static/index.html, and do not move a rule
|
||
* between files: with 1,151 redeclarations and 1,886 !important in this
|
||
* cascade, either can change which declaration wins.
|
||
*
|
||
* static/index.html and the PRECACHE list in static/sw.js must agree on this
|
||
* order. tests/test_static_stylesheet_manifest.py and
|
||
* tests/test_css_computed_style_snapshot.py fail if that drifts.
|
||
*/
|
||
#cookbook-modal .modal-content { height: 94vh; max-height: 94vh; }
|
||
.pdf-export-overlay .modal-content { max-height: 86vh; }
|
||
|
||
/* ── Background Patterns ── */
|
||
|
||
:root { --bg-effect-intensity: 1; }
|
||
|
||
/* Canvas-based effects — single source of truth for intensity */
|
||
#synapse-canvas, #rain-canvas, #constellations-canvas,
|
||
#perlin-flow-canvas, #petals-canvas, #sparkles-canvas,
|
||
#embers-canvas, #starfield-depth-canvas, #ascii-fireflies-background {
|
||
opacity: var(--bg-effect-intensity, 1);
|
||
}
|
||
|
||
body.bg-pattern-dots {
|
||
background-image: radial-gradient(color-mix(in srgb, var(--bg-effect-color, var(--fg)) calc(5% * var(--bg-effect-intensity, 1)), transparent) 1px, transparent 1px);
|
||
background-size: 20px 20px; background-attachment: fixed;
|
||
}
|
||
body.bg-pattern-synapse {
|
||
/* CSS grid as base, canvas pulses overlay */
|
||
background-image: linear-gradient(color-mix(in srgb, var(--bg-effect-color, var(--fg)) calc(3.5% * var(--bg-effect-intensity, 1)), transparent) 1px, transparent 1px),
|
||
linear-gradient(90deg, color-mix(in srgb, var(--bg-effect-color, var(--fg)) calc(3.5% * var(--bg-effect-intensity, 1)), transparent) 1px, transparent 1px);
|
||
background-size: 24px 24px; background-attachment: fixed;
|
||
}
|
||
body.memory-disabled.bg-pattern-synapse {
|
||
background-image: none;
|
||
}
|
||
body.memory-disabled #synapse-canvas {
|
||
display: none;
|
||
}
|
||
body.bg-pattern-perlin-flow,
|
||
body.bg-pattern-petals,
|
||
body.bg-pattern-sparkles {
|
||
/* canvas-only backgrounds */
|
||
}
|
||
body.bg-pattern-starfield-depth,
|
||
body.bg-pattern-ascii-fireflies {
|
||
/* rendered by their fixed background layer */
|
||
}
|
||
#ascii-fireflies-background {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 0;
|
||
box-sizing: border-box;
|
||
margin: 0;
|
||
padding: 14px 16px;
|
||
overflow: hidden;
|
||
pointer-events: none;
|
||
white-space: pre;
|
||
color: var(--bg-effect-color, var(--fg));
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||
font-size: 12px;
|
||
line-height: 1;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
|
||
/* ── Layout ── */
|
||
|
||
/* Top bar — session meta + incognito, single row */
|
||
.chat-top-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
position: relative;
|
||
z-index: 2;
|
||
padding: 5px 0 0;
|
||
min-height: 25px;
|
||
box-sizing: border-box;
|
||
}
|
||
.chat-top-bar::after {
|
||
content: none;
|
||
}
|
||
body.sidebar-collapsed.hamburger-left .chat-top-bar {
|
||
padding-left: 38px;
|
||
}
|
||
body.sidebar-collapsed.hamburger-right .chat-top-bar {
|
||
padding-right: 38px;
|
||
}
|
||
.incognito-indicator {
|
||
position: absolute;
|
||
left: 12px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
||
border: 1px solid var(--accent);
|
||
color: var(--accent);
|
||
cursor: pointer;
|
||
padding: 4px;
|
||
width: 26px;
|
||
height: 26px;
|
||
box-sizing: border-box;
|
||
border-radius: 6px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: opacity 0.15s, background 0.15s, left 0.15s;
|
||
z-index: 1;
|
||
opacity: 0.7;
|
||
}
|
||
body.sidebar-collapsed.hamburger-left .incognito-indicator {
|
||
left: 12px;
|
||
}
|
||
.incognito-indicator:hover {
|
||
opacity: 1;
|
||
background: color-mix(in srgb, var(--accent) 20%, transparent);
|
||
}
|
||
|
||
.chat-new-btn {
|
||
position: absolute;
|
||
right: 7px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
background: none;
|
||
border: none;
|
||
color: var(--fg);
|
||
opacity: 0.6;
|
||
cursor: pointer;
|
||
padding: 4px;
|
||
border-radius: 6px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: opacity 0.08s, background 0.08s, left 0.15s, right 0.15s;
|
||
flex-shrink: 0;
|
||
z-index: 1;
|
||
}
|
||
.chat-new-btn:hover {
|
||
opacity: 1;
|
||
color: var(--accent);
|
||
}
|
||
/* Flip new-chat and incognito when sidebar is on the right */
|
||
body:has(.sidebar.right-side) .chat-new-btn {
|
||
right: auto;
|
||
left: 12px;
|
||
}
|
||
body:has(.sidebar.right-side) .incognito-indicator {
|
||
left: auto;
|
||
right: 12px;
|
||
}
|
||
body.sidebar-collapsed.hamburger-right .incognito-indicator {
|
||
right: 42px;
|
||
}
|
||
|
||
/* Session meta — sits at top of chat area, scrolls with content */
|
||
.chat-meta-overlay {
|
||
position: absolute;
|
||
left: 44px;
|
||
right: 44px;
|
||
top: 50%;
|
||
transform: translateY(calc(-50% - 2px));
|
||
font-size: 0.75em;
|
||
line-height: 1;
|
||
/* 70% mix keeps the chat title clearly above the WCAG AA 4.5:1
|
||
contrast threshold (40% only reached ~2.8:1). */
|
||
color: color-mix(in srgb, var(--fg) 70%, transparent);
|
||
white-space: nowrap;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 2px;
|
||
pointer-events: none;
|
||
min-width: 0;
|
||
max-width: calc(100% - 88px);
|
||
overflow: hidden;
|
||
}
|
||
.chat-meta-overlay > * {
|
||
pointer-events: auto;
|
||
flex-shrink: 0;
|
||
}
|
||
.chat-meta-overlay #current-meta {
|
||
display: inline-block;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
cursor: pointer;
|
||
min-width: 0;
|
||
flex: 0 1 auto;
|
||
max-width: min(56vw, 520px);
|
||
line-height: 1.25;
|
||
}
|
||
.chat-top-bar:has(.incognito-indicator:not([style*="display:none"]):not([style*="display: none"])) .chat-meta-overlay {
|
||
left: 54px;
|
||
right: 44px;
|
||
max-width: calc(100% - 98px);
|
||
}
|
||
body:has(.sidebar.right-side) .chat-top-bar:has(.incognito-indicator:not([style*="display:none"]):not([style*="display: none"])) .chat-meta-overlay {
|
||
left: 44px;
|
||
right: 54px;
|
||
}
|
||
.chat-meta-overlay:hover {
|
||
color: color-mix(in srgb, var(--fg) 75%, transparent);
|
||
}
|
||
/* Offline model rows */
|
||
.models-row-offline {
|
||
opacity: 0.4;
|
||
pointer-events: none;
|
||
}
|
||
.models-row-offline .model-chat-btn {
|
||
pointer-events: auto;
|
||
}
|
||
/* Offline badge next to endpoint name */
|
||
.endpoint-offline-badge {
|
||
color: var(--danger, #f44);
|
||
font-size: 0.8em;
|
||
margin-left: 4px;
|
||
opacity: 0.7;
|
||
}
|
||
.chat-meta-overlay:empty,
|
||
.chat-meta-overlay:not(:has(#current-meta:not(:empty))) {
|
||
display: none;
|
||
}
|
||
.export-dropdown-wrap {
|
||
position: relative;
|
||
display: inline-flex;
|
||
flex-shrink: 0;
|
||
margin-left: -4px;
|
||
margin-right: -20px;
|
||
}
|
||
.export-dl-btn {
|
||
background: none; border: none;
|
||
color: inherit;
|
||
cursor: pointer;
|
||
padding: 4px 5px; border-radius: 4px;
|
||
display: flex; align-items: center;
|
||
transition: color 0.08s, background 0.08s;
|
||
}
|
||
.export-dl-btn > svg { color: var(--accent, var(--red)); }
|
||
.export-dl-btn:hover {
|
||
color: var(--accent);
|
||
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
||
}
|
||
.export-dropdown-menu {
|
||
display: none;
|
||
position: fixed;
|
||
background: var(--panel); border: 1px solid var(--border);
|
||
border-radius: 8px; padding: 4px; min-width: 120px;
|
||
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
|
||
z-index: 300;
|
||
color: var(--fg);
|
||
}
|
||
.export-dropdown-menu.open { display: block; }
|
||
.export-dropdown-item {
|
||
padding: 6px 8px; font-size: 11px; cursor: pointer;
|
||
border-radius: 6px; color: var(--fg); white-space: nowrap;
|
||
display: flex; align-items: center; gap: 10px;
|
||
transition: background 0.1s;
|
||
}
|
||
.export-dropdown-item:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
|
||
.export-settings-text-only .dropdown-icon { display: none; }
|
||
#export-compact-btn {
|
||
display: none !important;
|
||
}
|
||
#export-compact-btn .dropdown-icon {
|
||
display: none;
|
||
}
|
||
.export-compact-pill {
|
||
display: inline;
|
||
padding: 0;
|
||
border: 0;
|
||
border-radius: 0;
|
||
background: transparent;
|
||
color: inherit;
|
||
line-height: inherit;
|
||
}
|
||
.export-dropdown-item .dropdown-icon {
|
||
width: 14px; height: 14px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
flex-shrink: 0; opacity: 0.5;
|
||
}
|
||
/* On mobile the chat-header export dropdown was cramped — items
|
||
were 11px font with 6px padding. Bump to readable touch targets:
|
||
larger min-width, taller rows, bigger icons + text. */
|
||
@media (max-width: 768px) {
|
||
.export-dropdown-menu {
|
||
min-width: 200px;
|
||
padding: 6px;
|
||
border-radius: 10px;
|
||
}
|
||
.export-dropdown-item {
|
||
padding: 12px 14px;
|
||
font-size: 14px;
|
||
gap: 12px;
|
||
min-height: 44px;
|
||
}
|
||
#export-compact-btn {
|
||
margin: 5px 6px 7px;
|
||
min-height: 38px;
|
||
padding: 9px 14px;
|
||
}
|
||
.export-dropdown-item .dropdown-icon {
|
||
width: 18px; height: 18px; opacity: 0.7;
|
||
}
|
||
.export-dropdown-item .dropdown-icon svg {
|
||
width: 18px; height: 18px;
|
||
}
|
||
}
|
||
.sidebar {
|
||
width: 240px;
|
||
background: var(--sidebar-bg, var(--panel));
|
||
border-right: 1px solid var(--border);
|
||
transition: width 0.25s ease, opacity 0.2s ease, padding 0.25s ease;
|
||
display: flex;
|
||
flex-direction: column;
|
||
flex-shrink: 0;
|
||
overflow: hidden;
|
||
min-height: 0;
|
||
margin: 0;
|
||
padding: 0;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
backdrop-filter: blur(10px);
|
||
border: 1px solid color-mix(in srgb, var(--fg) 11%, transparent);
|
||
position: relative;
|
||
}
|
||
.sidebar-resize-handle {
|
||
position: absolute;
|
||
top: 0;
|
||
right: -3px;
|
||
width: 6px;
|
||
height: 100%;
|
||
cursor: col-resize;
|
||
z-index: 10;
|
||
background: transparent;
|
||
}
|
||
.sidebar-resize-handle:hover,
|
||
.sidebar-resize-handle.dragging {
|
||
background: var(--accent);
|
||
opacity: 0.6;
|
||
}
|
||
.sidebar.right-side .sidebar-resize-handle {
|
||
right: auto;
|
||
left: -3px;
|
||
}
|
||
.sidebar.resizing {
|
||
transition: none;
|
||
user-select: none;
|
||
}
|
||
.sidebar.right-side {
|
||
order: 2;
|
||
margin: 0;
|
||
border-right: none;
|
||
border-left: 1px solid var(--border);
|
||
}
|
||
.sidebar.hidden {
|
||
/* !important so it beats the inline width init.js restores from storage —
|
||
otherwise the width never changes and only opacity animates, making the
|
||
collapse look instant. With this, width animates from the inline value
|
||
down to 0 via the .sidebar width transition. */
|
||
width: 0 !important;
|
||
padding: 0 !important;
|
||
border: none;
|
||
overflow: hidden;
|
||
opacity: 0;
|
||
}
|
||
/* ===== Sidebar User Bar ===== */
|
||
.sidebar-user-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 12px 12px;
|
||
flex-shrink: 0;
|
||
gap: 4px;
|
||
min-height: 48px;
|
||
}
|
||
|
||
.user-bar-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
flex: 1;
|
||
min-width: 0;
|
||
cursor: pointer;
|
||
padding: 6px 8px;
|
||
border-radius: 8px;
|
||
transition: background 0.15s;
|
||
}
|
||
|
||
.user-bar-left:hover {
|
||
background: color-mix(in srgb, var(--fg) 6%, transparent);
|
||
}
|
||
|
||
.user-bar-avatar {
|
||
width: 24px;
|
||
height: 24px;
|
||
border-radius: 50%;
|
||
background: color-mix(in srgb, var(--fg) 12%, transparent);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
color: var(--fg);
|
||
opacity: 0.7;
|
||
flex-shrink: 0;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
#user-bar-avatar { position: relative; left: -4px; }
|
||
|
||
.user-bar-name {
|
||
font-size: 9.75px;
|
||
font-weight: 500;
|
||
color: var(--fg);
|
||
opacity: 0.8;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.user-bar-actions {
|
||
display: flex;
|
||
gap: 1px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.user-bar-btn {
|
||
background: none;
|
||
border: none;
|
||
color: var(--fg);
|
||
opacity: 0.35;
|
||
cursor: pointer;
|
||
padding: 6px;
|
||
border-radius: 6px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: opacity 0.12s, background 0.12s;
|
||
}
|
||
|
||
.user-bar-btn:hover {
|
||
opacity: 1;
|
||
background: color-mix(in srgb, var(--fg) 8%, transparent);
|
||
}
|
||
|
||
|
||
.sidebar.hidden .sidebar-user-bar {
|
||
display: none;
|
||
}
|
||
|
||
/* Sticky sidebar header — logo, never scrolls */
|
||
.sidebar-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-end; /* right-align when sidebar is on left */
|
||
gap: 8px;
|
||
padding: 15px 10px 0 40px; /* top padding aligns logo with fixed hamburger */
|
||
flex-shrink: 0;
|
||
min-height: 40px;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
position: relative;
|
||
z-index: 3;
|
||
background: var(--sidebar-bg, var(--panel));
|
||
}
|
||
.sidebar-hamburger {
|
||
display: none !important; /* external #hamburger-btn is the only toggle */
|
||
}
|
||
.sidebar-inner {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
overscroll-behavior-y: none;
|
||
scrollbar-width: none;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0;
|
||
padding: 10px 8px 8px;
|
||
min-height: 0;
|
||
transition: transform 0.3s cubic-bezier(0.25, 1, 0.5, 1);
|
||
}
|
||
.sidebar-brand {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-shrink: 0;
|
||
min-height: 24px;
|
||
}
|
||
.sidebar-brand-title {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
line-height: 1.35;
|
||
color: var(--brand-color, var(--red));
|
||
white-space: nowrap;
|
||
user-select: none;
|
||
position: relative;
|
||
top: 0;
|
||
left: -10px;
|
||
}
|
||
.sidebar-sep {
|
||
display: none;
|
||
}
|
||
#sidebar-search-btn,
|
||
#sidebar-new-chat-btn {
|
||
margin: 0;
|
||
padding: 8px 8px;
|
||
}
|
||
#sessions-section {
|
||
margin-top: -1px;
|
||
min-height: 0;
|
||
flex: 0 0 auto;
|
||
}
|
||
/* Keep a large chat history from pushing the rest of the sidebar below
|
||
the fold. The chat rows scroll independently; the sections beneath
|
||
them stay reachable in the main sidebar scroll area. */
|
||
#sessions-section #session-list {
|
||
max-height: min(42vh, 420px);
|
||
flex: 0 0 auto;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
scrollbar-width: thin;
|
||
min-height: 0;
|
||
}
|
||
#tools-section {
|
||
margin-top: 1px;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
#sessions-section {
|
||
flex: 0 0 auto !important;
|
||
overflow: visible !important;
|
||
}
|
||
#sessions-section #session-list {
|
||
max-height: min(34vh, 300px) !important;
|
||
flex: 0 0 auto !important;
|
||
overflow-y: auto !important;
|
||
overflow-x: hidden !important;
|
||
}
|
||
}
|
||
#tools-section .list-item {
|
||
padding: 8px 8px;
|
||
}
|
||
.sidebar.right-side .sidebar-header {
|
||
justify-content: flex-start;
|
||
padding-left: 10px;
|
||
padding-right: 40px;
|
||
}
|
||
.sidebar.right-side .sidebar-inner {
|
||
padding: 8px;
|
||
}
|
||
.sidebar.right-side .sidebar-brand {
|
||
justify-content: flex-start;
|
||
padding: 2px 30px 4px 4px;
|
||
}
|
||
.sidebar.right-side .sidebar-brand-title {
|
||
margin-left: 10px;
|
||
}
|
||
/* Fixed hamburger — always visible, toggles sidebar */
|
||
.hamburger-btn {
|
||
position: fixed;
|
||
top: 12px;
|
||
left: 9px;
|
||
right: auto;
|
||
z-index: 210;
|
||
width: 30px;
|
||
height: 30px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: none;
|
||
border: none;
|
||
color: var(--hamburger-color, var(--fg));
|
||
cursor: pointer;
|
||
opacity: 0.5;
|
||
-webkit-tap-highlight-color: transparent;
|
||
outline: none;
|
||
transition: opacity 0.15s;
|
||
padding: 0;
|
||
display: flex;
|
||
}
|
||
body.hamburger-right .hamburger-btn {
|
||
left: auto;
|
||
right: 9px;
|
||
}
|
||
.mobile-new-chat-btn {
|
||
display: none;
|
||
}
|
||
.hamburger-btn:hover {
|
||
opacity: 1;
|
||
}
|
||
/* Icon rail — mini sidebar that replaces the wide .sidebar when it's
|
||
hidden (mutually exclusive — see sidebar-layout.js:57). Fullscreen
|
||
panels reserve this strip of width via `left: var(--icon-rail-w)`
|
||
so the rail stays visible without needing a z-index hack (which
|
||
used to cover the fixed-position hamburger button). */
|
||
.icon-rail {
|
||
width: 48px;
|
||
flex-shrink: 0;
|
||
background: var(--panel);
|
||
border-right: 1px solid var(--border);
|
||
display: none;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 48px 4px 8px 4px;
|
||
gap: 4px;
|
||
margin: 0;
|
||
position: relative;
|
||
box-sizing: border-box;
|
||
/* Allow hover labels (e.g. the rail-new-chat "New" tooltip) to extend
|
||
outside the 48px column. overflow:hidden was clipping them. */
|
||
overflow: visible;
|
||
}
|
||
body.sidebar-collapsed:not(.hamburger-only) .icon-rail,
|
||
html.ody-sidebar-mini .icon-rail {
|
||
display: flex;
|
||
}
|
||
html.ody-sidebar-mini .sidebar,
|
||
html.ody-sidebar-off .sidebar {
|
||
width: 0 !important;
|
||
min-width: 0 !important;
|
||
opacity: 0;
|
||
overflow: hidden;
|
||
pointer-events: none;
|
||
padding: 0 !important;
|
||
border: 0 !important;
|
||
}
|
||
html.ody-sidebar-off .icon-rail {
|
||
display: none !important;
|
||
}
|
||
.rail-resize-handle {
|
||
position: absolute;
|
||
top: 0;
|
||
right: -3px;
|
||
width: 6px;
|
||
height: 100%;
|
||
cursor: col-resize;
|
||
z-index: 10;
|
||
background: transparent;
|
||
}
|
||
.rail-resize-handle:hover,
|
||
.rail-resize-handle.dragging {
|
||
background: var(--accent);
|
||
opacity: 0.6;
|
||
}
|
||
.icon-rail.right-side .rail-resize-handle {
|
||
right: auto;
|
||
left: -3px;
|
||
}
|
||
.icon-rail.right-side {
|
||
order: 2;
|
||
margin: 0;
|
||
border-right: none;
|
||
border-left: 1px solid var(--border);
|
||
}
|
||
.icon-rail-divider {
|
||
width: 24px;
|
||
height: 1px;
|
||
background: var(--border);
|
||
margin: 4px 0;
|
||
}
|
||
.icon-rail-btn {
|
||
position: relative;
|
||
width: 34px;
|
||
height: 34px;
|
||
border: none;
|
||
background: transparent;
|
||
color: var(--accent, var(--red));
|
||
font-size: 16px;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
opacity: 0.5;
|
||
transition: opacity 0.08s, background 0.08s;
|
||
}
|
||
.rail-notes-badge {
|
||
position: absolute;
|
||
top: 1px;
|
||
right: 1px;
|
||
min-width: 14px;
|
||
height: 14px;
|
||
padding: 0 3px;
|
||
border-radius: 7px;
|
||
background: color-mix(in srgb, var(--accent) 85%, var(--bg));
|
||
color: var(--bg);
|
||
font-size: 9px;
|
||
font-weight: 700;
|
||
line-height: 14px;
|
||
text-align: center;
|
||
box-sizing: border-box;
|
||
pointer-events: none;
|
||
box-shadow: 0 0 0 1px var(--bg);
|
||
}
|
||
.rail-notes-badge.fired {
|
||
background: var(--red);
|
||
animation: rail-notes-pulse 1.6s ease-in-out infinite;
|
||
}
|
||
@keyframes rail-notes-pulse {
|
||
0%, 100% { box-shadow: 0 0 0 1px var(--bg), 0 0 0 0 color-mix(in srgb, var(--red) 50%, transparent); }
|
||
50% { box-shadow: 0 0 0 1px var(--bg), 0 0 0 4px color-mix(in srgb, var(--red) 15%, transparent); }
|
||
}
|
||
/* Main sidebar notes button — dot when a reminder has fired */
|
||
.tool-notes-dot {
|
||
display: inline-block;
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: var(--red);
|
||
/* Match the Deep Research badge: right-aligned (auto) with the same
|
||
4px left nudge, so both sidebar buttons' dots line up identically. */
|
||
margin-left: auto;
|
||
position: relative;
|
||
left: -4px;
|
||
flex-shrink: 0;
|
||
align-self: center;
|
||
animation: rail-notes-pulse 1.6s ease-in-out infinite;
|
||
pointer-events: none;
|
||
}
|
||
/* Individual card — subtle accent border tint when a reminder has fired */
|
||
.note-card.note-card-reminder-due .note-card-reminder {
|
||
background: color-mix(in srgb, var(--red) 22%, transparent);
|
||
color: var(--red);
|
||
font-weight: 600;
|
||
}
|
||
.icon-rail-btn:hover { opacity: 1; background: color-mix(in srgb, var(--accent) 12%, transparent); }
|
||
.icon-rail-btn.active-section { opacity: 1; background: color-mix(in srgb, var(--color-accent) 15%, transparent); }
|
||
/* Unified "minimized" indicator for any rail/sidebar button whose modal is held open */
|
||
.rail-minimized { position: relative; }
|
||
.rail-minimized::after {
|
||
content: ''; position: absolute;
|
||
/* Default for inline spans like #email-section-title — sit just to the
|
||
right of the element so it never overlaps the icon. */
|
||
top: 50%; right: -10px; transform: translateY(-50%);
|
||
width: 6px; height: 6px; border-radius: 50%;
|
||
background: var(--accent, var(--red));
|
||
box-shadow: 0 0 0 2px var(--bg);
|
||
pointer-events: none;
|
||
animation: rail-min-pulse 2s ease-in-out infinite;
|
||
}
|
||
/* Compact icon-rail buttons: top-right corner of the icon */
|
||
.icon-rail-btn.rail-minimized::after { top: 4px; right: 4px; transform: none; }
|
||
/* Sidebar list-items are wider — anchor right-aligned vertically centered */
|
||
.list-item.rail-minimized::after { top: 50%; right: 8px; transform: translateY(-50%); }
|
||
#tool-memory-btn.rail-minimized::after { right: 12px; }
|
||
/* Per-user nudge: the listed tools' minimized dot sits 2px further
|
||
in from the right edge so it doesn't look glued to the border. */
|
||
#tool-theme-btn.rail-minimized::after,
|
||
#tool-tasks-btn.rail-minimized::after,
|
||
#tool-notes-btn.rail-minimized::after,
|
||
#tool-library-btn.rail-minimized::after,
|
||
#tool-gallery-btn.rail-minimized::after,
|
||
#tool-compare-btn.rail-minimized::after,
|
||
#tool-calendar-btn.rail-minimized::after { right: 12px; }
|
||
@media (max-width: 768px) {
|
||
/* On mobile the list-items are taller (touch-sized), so the tabbed-down
|
||
pulsing dot reads a hair low and right. Email uses its own dot and is
|
||
already aligned — only the tool list-item dots need the nudge:
|
||
4px left (right 8 → 12) and 2px up. */
|
||
.list-item.rail-minimized::after {
|
||
right: 13px;
|
||
transform: translateY(calc(-50% - 2px));
|
||
}
|
||
#tool-memory-btn.rail-minimized::after { right: 17px; }
|
||
}
|
||
@keyframes rail-min-pulse {
|
||
0%,100% { opacity: 1; }
|
||
50% { opacity: 0.4; }
|
||
}
|
||
/* Compact minimize button for modal headers — matches the close
|
||
button's round control so the two read as a paired set. The
|
||
header's h4 carries margin-right:auto, which groups minimize + close
|
||
on the right; this button just needs a small gap before close. */
|
||
.modal-minimize-btn {
|
||
background: transparent;
|
||
color: color-mix(in srgb, var(--fg) 58%, transparent);
|
||
border: 1px solid transparent;
|
||
cursor: pointer;
|
||
width: 24px;
|
||
height: 24px;
|
||
padding: 0;
|
||
margin-left: 0;
|
||
margin-right: 4px;
|
||
line-height: 1;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 999px;
|
||
flex-shrink: 0;
|
||
transition: background 0.15s, color 0.15s;
|
||
}
|
||
.modal-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);
|
||
}
|
||
.modal-minimize-btn svg {
|
||
transform: translateY(2px);
|
||
}
|
||
.modal.modal-minimized { display: none !important; }
|
||
/* Window tile snap ghost (desktop only) */
|
||
#tile-ghost {
|
||
position: fixed; pointer-events: none; z-index: 9000;
|
||
background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
|
||
border: 2px solid color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
|
||
border-radius: 8px;
|
||
opacity: 0; transform: scale(0.96);
|
||
transition: left 0.12s ease, top 0.12s ease, width 0.12s ease, height 0.12s ease, opacity 0.12s, transform 0.12s;
|
||
}
|
||
#tile-ghost.visible { opacity: 1; transform: scale(1); }
|
||
/* Bottom dock — chip per minimized modal */
|
||
#minimized-dock {
|
||
position: fixed; bottom: var(--composer-clearance, 12px); left: 50%; transform: translateX(-50%);
|
||
display: flex; gap: 6px; flex-wrap: wrap;
|
||
max-width: calc(100vw - 24px);
|
||
padding: 4px;
|
||
z-index: 100;
|
||
pointer-events: none;
|
||
}
|
||
.minimized-dock-chip {
|
||
pointer-events: auto;
|
||
display: inline-flex; align-items: center; gap: 6px;
|
||
padding: 6px 8px 6px 10px;
|
||
background: var(--panel, var(--bg));
|
||
border: 1px solid var(--border);
|
||
border-radius: 999px;
|
||
color: var(--fg); font-family: inherit; font-size: 12px;
|
||
cursor: grab; touch-action: none; user-select: none;
|
||
box-shadow: 0 4px 14px rgba(0,0,0,0.35);
|
||
transition: transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.15s, border-color 0.15s;
|
||
animation: dock-chip-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) both;
|
||
}
|
||
.minimized-dock-chip:hover {
|
||
background: color-mix(in srgb, var(--accent, var(--red)) 12%, var(--panel, var(--bg)));
|
||
border-color: color-mix(in srgb, var(--accent, var(--red)) 40%, var(--border));
|
||
}
|
||
.minimized-dock-chip:active { cursor: grabbing; }
|
||
.minimized-dock-chip.dragging {
|
||
cursor: grabbing; z-index: 1000;
|
||
box-shadow: 0 8px 24px rgba(0,0,0,0.55);
|
||
transition: none;
|
||
opacity: 0.95;
|
||
}
|
||
/* Whole-dock drag (grabbed an edge chip) */
|
||
#minimized-dock.dock-dragging { cursor: grabbing; }
|
||
#minimized-dock.dock-dragging .minimized-dock-chip {
|
||
transition: none;
|
||
box-shadow: 0 8px 24px rgba(0,0,0,0.45);
|
||
}
|
||
/* Subtle visual cue that edge chips drag the whole dock */
|
||
#minimized-dock .minimized-dock-chip:first-child:not(:only-child),
|
||
#minimized-dock .minimized-dock-chip:last-child:not(:only-child) {
|
||
box-shadow: 0 4px 14px rgba(0,0,0,0.35),
|
||
inset 0 0 0 1px color-mix(in srgb, var(--accent, var(--red)) 25%, transparent);
|
||
}
|
||
.minimized-dock-chip svg { opacity: 0.7; flex-shrink: 0; }
|
||
.minimized-dock-label { white-space: nowrap; }
|
||
|
||
/* Mobile: chips are icon-only round pills. Tap to restore, drag toward
|
||
the trash zone at top-center to close. touch-action:none lets the
|
||
pointermove listener claim the gesture instead of the page scroll. */
|
||
@media (max-width: 768px) {
|
||
.minimized-dock-chip {
|
||
width: 40px; height: 40px;
|
||
padding: 0 !important;
|
||
border-radius: 50% !important;
|
||
justify-content: center;
|
||
position: relative;
|
||
overflow: visible;
|
||
touch-action: none;
|
||
}
|
||
.minimized-dock-chip svg { width: 18px; height: 18px; opacity: 0.9; }
|
||
.minimized-dock-label,
|
||
.minimized-dock-x { display: none !important; }
|
||
.minimized-dock-chip.chip-free-drag {
|
||
box-shadow: 0 8px 22px rgba(0,0,0,0.55),
|
||
0 0 0 2px color-mix(in srgb, var(--accent, var(--red)) 50%, transparent) !important;
|
||
}
|
||
/* Long-press hint — chip swells and settles while the detach timer
|
||
counts down so the user feels feedback before the bubble peels out
|
||
of the chain. Returns to scale(1) at the end so there's no visual
|
||
jump when the timer fires and the chip becomes a free-drag puck. */
|
||
.minimized-dock-chip.chip-long-press {
|
||
background:
|
||
radial-gradient(circle at 30% 25%, color-mix(in srgb, #fff 28%, transparent), transparent 36%),
|
||
linear-gradient(135deg,
|
||
color-mix(in srgb, var(--accent, var(--red)) 34%, var(--panel, var(--bg))),
|
||
color-mix(in srgb, #7dd3fc 26%, var(--panel, var(--bg))) 52%,
|
||
color-mix(in srgb, #f0abfc 22%, var(--panel, var(--bg))));
|
||
border-color: color-mix(in srgb, var(--accent, var(--red)) 72%, #fff 12%) !important;
|
||
animation: chip-long-press-pulse 0.82s ease-in-out infinite;
|
||
z-index: 10030;
|
||
}
|
||
.minimized-dock-chip.chip-long-press::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: -96px;
|
||
border-radius: inherit;
|
||
background:
|
||
radial-gradient(circle,
|
||
color-mix(in srgb, var(--accent, var(--red)) 42%, transparent) 0 18%,
|
||
color-mix(in srgb, #7dd3fc 34%, transparent) 34%,
|
||
color-mix(in srgb, #f0abfc 30%, transparent) 50%,
|
||
transparent 72%);
|
||
pointer-events: none;
|
||
z-index: -1;
|
||
animation: chip-long-press-ripple 0.82s ease-out infinite;
|
||
}
|
||
@keyframes chip-long-press-pulse {
|
||
0%, 100% { transform: scale(1); }
|
||
50% { transform: scale(1.42);
|
||
box-shadow: 0 14px 42px rgba(0,0,0,0.66),
|
||
0 0 0 16px color-mix(in srgb, var(--accent, var(--red)) 42%, transparent),
|
||
0 0 72px color-mix(in srgb, #7dd3fc 44%, transparent),
|
||
0 0 118px color-mix(in srgb, #f0abfc 32%, transparent); }
|
||
}
|
||
@keyframes chip-long-press-ripple {
|
||
0% { opacity: 0.92; transform: scale(0.08); }
|
||
72% { opacity: 0.28; transform: scale(3.6); }
|
||
100% { opacity: 0; transform: scale(5.4); }
|
||
}
|
||
/* Chip whose modal is currently open — accent ring so the user can
|
||
tell at a glance which floating bubble belongs to the visible
|
||
modal. Tap it to minimize. */
|
||
.minimized-dock-chip.chip-active {
|
||
border-color: var(--accent, var(--red)) !important;
|
||
box-shadow: 0 4px 14px rgba(0,0,0,0.35),
|
||
0 0 0 2px color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
|
||
}
|
||
.minimized-dock-chip.chip-active svg { opacity: 1; }
|
||
}
|
||
/* Magnetic close zone — slides in from the side opposite the chip's
|
||
starting position so it never overlaps the dock. Always accent
|
||
color, larger than the chips, snappy spring transition. */
|
||
#dock-trash-zone {
|
||
position: fixed;
|
||
left: 50%;
|
||
width: 88px; height: 88px;
|
||
border-radius: 50%;
|
||
--trash-zone-color: var(--accent, var(--red, #e53935));
|
||
background: var(--trash-zone-color);
|
||
color: #fff;
|
||
display: flex; align-items: center; justify-content: center;
|
||
pointer-events: none;
|
||
opacity: 0;
|
||
z-index: 9000;
|
||
box-shadow: 0 8px 28px color-mix(in srgb, var(--trash-zone-color) 55%, transparent);
|
||
transition: opacity 0.18s ease,
|
||
transform 0.26s cubic-bezier(0.34, 1.56, 0.64, 1),
|
||
box-shadow 0.18s ease;
|
||
}
|
||
/* Off-screen start position depends on which side the chip is on so
|
||
the X slides in from the opposite edge with a snappy overshoot. */
|
||
#dock-trash-zone[data-side="top"] { transform: translateX(-50%) translateY(-180%) scale(0.7); }
|
||
#dock-trash-zone[data-side="bottom"] { transform: translateX(-50%) translateY(180%) scale(0.7); }
|
||
#dock-trash-zone.visible {
|
||
opacity: 1;
|
||
transform: translateX(-50%) translateY(0) scale(1);
|
||
}
|
||
#dock-trash-zone.engaged {
|
||
transform: translateX(-50%) translateY(0) scale(var(--trash-zone-scale, 1.22));
|
||
box-shadow: 0 0 0 14px color-mix(in srgb, var(--trash-zone-color) 22%, transparent),
|
||
0 12px 36px color-mix(in srgb, var(--trash-zone-color) 60%, transparent);
|
||
animation: trash-zone-breathe 1.35s ease-in-out infinite;
|
||
}
|
||
/* Whirlpool ring — fades in when chip is in capture range and spins
|
||
continuously, then bursts on drop. */
|
||
#dock-trash-zone .whirlpool {
|
||
position: absolute; inset: -8px;
|
||
border-radius: 50%;
|
||
border: 2px solid transparent;
|
||
border-top-color: color-mix(in srgb, var(--trash-zone-color) 95%, transparent);
|
||
border-right-color: color-mix(in srgb, var(--trash-zone-color) 65%, transparent);
|
||
border-bottom-color: color-mix(in srgb, var(--trash-zone-color) 30%, transparent);
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
animation: whirlpool-spin 0.85s linear infinite;
|
||
transition: opacity 0.15s ease;
|
||
}
|
||
#dock-trash-zone.engaged .whirlpool { opacity: 1; }
|
||
#dock-trash-zone.dropping .whirlpool {
|
||
opacity: 1;
|
||
animation: whirlpool-burst 0.36s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||
}
|
||
/* When a chip is swirling into the trash X, its inline `rotate(720deg)`
|
||
drags every child + ::after badge along with it — the count/dot pill
|
||
spinning looks chaotic. Fade those out fast at the start of the close
|
||
so visually only the icon glyph rotates. */
|
||
.minimized-dock-chip.chip-trashing > :not(svg),
|
||
.minimized-dock-chip.chip-trashing::after,
|
||
.minimized-dock-chip.chip-trashing::before {
|
||
opacity: 0 !important;
|
||
transition: opacity 0.16s ease-out !important;
|
||
}
|
||
@keyframes whirlpool-spin { to { transform: rotate(360deg); } }
|
||
@keyframes trash-zone-breathe {
|
||
0%, 100% { box-shadow: 0 0 0 10px color-mix(in srgb, var(--trash-zone-color) 16%, transparent), 0 10px 30px color-mix(in srgb, var(--trash-zone-color) 48%, transparent); }
|
||
50% { box-shadow: 0 0 0 16px color-mix(in srgb, var(--trash-zone-color) 25%, transparent), 0 14px 38px color-mix(in srgb, var(--trash-zone-color) 68%, transparent); }
|
||
}
|
||
@keyframes whirlpool-burst {
|
||
0% { transform: rotate(0deg) scale(1); opacity: 1; }
|
||
60% { transform: rotate(540deg) scale(1.5); opacity: 0.6; }
|
||
100% { transform: rotate(900deg) scale(2.2); opacity: 0; }
|
||
}
|
||
|
||
/* Email chip badges:
|
||
- email-lib-modal chip: "1" badge when an email is expanded
|
||
inside (JS sets data-has-expanded).
|
||
- email-reader-* chips: auto-numbered 1, 2, 3 … via CSS counter,
|
||
so multiple opened-email windows are visually distinguishable. */
|
||
.minimized-dock-chip[data-modal-id="email-lib-modal"],
|
||
.minimized-dock-chip[data-modal-id^="email-reader-"] {
|
||
position: relative;
|
||
}
|
||
.minimized-dock-chip[data-modal-id^="email-reader-"][data-tab-num]::after {
|
||
content: attr(data-tab-num);
|
||
position: absolute;
|
||
top: -4px;
|
||
right: -4px;
|
||
min-width: 16px;
|
||
height: 16px;
|
||
padding: 0 4px;
|
||
background: var(--accent, var(--red));
|
||
color: #fff;
|
||
font-size: 9px;
|
||
font-weight: 700;
|
||
line-height: 16px;
|
||
text-align: center;
|
||
border-radius: 8px;
|
||
box-shadow: 0 0 0 2px var(--bg);
|
||
pointer-events: none;
|
||
}
|
||
.minimized-dock-chip[data-modal-id="email-lib-modal"][data-email-unread-label]::after {
|
||
content: attr(data-email-unread-label);
|
||
position: absolute;
|
||
top: -6px;
|
||
right: 10px;
|
||
height: 16px;
|
||
padding: 0 6px;
|
||
background: var(--accent, var(--red));
|
||
color: #fff;
|
||
font-size: 9px;
|
||
font-weight: 700;
|
||
line-height: 16px;
|
||
white-space: nowrap;
|
||
border-radius: 8px;
|
||
box-shadow: 0 0 0 2px var(--bg);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.minimized-dock-x {
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
width: 16px; height: 16px; border-radius: 50%;
|
||
font-size: 14px; line-height: 1; opacity: 0.4;
|
||
margin-left: 3px;
|
||
}
|
||
.minimized-dock-x:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 12%, transparent); }
|
||
@keyframes dock-chip-in {
|
||
from { opacity: 0; transform: translateY(20px) scale(0.85); }
|
||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||
}
|
||
.rail-new-chat svg { transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1); }
|
||
.rail-new-chat:hover svg { transform: rotate(90deg); }
|
||
/* Sleek hover labels for the mini sidebar. Use a real child span instead
|
||
of ::before/::after so status dots and minimized indicators keep working. */
|
||
.rail-hover-label {
|
||
position: absolute;
|
||
left: 100%;
|
||
top: 50%;
|
||
margin-left: 6px;
|
||
transform: translateY(-50%) translateX(-6px);
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
white-space: nowrap;
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
color: var(--fg);
|
||
background: var(--panel);
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
box-shadow: 0 2px 6px rgba(0,0,0,0.25);
|
||
z-index: 20;
|
||
transition: opacity 0.25s ease, transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||
}
|
||
.icon-rail-btn:hover .rail-hover-label {
|
||
opacity: 1;
|
||
transform: translateY(-50%) translateX(0);
|
||
}
|
||
#rail-admin,
|
||
#rail-settings { color: var(--fg); }
|
||
.rail-separator { width: 20px; height: 1px; background: var(--border); margin: 4px auto; }
|
||
.rail-dynamic {
|
||
position: relative;
|
||
}
|
||
.rail-dynamic::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: 2px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 4px;
|
||
height: 4px;
|
||
border-radius: 50%;
|
||
background: var(--accent, var(--red));
|
||
opacity: 0.7;
|
||
}
|
||
/* ── Sidebar sections (flat layout) ── */
|
||
.section { padding: 0; border: none; background: none; border-radius: 0; box-shadow: none; margin: 0; }
|
||
|
||
/* Section header row — identical sizing to .list-item */
|
||
.section-header-flex {
|
||
display: flex; align-items: center; gap: 6px;
|
||
padding: 8px 8px; margin: 1px 0; border-radius: 4px;
|
||
background: transparent; cursor: pointer;
|
||
transition: background 0.08s;
|
||
height: 29px;
|
||
box-sizing: border-box;
|
||
}
|
||
.section-header-flex:hover { background: color-mix(in srgb, var(--red) 8%, transparent); }
|
||
.section-header-flex::after { content: none; }
|
||
.section-header-flex h4::before { content: none; }
|
||
|
||
/* Section title text — same font as .list-item .grow */
|
||
.section-header-flex h4,
|
||
.section-header-flex .section-title {
|
||
flex: 1; cursor: pointer; user-select: none;
|
||
display: flex; align-items: center; gap: 6px;
|
||
margin: 0; padding: 0; border: none;
|
||
font-size: 10px; font-weight: 400; font-family: inherit;
|
||
/* 1.3 (not 1) so Fira Code's tall glyph box isn't vertically clipped in
|
||
Chromium/Edge — mirrors the .list-item fix. The title is flex-centred
|
||
in a fixed-height (29px) header, so this adds headroom without reflow. */
|
||
line-height: 1.3; letter-spacing: 0; text-transform: none;
|
||
color: var(--fg);
|
||
}
|
||
.section-icon,
|
||
.sidebar-action-icon {
|
||
flex-shrink: 0;
|
||
stroke: var(--accent, var(--red));
|
||
position: relative;
|
||
left: -1px;
|
||
color: var(--accent, var(--red));
|
||
}
|
||
/* Shared notification dot for sidebar section titles. Single source of
|
||
truth so chats / email / assistant / future-section dots all sit at
|
||
the same offset from their label. */
|
||
.sidebar-notif-dot {
|
||
display: inline-block;
|
||
width: 6px;
|
||
height: 6px;
|
||
/* Push to the right edge of the flex section-title so chats / email
|
||
/ assistant dots all line up vertically in the same column
|
||
instead of trailing right after each (differently-sized) label. */
|
||
margin-left: auto;
|
||
border-radius: 50%;
|
||
background: var(--accent, var(--red));
|
||
flex-shrink: 0;
|
||
vertical-align: middle;
|
||
}
|
||
/* The email notification gets a soft breathing glow so new-mail catches
|
||
the eye without being shouty. Vertical alignment stays with the
|
||
inherited .sidebar-notif-dot rule so it lines up with chats/assistant. */
|
||
#email-unread-dot.sidebar-notif-dot {
|
||
animation: email-notif-breathe 2.2s ease-in-out infinite;
|
||
/* Nudge in from the far-right edge so it doesn't crowd the corner. */
|
||
margin-right: 4px;
|
||
/* Tiny vertical nudge to center with the email label. */
|
||
position: relative;
|
||
top: 0.1px;
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
#email-unread-dot.sidebar-notif-dot { animation: none; }
|
||
}
|
||
@media (max-width: 768px) {
|
||
/* Nudge the sidebar notification dot up 1px on mobile so it lines
|
||
up with the bigger section titles. vertical-align:middle drifts
|
||
a hair low against the larger touch-friendly text. */
|
||
.sidebar-notif-dot {
|
||
position: relative;
|
||
top: -1px;
|
||
}
|
||
/* Cookbook's sidebar status dot carries an inline top:-1px, so override
|
||
with the ID + !important to nudge it 2px left / 1px up on mobile. */
|
||
#cookbook-notif-dot {
|
||
left: -1px !important;
|
||
top: -2px !important;
|
||
}
|
||
}
|
||
#sidebar-new-chat-btn svg {
|
||
transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||
}
|
||
#sidebar-new-chat-btn:hover svg {
|
||
transform: rotate(90deg);
|
||
}
|
||
|
||
/* Sort/select buttons in header — compact */
|
||
.section-header-flex > div { display: flex; align-items: center; gap: 2px; }
|
||
.section-header-btn {
|
||
all: unset;
|
||
cursor: pointer; opacity: 0.4; padding: 1px 3px; border-radius: 4px;
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
transition: opacity 0.08s, background 0.08s;
|
||
}
|
||
.section-header-btn:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 7%, transparent); }
|
||
.section-header-btn.active { opacity: 0.9; color: var(--accent); }
|
||
.section-header-btn svg { width: 12px; height: 12px; }
|
||
#session-sort-btn { transform: translateX(2px); }
|
||
|
||
/* Chats library — grid icon, hover-reveal so the header only toggles
|
||
collapse. Uses !important to win over .list-item-plus-btn's
|
||
opacity:1!important (the email-style plus button forces always-on,
|
||
which we don't want here — the manage button should fade until the
|
||
user actually hovers the section). */
|
||
#sessions-section .chats-manage-btn {
|
||
opacity: 0.4 !important;
|
||
transition: opacity 0.12s, background 0.08s;
|
||
}
|
||
#sessions-section .section-header-flex:hover .chats-manage-btn,
|
||
#sessions-section .chats-manage-btn:hover,
|
||
#sessions-section .chats-manage-btn:focus-visible {
|
||
opacity: 0.85 !important;
|
||
}
|
||
#sessions-section .chats-manage-btn:hover,
|
||
#sessions-section .chats-manage-btn:focus-visible {
|
||
opacity: 1 !important;
|
||
}
|
||
/* No hover background on the Chats manage button — the inherited
|
||
section-header-btn rule was painting a tinted box behind the
|
||
'manage' label as it slid in. */
|
||
#sessions-section .chats-manage-btn,
|
||
#sessions-section .chats-manage-btn:hover,
|
||
#sessions-section .chats-manage-btn:focus-visible {
|
||
background: transparent !important;
|
||
}
|
||
@media (hover: none) {
|
||
#sessions-section .chats-manage-btn { opacity: 0.35 !important; }
|
||
#sessions-section .chats-manage-btn:active { opacity: 1 !important; }
|
||
}
|
||
/* Pull the 'manage' label out of email's absolute-positioning trick
|
||
so it sits in the flex flow and expands the button's clickable
|
||
area as it slides in. Otherwise hovering the visible 'manage'
|
||
text would actually be outside the button's bounding box. */
|
||
#sessions-section .chats-manage-btn .list-item-plus-label {
|
||
position: static;
|
||
transform: none;
|
||
max-width: 0;
|
||
overflow: hidden;
|
||
padding-right: 0;
|
||
transition: max-width 0.2s ease, opacity 0.18s ease, padding-right 0.18s ease;
|
||
}
|
||
#sessions-section .chats-manage-btn:hover .list-item-plus-label,
|
||
#sessions-section .chats-manage-btn:focus-visible .list-item-plus-label,
|
||
#sessions-section .section-header-flex:hover .chats-manage-btn .list-item-plus-label {
|
||
max-width: 80px;
|
||
padding-right: 4px;
|
||
}
|
||
|
||
/* Collapse chevron */
|
||
.section-collapse-btn {
|
||
all: unset;
|
||
cursor: pointer; display: inline-flex; align-items: center; padding: 0 2px; border-radius: 4px;
|
||
}
|
||
.section-collapse-btn:hover { background: color-mix(in srgb, var(--fg) 8%, transparent); }
|
||
.section-collapse-chevron { display: inline-flex; opacity: 0.4; transition: transform 0.2s, opacity 0.15s; }
|
||
.section-collapse-btn:hover .section-collapse-chevron { opacity: 0.6; }
|
||
.section.collapsed .section-collapse-chevron { transform: rotate(-90deg); opacity: 0.4; }
|
||
.section-header-flex:has(.section-header-btn) .section-collapse-btn { display: none; }
|
||
.section.collapsed .section-collapse-btn { display: inline-flex !important; }
|
||
|
||
/* Collapsed state */
|
||
.section.collapsed > *:not(h4):not(.section-title):not(.section-header-flex) { display: none !important; }
|
||
.section.collapsed .section-header-flex { margin-bottom: 0; }
|
||
.section.collapsed .section-header-btn { display: none; }
|
||
.section.collapsed { cursor: pointer; }
|
||
|
||
/* Domino expand: every time a section goes from .collapsed → open
|
||
(toggleCollapse in section-management.js adds .section-just-expanded
|
||
for ~700ms), the .list-item children cascade in one after another,
|
||
same feel as the chat input's tools menu. Each row springs in
|
||
from a tiny offset below + scaled-down, staggered by nth-child. */
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header) {
|
||
animation: section-domino-in 0.36s cubic-bezier(0.22, 1.61, 0.36, 1) backwards;
|
||
}
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header):nth-child(1) { animation-delay: 0.04s; }
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header):nth-child(2) { animation-delay: 0.08s; }
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header):nth-child(3) { animation-delay: 0.12s; }
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header):nth-child(4) { animation-delay: 0.16s; }
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header):nth-child(5) { animation-delay: 0.20s; }
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header):nth-child(6) { animation-delay: 0.24s; }
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header):nth-child(7) { animation-delay: 0.28s; }
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header):nth-child(8) { animation-delay: 0.32s; }
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header):nth-child(9) { animation-delay: 0.36s; }
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header):nth-child(10) { animation-delay: 0.40s; }
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header):nth-child(11) { animation-delay: 0.44s; }
|
||
.section.section-just-expanded :is(.list-item, .models-row, .date-section-header):nth-child(12) { animation-delay: 0.48s; }
|
||
@keyframes section-domino-in {
|
||
0% { opacity: 0; transform: translateY(8px) translateX(-4px) scale(0.92); }
|
||
60% { opacity: 1; }
|
||
100% { opacity: 1; transform: translateY(0) translateX(0) scale(1); }
|
||
}
|
||
|
||
/* Domino collapse: when toggleCollapse goes open → closed, JS adds
|
||
.section-just-collapsing for ~530ms before flipping in .collapsed.
|
||
During that window the items fade/slide DOWN one after another so
|
||
you see them peel off instead of vanishing as a block. Uses
|
||
nth-last-child so the BOTTOM item leaves first and the cascade
|
||
rolls upward — mirrors the "stacked deck" feeling of the open
|
||
animation reversed. */
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header) {
|
||
animation: section-domino-out 0.22s ease-in forwards;
|
||
}
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header):nth-last-child(1) { animation-delay: 0.00s; }
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header):nth-last-child(2) { animation-delay: 0.025s; }
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header):nth-last-child(3) { animation-delay: 0.05s; }
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header):nth-last-child(4) { animation-delay: 0.075s; }
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header):nth-last-child(5) { animation-delay: 0.10s; }
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header):nth-last-child(6) { animation-delay: 0.125s; }
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header):nth-last-child(7) { animation-delay: 0.15s; }
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header):nth-last-child(8) { animation-delay: 0.175s; }
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header):nth-last-child(9) { animation-delay: 0.20s; }
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header):nth-last-child(10) { animation-delay: 0.225s; }
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header):nth-last-child(11) { animation-delay: 0.25s; }
|
||
.section.section-just-collapsing :is(.list-item, .models-row, .date-section-header):nth-last-child(12) { animation-delay: 0.275s; }
|
||
@keyframes section-domino-out {
|
||
0% { opacity: 1; transform: translateY(0) translateX(0) scale(1); }
|
||
100% { opacity: 0; transform: translateY(6px) translateX(-3px) scale(0.94); }
|
||
}
|
||
|
||
.row { display:flex; gap:6px; align-items:center; }
|
||
.list-item:hover,
|
||
.models-row:hover {
|
||
background: color-mix(in srgb, var(--red) 8%, transparent);
|
||
border-color: var(--red);
|
||
}
|
||
#models.models-refreshing {
|
||
opacity: 0.72;
|
||
transition: opacity 0.16s ease;
|
||
}
|
||
/* Disabled tool — dimmed to signal its feature is turned off globally */
|
||
.list-item.tool-disabled {
|
||
opacity: 0.4;
|
||
}
|
||
.list-item.tool-disabled:hover {
|
||
opacity: 0.7;
|
||
}
|
||
/* Session bulk select mode */
|
||
.session-bulk-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 4px 8px;
|
||
border-bottom: 1px solid var(--border);
|
||
background: var(--sidebar-bg, var(--panel));
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 3;
|
||
font-size: 11px;
|
||
}
|
||
.session-bulk-bar.hidden { display: none; }
|
||
#session-select-all-dot {
|
||
display: inline-block;
|
||
position: relative;
|
||
top: -2px;
|
||
}
|
||
.session-bulk-btn {
|
||
background: none;
|
||
border: none;
|
||
border-radius: 4px;
|
||
color: var(--fg);
|
||
padding: 4px;
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
opacity: 0.4;
|
||
transition: opacity 0.1s;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.session-bulk-btn:hover { opacity: 1; }
|
||
.session-bulk-btn-danger { color: var(--red); opacity: 0.5; }
|
||
.session-bulk-btn-danger:hover { opacity: 1; }
|
||
/* Checkbox in select mode */
|
||
.session-select-cb {
|
||
accent-color: var(--accent, var(--red));
|
||
margin: 0 4px 0 0;
|
||
flex-shrink: 0;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.session-icon,
|
||
.model-icon {
|
||
flex-shrink: 0;
|
||
opacity: 0.35;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
}
|
||
.session-icon.has-docs {
|
||
color: inherit;
|
||
opacity: 0.5;
|
||
}
|
||
.session-star {
|
||
width: 10px; height: 10px;
|
||
border-radius: 50%;
|
||
border: 1.5px solid color-mix(in srgb, var(--fg) 22%, transparent);
|
||
flex-shrink: 0;
|
||
position: relative;
|
||
}
|
||
.session-star.processing {
|
||
animation: none;
|
||
}
|
||
.session-star.provider-logo.processing svg {
|
||
animation: none;
|
||
}
|
||
.session-star.notify {
|
||
animation: none;
|
||
opacity: 1;
|
||
/* Completed runs keep the provider slot but turn it into a compact
|
||
success check, so the logo does not simply appear to vanish. */
|
||
width: 14px; height: 14px;
|
||
background: transparent;
|
||
border-color: transparent;
|
||
color: var(--accent, var(--red));
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
/* The dot doubles as the provider-logo holder; hide that logo in the
|
||
done state so the solid notif dot doesn't collide with the SVG behind it. */
|
||
.session-star.notify svg { display: none; }
|
||
.session-star.notify::after {
|
||
content: '';
|
||
width: 14px;
|
||
height: 14px;
|
||
background: currentColor;
|
||
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpolyline points='20 6 9 17 4 12' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
|
||
}
|
||
.session-fav {
|
||
flex-shrink: 0;
|
||
cursor: pointer;
|
||
color: var(--red);
|
||
opacity: 0.6;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
transition: opacity 0.1s;
|
||
}
|
||
.session-fav:hover {
|
||
opacity: 1;
|
||
}
|
||
.list-item.active-session {
|
||
background: color-mix(in srgb, var(--red) 10%, transparent);
|
||
border-color: var(--red);
|
||
border-left-color: var(--red);
|
||
}
|
||
.list-item .provider-logo { opacity: 0.4 !important; }
|
||
.list-item:has(.session-star.processing) .provider-logo { opacity: 1 !important; }
|
||
.list-item.stream-complete .provider-logo { opacity: 1 !important; }
|
||
.list-item.active {
|
||
background: color-mix(in srgb, var(--red) 10%, transparent);
|
||
border-color: var(--red);
|
||
border-left-color: var(--red);
|
||
}
|
||
/* Only show the red focus ring for keyboard navigation (Tab). Mouse
|
||
clicks shouldn't leave a sticky red outline on a sidebar item. */
|
||
.list-item:focus { outline: none; }
|
||
.list-item:focus-visible {
|
||
outline: none;
|
||
border-color: var(--red, var(--color-error));
|
||
box-shadow: 0 0 0 1px var(--red, var(--color-error));
|
||
}
|
||
/* Drag handles hidden by default, shown via body.rearrange-mode */
|
||
.drag-handle, .item-drag-handle, .folder-drag-handle { display: none; }
|
||
body.rearrange-mode .drag-handle,
|
||
body.rearrange-mode .item-drag-handle,
|
||
body.rearrange-mode .folder-drag-handle { display: inline; }
|
||
|
||
/* Drag sorting styles for list items */
|
||
.item-drag-handle {
|
||
cursor: grab;
|
||
opacity: 0.4;
|
||
font-size: 10px;
|
||
padding: 0 4px;
|
||
user-select: none;
|
||
letter-spacing: -2px;
|
||
transition: opacity 0.15s;
|
||
}
|
||
.item-drag-handle:hover {
|
||
opacity: 1;
|
||
color: var(--red);
|
||
}
|
||
.item-drag-handle:active {
|
||
cursor: grabbing;
|
||
}
|
||
.list-item.dragging,
|
||
.models-row.dragging,
|
||
.session-folder.dragging {
|
||
opacity: 0.95;
|
||
box-shadow: 0 8px 24px color-mix(in srgb, var(--red) 30%, transparent);
|
||
border-color: var(--red);
|
||
background: var(--panel);
|
||
cursor: grabbing;
|
||
}
|
||
.session-item.dragging-to-folder {
|
||
opacity: 0.55;
|
||
border-color: var(--red);
|
||
box-shadow: 0 4px 14px color-mix(in srgb, var(--red) 22%, transparent);
|
||
}
|
||
.list-item.dragging .item-drag-handle,
|
||
.models-row.dragging .item-drag-handle,
|
||
.session-folder.dragging .folder-drag-handle {
|
||
opacity: 1;
|
||
color: var(--red);
|
||
}
|
||
/* ── Model category grouping ── */
|
||
.models-category-header {
|
||
display: flex; align-items: center; gap: 6px;
|
||
padding: 4px 8px; cursor: pointer;
|
||
font-size: 0.85em; font-weight: 600;
|
||
color: color-mix(in srgb, var(--fg) 55%, transparent);
|
||
border-radius: 4px; user-select: none;
|
||
margin-top: 4px;
|
||
transition: opacity 0.08s, background 0.08s, color 0.08s;
|
||
}
|
||
.models-category-header:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 4%, transparent); }
|
||
.models-category-header .folder-count { font-weight: 400; opacity: 0.5; font-size: 0.9em; }
|
||
.models-endpoint-label {
|
||
display: flex; align-items: center; gap: 6px;
|
||
padding: 4px 8px; cursor: pointer;
|
||
font-size: 0.85em; font-weight: 600;
|
||
color: color-mix(in srgb, var(--fg) 55%, transparent);
|
||
border-radius: 4px; user-select: none;
|
||
transition: opacity 0.08s, background 0.08s;
|
||
}
|
||
.models-endpoint-label:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 4%, transparent); }
|
||
.models-endpoint-label .folder-count { font-weight: 400; opacity: 0.5; }
|
||
.models-group-content.indented {
|
||
padding-left: 4px;
|
||
}
|
||
.models-group-content.indented .models-row { border-bottom: 1px solid color-mix(in srgb, var(--fg) 7%, transparent); }
|
||
.models-group-content.indented .models-row:last-child { border-bottom: none; }
|
||
/* ── Session folders ── */
|
||
.session-folder { margin: 2px 0; }
|
||
.session-folder-header {
|
||
display: flex; align-items: center; gap: 6px;
|
||
padding: 4px 8px; cursor: pointer;
|
||
font-size: 0.88em; font-weight: 600;
|
||
color: color-mix(in srgb, var(--fg) 55%, transparent);
|
||
border-radius: 4px; user-select: none;
|
||
transition: opacity 0.08s, background 0.08s;
|
||
}
|
||
.session-folder-header:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 4%, transparent); }
|
||
.session-folder-header .folder-count { font-weight: 400; opacity: 0.5; }
|
||
.session-folder-header.drag-over {
|
||
outline: 2px dashed var(--red); background: color-mix(in srgb, var(--red) 13%, transparent); opacity: 1;
|
||
}
|
||
.session-folder.dragging-folder { opacity: 0.4; }
|
||
.folder-toggle { font-size: 0.7em; width: 10px; text-align: center; }
|
||
.folder-name { font-weight: inherit; flex: 1; }
|
||
.folder-count { font-size: 0.85em; opacity: 0.5; }
|
||
.folder-drag-handle {
|
||
cursor: grab; opacity: 0.3; font-size: 0.8em; padding: 0 2px;
|
||
transition: opacity 0.08s;
|
||
}
|
||
.session-folder-header:hover .folder-drag-handle { opacity: 0.7; }
|
||
.folder-delete-btn {
|
||
background: none; border: none; color: var(--fg); opacity: 0;
|
||
cursor: pointer; font-size: 1.1em; padding: 0 2px; line-height: 1;
|
||
min-height: 0; height: auto;
|
||
transition: opacity 0.08s, color 0.08s;
|
||
}
|
||
.session-folder-header:hover .folder-delete-btn { opacity: 0.5; }
|
||
.folder-delete-btn:hover { opacity: 1 !important; color: var(--color-error); }
|
||
.session-folder-content { padding-left: 4px; }
|
||
.session-folder-content .list-item { border-bottom: 1px solid color-mix(in srgb, var(--fg) 7%, transparent); }
|
||
.session-folder-content .list-item:last-child { border-bottom: none; }
|
||
.session-show-more-btn {
|
||
display: block;
|
||
width: 100%;
|
||
background: none;
|
||
border: none;
|
||
color: var(--fg);
|
||
opacity: 0.4;
|
||
font-size: 0.8em;
|
||
padding: 6px 8px;
|
||
cursor: pointer;
|
||
text-align: center;
|
||
transition: opacity 0.15s;
|
||
}
|
||
.session-show-more-btn:hover { opacity: 0.8; }
|
||
.drag-folder-placeholder {
|
||
height: 4px; margin: 2px 0; border-radius: 2px;
|
||
background: var(--red); opacity: 0.6;
|
||
}
|
||
.unfiled-drop-zone {
|
||
min-height: 8px; border-radius: 4px; transition: all 0.15s;
|
||
}
|
||
.unfiled-drop-zone.drag-over {
|
||
min-height: 24px; outline: 2px dashed var(--red);
|
||
background: color-mix(in srgb, var(--red) 9%, transparent);
|
||
}
|
||
|
||
/* Mobile swipe-to-delete */
|
||
.swipe-delete-action {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
width: 60px;
|
||
background: color-mix(in srgb, var(--color-error) 12%, transparent);
|
||
color: var(--color-error);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 0 4px 4px 0;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 0.15s;
|
||
cursor: pointer;
|
||
z-index: 1;
|
||
}
|
||
.swipe-delete-action::before {
|
||
content: '';
|
||
display: block;
|
||
width: 18px;
|
||
height: 18px;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23ff4444' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 6 5 6 21 6'/%3E%3Cpath d='M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2'/%3E%3C/svg%3E");
|
||
background-size: contain;
|
||
background-repeat: no-repeat;
|
||
}
|
||
.swipe-delete-action:active {
|
||
background: color-mix(in srgb, var(--color-error) 22%, transparent);
|
||
}
|
||
.drag-placeholder {
|
||
background: color-mix(in srgb, var(--red) 10%, transparent);
|
||
border: 2px dashed color-mix(in srgb, var(--color-accent) 40%, transparent);
|
||
border-radius: 6px;
|
||
margin: 4px 0;
|
||
transition: height 0.15s ease;
|
||
}
|
||
.list-item,
|
||
.models-row {
|
||
display: flex;
|
||
gap: 6px;
|
||
align-items: center;
|
||
padding: 3px 8px;
|
||
margin: 0;
|
||
border-radius: 4px;
|
||
border: none;
|
||
line-height: 1.3;
|
||
font-size: 13px;
|
||
background: transparent;
|
||
transition: background 0.08s;
|
||
cursor: pointer;
|
||
touch-action: pan-y;
|
||
}
|
||
.list-item button {
|
||
margin: 0;
|
||
height: 24px;
|
||
padding: 0 8px;
|
||
font-size: 9px;
|
||
}
|
||
/* Inline "+" action on a tool/section row (Library → new document,
|
||
Email → compose). Sizing forced + min-* zeroed so the mobile
|
||
touch-target rule (44px) can't inflate it. Selector intentionally
|
||
NOT scoped to `.list-item` so the same class also styles buttons
|
||
sitting in `.section-header-flex` (e.g. #email-compose-btn). */
|
||
.list-item-plus-btn {
|
||
all: unset;
|
||
box-sizing: border-box;
|
||
flex-shrink: 0;
|
||
position: relative;
|
||
left: 4px;
|
||
display: inline-flex !important;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 14px !important;
|
||
min-height: 0 !important;
|
||
width: auto !important;
|
||
min-width: 0 !important;
|
||
padding: 0 5px !important;
|
||
border-radius: 4px;
|
||
color: var(--fg);
|
||
/* Always visible at rest. On hover (devices that have it) the + spins
|
||
90° and "new" reveals to its right — neat expand affordance. */
|
||
opacity: 1 !important;
|
||
cursor: pointer;
|
||
gap: 0;
|
||
overflow: visible;
|
||
z-index: 1;
|
||
transition: background 0.12s, color 0.12s, gap 0.18s ease;
|
||
}
|
||
.list-item-plus-btn svg.list-item-plus-icon {
|
||
width: 13px; height: 13px;
|
||
transition: transform 0.22s ease;
|
||
}
|
||
/* Legacy fallback for plus-btns without the `.list-item-plus-icon` class. */
|
||
.list-item-plus-btn svg:not(.list-item-plus-icon) { width: 13px; height: 13px; }
|
||
/* Label is absolutely positioned to the LEFT of the icon so the + stays
|
||
put — it just appears beside it on hover instead of pushing it. The
|
||
button's intrinsic width remains the icon, so neighbours don't shift. */
|
||
.list-item-plus-label {
|
||
position: absolute;
|
||
right: 100%;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
padding-right: 5px;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
white-space: nowrap;
|
||
font-size: 9.5px;
|
||
line-height: 1;
|
||
letter-spacing: 0.02em;
|
||
transition: opacity 0.18s ease, transform 0.22s ease;
|
||
}
|
||
@media (hover: hover) {
|
||
.list-item-plus-btn:hover .list-item-plus-icon {
|
||
transform: rotate(90deg);
|
||
}
|
||
.list-item-plus-btn:hover .list-item-plus-label {
|
||
opacity: 1;
|
||
transform: translateY(-50%) translateX(0);
|
||
pointer-events: auto;
|
||
}
|
||
.list-item-plus-btn .list-item-plus-label {
|
||
transform: translateY(-50%) translateX(6px);
|
||
}
|
||
}
|
||
.list-item-plus-btn:hover {
|
||
background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
|
||
color: var(--accent, var(--red));
|
||
}
|
||
.list-item span {
|
||
color: var(--fg);
|
||
font-size: 9.75px;
|
||
}
|
||
.grow {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
text-overflow: clip;
|
||
white-space: nowrap;
|
||
}
|
||
input, textarea, button, select {
|
||
background:var(--bg);
|
||
color:var(--fg);
|
||
border:1px solid var(--border);
|
||
font-family:inherit;
|
||
padding:6.9px;
|
||
border-radius: 4px;
|
||
transition: background 0.08s, border-color 0.08s, color 0.08s, opacity 0.08s;
|
||
}
|
||
input:hover, textarea:hover, button:hover, select:hover {
|
||
border-color: var(--fg);
|
||
background-color: var(--panel);
|
||
}
|
||
:root.light input,
|
||
:root.light textarea,
|
||
:root.light button,
|
||
:root.light select {
|
||
background:#eaeaea;
|
||
color-scheme: light;
|
||
}
|
||
input[type="text"] { height:32px; width:100%; }
|
||
textarea { width:100%; min-height:32px; height:auto; max-height:30lh; overflow-y:auto; resize:none; }
|
||
button { height:32px; padding:0 10px; }
|
||
#chat-form button[type="submit"] { height:38px; }
|
||
select {
|
||
height:32px;
|
||
color-scheme: dark;
|
||
background-color: var(--select-bg);
|
||
color: var(--select-fg);
|
||
}
|
||
select option,
|
||
select optgroup {
|
||
background-color: var(--select-option-bg);
|
||
color: var(--select-option-fg);
|
||
}
|
||
select option:checked {
|
||
background-color: var(--select-option-active-bg);
|
||
color: var(--select-option-fg);
|
||
}
|
||
:root.light select { color-scheme: light; }
|
||
.chat-container {
|
||
flex:1;
|
||
display:flex;
|
||
flex-direction:column;
|
||
padding:0 16px;
|
||
overflow:hidden;
|
||
position:relative;
|
||
min-height:0;
|
||
min-width:0;
|
||
margin-top:8px;
|
||
margin-bottom: 0;
|
||
transition: margin-left 160ms cubic-bezier(0.22, 0.61, 0.36, 1),
|
||
margin-right 160ms cubic-bezier(0.22, 0.61, 0.36, 1);
|
||
}
|
||
.chat-meta { font-size:12px; color:color-mix(in srgb, var(--fg) 60%, transparent); margin-bottom:6px; }
|
||
.chat-history {
|
||
display:flex;
|
||
flex-direction:column;
|
||
flex:1;
|
||
overflow-y:auto;
|
||
overflow-x:hidden;
|
||
overscroll-behavior-y: none;
|
||
margin-bottom:8px;
|
||
white-space:normal;
|
||
min-height:0;
|
||
/* Streaming completion replaces the live turn with its persisted DOM.
|
||
We preserve the scroll position explicitly; native anchoring would
|
||
otherwise fight that replacement and create a visible bounce. */
|
||
overflow-anchor: none;
|
||
--chat-max: 800px;
|
||
padding-left: max(0px, calc((100% - var(--chat-max)) / 2));
|
||
padding-right: max(12px, calc((100% - var(--chat-max)) / 2 + 12px));
|
||
}
|
||
.chat-history > * {
|
||
flex: 0 0 auto;
|
||
}
|
||
.chat-pull-refresh {
|
||
--pull-refresh-y: 0px;
|
||
--pull-refresh-progress: 0;
|
||
position: absolute;
|
||
top: 8px;
|
||
left: 50%;
|
||
z-index: 48;
|
||
width: 34px;
|
||
height: 34px;
|
||
display: none;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: 1px solid color-mix(in srgb, var(--border) 75%, transparent);
|
||
border-radius: 999px;
|
||
background: color-mix(in srgb, var(--panel) 94%, transparent);
|
||
box-shadow: 0 8px 20px color-mix(in srgb, #000 16%, transparent);
|
||
opacity: calc(0.25 + (var(--pull-refresh-progress) * 0.75));
|
||
pointer-events: none;
|
||
transform: translate(-50%, calc(-42px + var(--pull-refresh-y))) scale(calc(0.82 + (var(--pull-refresh-progress) * 0.18)));
|
||
transition: opacity 0.16s ease, transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1), border-color 0.16s ease;
|
||
}
|
||
.chat-pull-refresh.is-visible,
|
||
.chat-pull-refresh.is-refreshing {
|
||
display: inline-flex;
|
||
}
|
||
.chat-pull-refresh.is-ready,
|
||
.chat-pull-refresh.is-refreshing {
|
||
border-color: color-mix(in srgb, var(--accent, var(--red)) 58%, transparent);
|
||
color: var(--accent, var(--red));
|
||
}
|
||
.chat-pull-refresh .spinner-whirlpool {
|
||
margin: 0 !important;
|
||
width: 18px !important;
|
||
height: 18px !important;
|
||
}
|
||
.chat-pull-refresh canvas {
|
||
display: block;
|
||
}
|
||
#email-lib-modal .email-pull-refresh {
|
||
top: 112px;
|
||
z-index: 4;
|
||
}
|
||
/* Sortable Cookbook column headers had no visual cue, so users couldn't tell
|
||
a header was clickable (the Newest sort on the Model column was invisible).
|
||
Show a pointer + hover highlight, and underline the active sort column. */
|
||
.hwfit-header .hwfit-sortable { cursor: pointer; transition: color .12s; }
|
||
.hwfit-header .hwfit-sortable:hover { color: var(--fg); text-decoration: underline dotted; }
|
||
.hwfit-header .hwfit-sort-active { color: var(--fg); font-weight: 600; }
|
||
/* Welcome screen — centered in available space above input bar */
|
||
#welcome-screen {
|
||
position:absolute;
|
||
top:40%;
|
||
left:50%;
|
||
transform:translate(-50%,-50%);
|
||
display:flex;
|
||
flex-direction:column;
|
||
align-items:center;
|
||
text-align:center;
|
||
pointer-events:none;
|
||
animation: welcome-enter 0.4s ease-out both;
|
||
transition: top 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease, transform 0.3s ease;
|
||
}
|
||
#welcome-screen .welcome-tip,
|
||
#welcome-screen .welcome-sub,
|
||
#welcome-screen .welcome-version {
|
||
transition: opacity 0.25s ease, max-height 0.25s ease, margin 0.25s ease;
|
||
max-height: 60px;
|
||
overflow: hidden;
|
||
}
|
||
/* The tip is a full sentence that wraps to 4-5 lines on narrow phones,
|
||
where the welcome block shrink-wraps small; the shared 60px ceiling
|
||
(sized for the one-line sub/version) clipped its last line. Give the tip
|
||
a taller ceiling so it isn't truncated. Kept above the max-height:650px
|
||
block below so that rule's max-height:0 still collapses it on short
|
||
viewports. */
|
||
#welcome-screen .welcome-tip {
|
||
max-height: 120px;
|
||
}
|
||
@media (max-height: 650px) {
|
||
#welcome-screen { top: 28%; }
|
||
#welcome-screen .welcome-tip { opacity: 0; max-height: 0; margin: 0; }
|
||
#welcome-screen .welcome-version { margin-top: 6px; }
|
||
.incognito-btn { margin-top: 8px; }
|
||
}
|
||
@media (max-height: 500px) {
|
||
#welcome-screen { top: 22%; }
|
||
#welcome-screen .welcome-name { font-size: 1.4rem; margin-bottom: 0; }
|
||
#welcome-screen .welcome-sub { opacity: 0; max-height: 0; margin: 0; }
|
||
.incognito-btn { margin-top: 4px !important; }
|
||
}
|
||
@media (max-height: 380px) {
|
||
#welcome-screen { opacity: 0; pointer-events: none; }
|
||
}
|
||
#welcome-screen.hidden { display:none; }
|
||
#welcome-screen.kb-hidden {
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transform: translate(-50%, -50%);
|
||
transition: opacity 0.16s ease;
|
||
}
|
||
@media (max-width: 768px) {
|
||
#welcome-screen { top: 42%; }
|
||
#welcome-screen .welcome-name { margin-bottom: 2px; }
|
||
#welcome-screen .welcome-sub { margin-bottom: 0; }
|
||
#welcome-screen .incognito-btn { margin-top: 6px; }
|
||
}
|
||
#welcome-screen .welcome-name {
|
||
font-size:2.2rem;
|
||
font-weight:700;
|
||
background: linear-gradient(135deg, var(--brand-color, var(--red)), color-mix(in srgb, var(--brand-color, var(--red)) 60%, var(--fg)));
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
letter-spacing:0.03em;
|
||
margin-bottom:10px;
|
||
}
|
||
.welcome-boat {
|
||
width: 1.8rem;
|
||
height: 1.8rem;
|
||
margin-right: 0.4rem;
|
||
vertical-align: -0.15em;
|
||
color: var(--brand-color, var(--red));
|
||
}
|
||
#welcome-screen .welcome-sub {
|
||
font-size:0.85rem;
|
||
color:color-mix(in srgb, var(--fg) 60%, transparent);
|
||
opacity:0.6;
|
||
line-height:1.5;
|
||
max-width:300px;
|
||
}
|
||
#welcome-screen .welcome-version {
|
||
font-size:0.7rem;
|
||
opacity:0.25;
|
||
margin-top:12px;
|
||
pointer-events:none;
|
||
user-select:none;
|
||
}
|
||
#welcome-screen .welcome-tip {
|
||
font-size:0.75rem;
|
||
color:var(--fg);
|
||
opacity:0.2;
|
||
margin-top:24px;
|
||
max-width:320px;
|
||
line-height:1.4;
|
||
}
|
||
#welcome-screen .welcome-tip.nobody-session-note {
|
||
display:flex;
|
||
flex-direction:column;
|
||
gap:2px;
|
||
}
|
||
/* Incognito toggle — on welcome screen */
|
||
.incognito-btn {
|
||
pointer-events: auto;
|
||
background: none;
|
||
border: 1px solid var(--border);
|
||
color: var(--fg);
|
||
opacity: 0.25;
|
||
cursor: pointer;
|
||
padding: 6px 12px;
|
||
border-radius: 20px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6px;
|
||
transition: all 0.15s;
|
||
margin-top: 16px;
|
||
font-family: inherit;
|
||
font-size: 11px;
|
||
max-width: min(160px, 70vw);
|
||
}
|
||
.incognito-btn:hover {
|
||
opacity: 0.6;
|
||
background: color-mix(in srgb, var(--fg) 6%, transparent);
|
||
}
|
||
.incognito-btn.active {
|
||
opacity: 1;
|
||
color: var(--accent);
|
||
border-color: var(--accent);
|
||
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
||
}
|
||
.incognito-btn.active .eye-open { display: none; }
|
||
.incognito-btn.active .eye-blinded { display: inline !important; }
|
||
.incognito-label {
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
letter-spacing: 0.3px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
/* When welcome is active, push input bar up toward center */
|
||
.chat-container .chat-input-bar {
|
||
transition: margin 0.3s ease, max-width 0.3s ease;
|
||
}
|
||
.chat-container.welcome-active .chat-input-bar {
|
||
margin-bottom:30vh;
|
||
margin-left:auto;
|
||
margin-right:auto;
|
||
max-width: 800px;
|
||
width: 100%;
|
||
}
|
||
@media (max-width:768px) {
|
||
.chat-meta-overlay {
|
||
left: 36px;
|
||
right: 36px;
|
||
}
|
||
#welcome-screen .welcome-name { font-size:1.8rem; }
|
||
#welcome-screen .welcome-sub { font-size:0.8rem; }
|
||
.chat-container.welcome-active .chat-input-bar {
|
||
margin-bottom:0;
|
||
margin-left:0;
|
||
margin-right:0;
|
||
position: relative;
|
||
z-index: 20;
|
||
}
|
||
.chat-container.welcome-active:has(.chat-input-bar:focus-within) #welcome-screen,
|
||
.chat-container.welcome-active:has(textarea#message:not(:placeholder-shown)) #welcome-screen {
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transform: translate(-50%, -50%);
|
||
transition: opacity 0.16s ease;
|
||
}
|
||
.chat-container.welcome-active:has(.input-icon-btn.expanded) #welcome-screen,
|
||
.chat-container.welcome-active:has(.send-btn.newchat-expanded) #welcome-screen,
|
||
.chat-container.welcome-active:has(#model-picker-wrap:not(.picker-auto-hidden) .model-picker-menu:not(.hidden)) #welcome-screen {
|
||
opacity: 0.45;
|
||
transform: translate(-50%, -76%) scale(0.94);
|
||
}
|
||
.chat-container.welcome-active:has(.input-icon-btn.expanded) #welcome-screen .welcome-tip,
|
||
.chat-container.welcome-active:has(.send-btn.newchat-expanded) #welcome-screen .welcome-tip,
|
||
.chat-container.welcome-active:has(#model-picker-wrap:not(.picker-auto-hidden) .model-picker-menu:not(.hidden)) #welcome-screen .welcome-tip {
|
||
opacity: 0.14;
|
||
}
|
||
.chat-container.welcome-active textarea#message {
|
||
max-height: min(34vh, 150px);
|
||
}
|
||
}
|
||
@media (max-width: 420px) {
|
||
.chat-meta-overlay {
|
||
left: 34px;
|
||
right: 34px;
|
||
}
|
||
.chat-meta-overlay #session-cost-display,
|
||
.chat-meta-overlay .chat-meta-count {
|
||
display: none !important;
|
||
}
|
||
#welcome-screen {
|
||
width: calc(100vw - 36px);
|
||
}
|
||
#welcome-screen .welcome-name,
|
||
#welcome-screen .welcome-sub,
|
||
#welcome-screen .welcome-tip {
|
||
max-width: 100%;
|
||
}
|
||
.incognito-btn {
|
||
padding: 6px 9px;
|
||
max-width: 120px;
|
||
}
|
||
}
|
||
@media (max-width: 340px) {
|
||
.incognito-btn .incognito-label {
|
||
display: none;
|
||
}
|
||
.incognito-btn {
|
||
width: 30px;
|
||
height: 30px;
|
||
padding: 0;
|
||
border-radius: 999px;
|
||
}
|
||
}
|
||
.msg {
|
||
margin: 8px 0;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
width: fit-content;
|
||
max-width: 85%;
|
||
min-width: 80px;
|
||
border-radius: 12px;
|
||
padding: 10px 12px;
|
||
line-height: 1.4;
|
||
word-wrap: break-word;
|
||
overflow-wrap: break-word;
|
||
overflow: hidden;
|
||
animation: msg-enter 0.3s ease-out both;
|
||
}
|
||
.msg-user {
|
||
align-items: flex-end;
|
||
margin-left: auto;
|
||
margin-right: 8px;
|
||
background: var(--user-bubble-bg, color-mix(in srgb, var(--fg) 8%, var(--bg)));
|
||
border: 1px solid var(--bubble-border, var(--border));
|
||
border-radius: 18px 18px 0 18px;
|
||
align-self: flex-end;
|
||
width: fit-content;
|
||
max-width: 85%;
|
||
min-width: 80px;
|
||
word-wrap: break-word;
|
||
overflow-wrap: break-word;
|
||
overflow: hidden;
|
||
}
|
||
.msg-user:has(textarea) {
|
||
width: 85%;
|
||
}
|
||
.msg-ai {
|
||
align-items: flex-start;
|
||
margin-right: auto;
|
||
margin-left: 8px;
|
||
background: var(--ai-bubble-bg, var(--panel));
|
||
border: 1px solid var(--bubble-border, var(--border));
|
||
border-radius: 18px 18px 18px 0;
|
||
align-self: flex-start;
|
||
width: 85%;
|
||
max-width: 85%;
|
||
min-width: 80px;
|
||
word-wrap: break-word;
|
||
overflow-wrap: break-word;
|
||
overflow: hidden;
|
||
transition: min-height 0.25s ease-out;
|
||
}
|
||
.msg .role {
|
||
font-weight: 600;
|
||
margin-bottom: 6px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.msg .role::before {
|
||
content: '';
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
background: var(--model-dot, color-mix(in srgb, var(--fg) 30%, transparent));
|
||
flex-shrink: 0;
|
||
}
|
||
.msg .role.has-logo::before { display: none; }
|
||
.role-provider-logo { display: inline-flex; align-items: center; flex-shrink: 0; }
|
||
.role-provider-logo svg { width: 12px; height: 12px; }
|
||
.msg-user .role {
|
||
color: color-mix(in srgb, var(--fg) 60%, transparent);
|
||
}
|
||
.msg-user .role::before {
|
||
background: color-mix(in srgb, var(--fg) 40%, transparent);
|
||
}
|
||
.user-mode-pill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 15px;
|
||
min-width: 15px;
|
||
padding: 0 3px;
|
||
border-radius: 4px;
|
||
border: 1px solid color-mix(in srgb, var(--fg) 14%, transparent);
|
||
background: color-mix(in srgb, var(--fg) 6%, transparent);
|
||
color: color-mix(in srgb, var(--fg) 58%, transparent);
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
line-height: 15px;
|
||
text-transform: lowercase;
|
||
}
|
||
.user-mode-pill svg {
|
||
display: block;
|
||
width: 11px;
|
||
height: 11px;
|
||
}
|
||
.user-mode-pill[data-auto="true"]::after {
|
||
content: 'auto';
|
||
margin-left: 4px;
|
||
opacity: 0.65;
|
||
font-weight: 500;
|
||
}
|
||
.msg .body {
|
||
width: 100%;
|
||
white-space: normal;
|
||
word-break: break-word;
|
||
overflow-wrap: anywhere;
|
||
line-height: 1.5;
|
||
font-size: 0.95em;
|
||
margin: 0;
|
||
overflow: hidden;
|
||
max-width: 100%;
|
||
}
|
||
.msg .body > * {
|
||
margin-top: 8px;
|
||
margin-bottom: 8px;
|
||
}
|
||
.msg .body > *:first-child {
|
||
margin-top: 0;
|
||
}
|
||
.msg .body > *:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
.msg .body p:empty {
|
||
display: none;
|
||
}
|
||
.msg-user .body {
|
||
color: var(--fg);
|
||
}
|
||
.chat-queued-bubble-host {
|
||
order: 2147483647;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
flex: 0 0 auto;
|
||
width: 100%;
|
||
min-width: 0;
|
||
}
|
||
.msg-user.msg-user-queued {
|
||
opacity: 0.62;
|
||
cursor: pointer;
|
||
animation: none;
|
||
border: 1px dashed color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
|
||
background: color-mix(in srgb, var(--accent, var(--red)) 8%, var(--input-bg, var(--panel)));
|
||
transition: opacity 0.12s ease, border-color 0.12s ease, transform 0.12s ease;
|
||
}
|
||
.msg-user.msg-user-queued:hover {
|
||
opacity: 0.9;
|
||
border-color: color-mix(in srgb, var(--accent, var(--red)) 85%, transparent);
|
||
transform: translateY(-1px);
|
||
}
|
||
.msg-user.msg-user-queued .queued-pill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 3px;
|
||
height: 14px;
|
||
padding: 0 5px;
|
||
margin-left: 5px;
|
||
border-radius: 999px;
|
||
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 45%, transparent);
|
||
color: var(--accent, var(--red));
|
||
background: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent);
|
||
font-size: 8px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.03em;
|
||
vertical-align: 1px;
|
||
}
|
||
.msg-user.msg-user-queued .queued-pill svg {
|
||
flex: 0 0 auto;
|
||
opacity: 0.9;
|
||
}
|
||
.msg-ai .body {
|
||
color: var(--fg);
|
||
}
|
||
.model-endpoint-add-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
margin-left: 7px;
|
||
padding: 2px 7px;
|
||
border: 1px solid color-mix(in srgb, var(--red) 34%, var(--border));
|
||
border-radius: 999px;
|
||
background: color-mix(in srgb, var(--red) 8%, transparent);
|
||
color: var(--red);
|
||
font: inherit;
|
||
font-size: 0.78em;
|
||
line-height: 1.45;
|
||
cursor: pointer;
|
||
vertical-align: 1px;
|
||
}
|
||
.model-endpoint-add-btn:hover {
|
||
background: color-mix(in srgb, var(--red) 14%, transparent);
|
||
border-color: color-mix(in srgb, var(--red) 55%, var(--border));
|
||
}
|
||
.model-endpoint-add-btn:disabled {
|
||
cursor: default;
|
||
opacity: 0.72;
|
||
}
|
||
.model-endpoint-add-btn.added {
|
||
color: var(--color-save-green, #4caf50);
|
||
border-color: color-mix(in srgb, var(--color-save-green, #4caf50) 45%, var(--border));
|
||
background: color-mix(in srgb, var(--color-save-green, #4caf50) 9%, transparent);
|
||
}
|
||
.task-completed-marker {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 7px;
|
||
margin: 7px 0 2px;
|
||
padding: 5px 9px;
|
||
border: 1px solid color-mix(in srgb, var(--color-save-green, #4caf50) 42%, var(--border));
|
||
border-radius: 999px;
|
||
background: color-mix(in srgb, var(--color-save-green, #4caf50) 9%, transparent);
|
||
color: var(--color-save-green, #4caf50);
|
||
font-size: 0.86em;
|
||
font-weight: 600;
|
||
}
|
||
.task-completed-icon {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 17px;
|
||
height: 17px;
|
||
border-radius: 50%;
|
||
background: color-mix(in srgb, var(--color-save-green, #4caf50) 18%, transparent);
|
||
flex: 0 0 auto;
|
||
}
|
||
.rag-sources {
|
||
margin-top: 12px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
padding: 8px;
|
||
font-size: 11px;
|
||
}
|
||
.rag-sources summary {
|
||
cursor: pointer;
|
||
color: var(--red);
|
||
font-weight: bold;
|
||
font-size: 11px;
|
||
}
|
||
.rag-source-item {
|
||
margin-top: 8px;
|
||
padding: 6px;
|
||
background: color-mix(in srgb, var(--fg) 4%, transparent);
|
||
border-radius: 4px;
|
||
border-left: 2px solid var(--red);
|
||
}
|
||
.rag-similarity {
|
||
color: color-mix(in srgb, var(--fg) 50%, transparent);
|
||
font-size: 10px;
|
||
margin-left: 6px;
|
||
}
|
||
.rag-snippet {
|
||
color: color-mix(in srgb, var(--fg) 65%, transparent);
|
||
font-size: 11px;
|
||
margin-top: 4px;
|
||
white-space: pre-wrap;
|
||
max-height: 60px;
|
||
overflow: hidden;
|
||
}
|
||
.rag-file-delete {
|
||
background: none;
|
||
border: 1px solid var(--border);
|
||
border-radius: 4px;
|
||
color: color-mix(in srgb, var(--fg) 50%, transparent);
|
||
cursor: pointer;
|
||
font-size: 10px;
|
||
padding: 1px 5px;
|
||
}
|
||
.rag-file-delete:hover { color: var(--fg); border-color: var(--fg); }
|
||
.rag-file-size {
|
||
color: color-mix(in srgb, var(--fg) 50%, transparent);
|
||
font-size: 10px;
|
||
margin-left: 4px;
|
||
}
|
||
.rag-upload-zone {
|
||
border: 2px dashed var(--border);
|
||
border-radius: 6px;
|
||
padding: 12px;
|
||
text-align: center;
|
||
color: color-mix(in srgb, var(--fg) 45%, transparent);
|
||
font-size: 11px;
|
||
cursor: pointer;
|
||
transition: border-color 0.2s;
|
||
}
|
||
.rag-upload-zone.dragover {
|
||
border-color: var(--red);
|
||
color: var(--red);
|
||
}
|
||
.msg .timestamp {
|
||
font-size: 10px;
|
||
color: color-mix(in srgb, var(--fg) 45%, transparent);
|
||
margin-top: 4px;
|
||
text-align: right;
|
||
opacity: 0.7;
|
||
}
|
||
.msg-user .timestamp {
|
||
color: color-mix(in srgb, var(--fg) 72%, transparent);
|
||
}
|
||
pre {
|
||
overflow: hidden;
|
||
padding: 6px 4px 2px 4px;
|
||
border: 1px solid var(--border);
|
||
background: var(--bg);
|
||
position: relative;
|
||
line-height: 1.4;
|
||
font-size: 0.95em;
|
||
font-family: 'Fira Code', 'Courier New', monospace;
|
||
min-height: 38px;
|
||
max-width: 100%;
|
||
margin: 8px 0;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
border-radius: 4px;
|
||
min-height: 20px;
|
||
min-width: 80px;
|
||
}
|
||
|
||
/* Ensure code block headers are only slightly larger than regular text */
|
||
pre h1, pre h2, pre h3, pre h4, pre h5, pre h6 {
|
||
font-size: 1.1em;
|
||
font-weight: bold;
|
||
margin: 8px 0 4px 0;
|
||
color: var(--fg);
|
||
}
|
||
.code-lang {
|
||
font-size:0.8em;
|
||
color:color-mix(in srgb, var(--fg) 60%, transparent);
|
||
display:block;
|
||
margin-bottom:2px;
|
||
font-style:italic;
|
||
}
|
||
code { font-family:inherit; }
|
||
.loading { color:var(--red); font-style:italic; }
|
||
#chat-form { display:none; }
|
||
|
||
/* Unified chat input bar */
|
||
.chat-input-bar {
|
||
background: var(--input-bg, var(--panel));
|
||
border: 1px solid var(--input-border, var(--border));
|
||
border-radius: 16px;
|
||
padding: 10px 12px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
max-width: 800px;
|
||
margin-left: auto;
|
||
margin-right: auto;
|
||
width: 100%;
|
||
transition: border-color 0.16s ease, background 0.16s ease;
|
||
}
|
||
body.plan-mode-active .chat-input-bar {
|
||
border-style: dashed;
|
||
border-color: color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
|
||
background: color-mix(in srgb, var(--accent, var(--red)) 8%, var(--input-bg, var(--panel)));
|
||
}
|
||
.chat-input-top {
|
||
width: 100%;
|
||
position: relative;
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
align-items: start;
|
||
}
|
||
.plan-mode-status {
|
||
position: absolute;
|
||
right: 0;
|
||
bottom: 2px;
|
||
z-index: 245;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 7px;
|
||
height: 22px;
|
||
padding: 0 6px 0 8px;
|
||
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
|
||
border-radius: 999px;
|
||
background: color-mix(in srgb, var(--accent, var(--red)) 12%, var(--input-bg, var(--panel)));
|
||
color: var(--accent, var(--red));
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
line-height: 1;
|
||
transition: opacity 0.22s ease, transform 0.22s ease;
|
||
}
|
||
.plan-mode-status[hidden] {
|
||
display: none !important;
|
||
}
|
||
.plan-mode-status.plan-mode-status-autohide {
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transform: translateY(-4px) scale(0.96);
|
||
}
|
||
.plan-mode-status-toggle {
|
||
width: 26px;
|
||
height: 14px;
|
||
padding: 0;
|
||
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
|
||
border-radius: 999px;
|
||
background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
|
||
cursor: pointer;
|
||
position: relative;
|
||
flex-shrink: 0;
|
||
}
|
||
.plan-mode-status-toggle span {
|
||
position: absolute;
|
||
top: 2px;
|
||
right: 2px;
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
background: var(--accent, var(--red));
|
||
}
|
||
.plan-mode-status-toggle:hover {
|
||
background: color-mix(in srgb, var(--accent, var(--red)) 30%, transparent);
|
||
}
|
||
.chat-input-top > .model-picker-wrap {
|
||
position: relative;
|
||
grid-column: 2;
|
||
grid-row: 1;
|
||
top: 0;
|
||
right: auto;
|
||
z-index: 250;
|
||
transform-origin: top right;
|
||
transition: opacity 0.22s ease, transform 0.22s ease;
|
||
will-change: opacity, transform;
|
||
}
|
||
body.plan-mode-active .chat-input-top > .model-picker-wrap {
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transform: translateY(-4px) scale(0.96);
|
||
}
|
||
.chat-input-top > .model-picker-wrap.model-picker-autohide,
|
||
.chat-input-top > .model-picker-wrap.picker-auto-hidden {
|
||
display: none !important;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transform: translateY(-4px) scale(0.96);
|
||
}
|
||
.ghost-text-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
pointer-events: none;
|
||
white-space: pre-wrap;
|
||
overflow-wrap: break-word;
|
||
font-family: inherit;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
padding: 0;
|
||
border: 1px solid transparent;
|
||
color: transparent;
|
||
z-index: 1;
|
||
}
|
||
.ghost-text-overlay .ghost-suggestion {
|
||
color: color-mix(in srgb, var(--fg) 30%, transparent);
|
||
}
|
||
.chat-input-bar textarea#message {
|
||
grid-column: 1;
|
||
grid-row: 1;
|
||
min-width: 0;
|
||
width: 100%;
|
||
background: transparent;
|
||
border: none;
|
||
outline: none;
|
||
resize: none;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
color: var(--fg);
|
||
min-height: 24px;
|
||
max-height: 200px;
|
||
padding: 0;
|
||
font-family: inherit;
|
||
transition: height 0.12s ease-out;
|
||
}
|
||
.chat-input-bar textarea#message::placeholder {
|
||
color: color-mix(in srgb, var(--fg) 35%, transparent);
|
||
}
|
||
.chat-input-bottom {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-top: 4px;
|
||
}
|
||
/* Progressive shrinkage: as the chat input bar gets narrow, sacrifice
|
||
chrome before the typing area. First the Agent/Chat toggle drops out,
|
||
then the model picker — textarea + send button always stay. Container
|
||
queries so it responds to *bar width*, not viewport width (the bar
|
||
can be in a split pane). */
|
||
.chat-input-bar { container-type: inline-size; container-name: chatbar; }
|
||
@container chatbar (max-width: 340px) {
|
||
.chat-input-right .mode-toggle { display: none !important; }
|
||
}
|
||
.chat-input-left {
|
||
display: flex;
|
||
gap: 4px;
|
||
align-items: center;
|
||
flex-wrap: nowrap;
|
||
overflow: hidden;
|
||
min-width: 0;
|
||
flex: 1;
|
||
}
|
||
/* Collapsible buttons shrink away; collapsed ones disappear */
|
||
.chat-input-left > .input-icon-btn {
|
||
flex-shrink: 0;
|
||
}
|
||
.chat-input-left > .input-icon-btn.toolbar-collapsed {
|
||
display: none !important;
|
||
}
|
||
/* Always keep overflow wrapper visible and leftmost */
|
||
.chat-input-left > .overflow-wrapper {
|
||
flex-shrink: 0;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
.input-divider {
|
||
width: 1px;
|
||
height: 16px;
|
||
background: var(--border);
|
||
opacity: 0.4;
|
||
margin: 0 2px;
|
||
flex-shrink: 0;
|
||
}
|
||
.chat-input-right {
|
||
display: flex;
|
||
gap: 8px;
|
||
align-items: center;
|
||
flex-shrink: 0;
|
||
}
|
||
/* Reasoning effort dropdown in Chat Context */
|
||
.reasoning-effort-wrap {
|
||
position: relative;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
}
|
||
.reasoning-effort-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
height: 28px;
|
||
padding: 0 8px;
|
||
border-radius: 6px;
|
||
border: 1px solid var(--border);
|
||
background: color-mix(in srgb, var(--fg) 4%, transparent);
|
||
color: var(--fg);
|
||
font-size: 11.5px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
transition: background 0.15s, border-color 0.15s;
|
||
}
|
||
.reasoning-effort-btn:hover {
|
||
background: color-mix(in srgb, var(--fg) 8%, transparent);
|
||
border-color: color-mix(in srgb, var(--fg) 20%, transparent);
|
||
}
|
||
.reasoning-effort-chevron {
|
||
opacity: 0.5;
|
||
transition: transform 0.15s;
|
||
flex-shrink: 0;
|
||
}
|
||
.reasoning-effort-btn[aria-expanded="true"] .reasoning-effort-chevron {
|
||
transform: rotate(180deg);
|
||
}
|
||
.reasoning-effort-menu {
|
||
position: absolute;
|
||
bottom: calc(100% + 6px);
|
||
right: 0;
|
||
min-width: 140px;
|
||
background: var(--bg-surface, var(--bg));
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
|
||
padding: 4px;
|
||
z-index: 1000;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
.reasoning-effort-menu.hidden {
|
||
display: none !important;
|
||
}
|
||
.reasoning-effort-option {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 6px 10px;
|
||
border-radius: 5px;
|
||
font-size: 12px;
|
||
color: var(--fg);
|
||
cursor: pointer;
|
||
background: transparent;
|
||
border: none;
|
||
text-align: left;
|
||
width: 100%;
|
||
transition: background 0.12s;
|
||
}
|
||
.reasoning-effort-option:hover {
|
||
background: color-mix(in srgb, var(--fg) 8%, transparent);
|
||
}
|
||
.reasoning-effort-option.active {
|
||
color: var(--accent);
|
||
font-weight: 600;
|
||
}
|
||
.chat-context-popup .reasoning-effort-wrap {
|
||
width: 100%;
|
||
margin-top: 8px;
|
||
}
|
||
.chat-context-popup .reasoning-effort-btn {
|
||
width: 100%;
|
||
font: inherit;
|
||
}
|
||
.chat-context-popup .reasoning-effort-prefix {
|
||
flex: 1;
|
||
text-align: left;
|
||
}
|
||
.chat-context-popup .reasoning-effort-menu {
|
||
top: calc(100% + 4px);
|
||
bottom: auto;
|
||
max-height: 180px;
|
||
overflow-y: auto;
|
||
}
|
||
.input-icon-btn {
|
||
background: none;
|
||
border: none;
|
||
color: var(--fg);
|
||
opacity: 0.5;
|
||
cursor: pointer;
|
||
padding: 6px;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: opacity 0.15s, background 0.15s, color 0.15s;
|
||
position: relative;
|
||
}
|
||
.input-icon-btn:hover {
|
||
opacity: 0.8;
|
||
background: color-mix(in srgb, var(--accent) 8%, transparent);
|
||
}
|
||
.input-icon-btn.active,
|
||
.input-icon-btn.expanded {
|
||
opacity: 1;
|
||
color: var(--fg);
|
||
background: color-mix(in srgb, var(--fg) 9%, transparent);
|
||
}
|
||
.plan-mode-btn {
|
||
width: 28px;
|
||
height: 28px;
|
||
padding: 0;
|
||
flex-shrink: 0;
|
||
top: -2px;
|
||
}
|
||
.plan-mode-btn.active {
|
||
color: var(--accent);
|
||
background: color-mix(in srgb, var(--accent) 14%, transparent);
|
||
}
|
||
/* GitHub-style task lists (- [ ] / - [x]) */
|
||
li.task-item {
|
||
list-style: none;
|
||
margin-left: -1.2em;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 8px;
|
||
}
|
||
li.task-item .task-check {
|
||
flex: 0 0 auto;
|
||
width: 15px;
|
||
height: 15px;
|
||
margin-top: 3px;
|
||
border-radius: 4px;
|
||
border: 1.5px solid color-mix(in srgb, var(--fg) 45%, transparent);
|
||
box-sizing: border-box;
|
||
position: relative;
|
||
}
|
||
li.task-item.task-done .task-check {
|
||
background: var(--accent);
|
||
border-color: var(--accent);
|
||
}
|
||
li.task-item.task-done .task-check::after {
|
||
content: '';
|
||
position: absolute;
|
||
left: 4px;
|
||
top: 1px;
|
||
width: 4px;
|
||
height: 8px;
|
||
border: solid var(--bg);
|
||
border-width: 0 2px 2px 0;
|
||
transform: rotate(45deg);
|
||
}
|
||
li.task-item.task-done .task-text {
|
||
opacity: 0.6;
|
||
text-decoration: line-through;
|
||
}
|
||
.plan-inline-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 7px;
|
||
margin-top: 10px;
|
||
}
|
||
.plan-inline-execute,
|
||
.plan-inline-clear {
|
||
height: 26px;
|
||
border-radius: 7px;
|
||
border: 1px solid color-mix(in srgb, var(--fg) 13%, transparent);
|
||
background: transparent;
|
||
color: var(--fg);
|
||
font: inherit;
|
||
font-size: 11px;
|
||
padding: 0 9px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
cursor: pointer;
|
||
}
|
||
.plan-inline-execute {
|
||
background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent);
|
||
border-color: color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
|
||
color: var(--accent, var(--red));
|
||
font-weight: 650;
|
||
}
|
||
.plan-review-panel {
|
||
position: fixed;
|
||
right: 24px;
|
||
bottom: 92px;
|
||
z-index: 500;
|
||
width: min(520px, calc(100vw - 32px));
|
||
max-height: min(560px, calc(100vh - 120px));
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, var(--border));
|
||
border-radius: 8px;
|
||
background: var(--panel);
|
||
color: var(--fg);
|
||
box-shadow: 0 14px 40px color-mix(in srgb, #000 32%, transparent);
|
||
}
|
||
.plan-review-panel[hidden] { display: none !important; }
|
||
.plan-review-panel-header,
|
||
.plan-review-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 10px 12px;
|
||
}
|
||
.plan-review-panel-header {
|
||
justify-content: space-between;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.plan-review-panel-header h3 {
|
||
margin: 0;
|
||
font-size: 13px;
|
||
line-height: 1.2;
|
||
}
|
||
.plan-review-close {
|
||
width: 24px;
|
||
height: 24px;
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--fg);
|
||
font-size: 19px;
|
||
line-height: 1;
|
||
cursor: pointer;
|
||
opacity: 0.65;
|
||
}
|
||
.plan-review-close:hover { opacity: 1; }
|
||
.plan-review-content {
|
||
margin: 0;
|
||
padding: 12px;
|
||
overflow: auto;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
font-size: 12px;
|
||
line-height: 1.55;
|
||
font-family: inherit;
|
||
}
|
||
.plan-review-actions {
|
||
border-top: 1px solid var(--border);
|
||
}
|
||
.plan-review-execute,
|
||
.plan-review-clear {
|
||
height: 28px;
|
||
padding: 0 9px;
|
||
border: 1px solid color-mix(in srgb, var(--fg) 14%, transparent);
|
||
border-radius: 6px;
|
||
background: transparent;
|
||
color: var(--fg);
|
||
font: inherit;
|
||
font-size: 11px;
|
||
cursor: pointer;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
}
|
||
.plan-review-execute {
|
||
border-color: color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
|
||
background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent);
|
||
color: var(--accent, var(--red));
|
||
font-weight: 650;
|
||
}
|
||
/* While the menu is open the chevron stays in its highlighted state
|
||
— don't run the opacity fade transition so we never flash from
|
||
0.5 → hover-1.0 → drop-back. The state holds steady. */
|
||
.input-icon-btn.expanded { transition: none; }
|
||
/* Accent color for Research, Compare toolbar indicators */
|
||
#research-toggle-btn.active,
|
||
.tool-indicator.active {
|
||
color: var(--red);
|
||
background: color-mix(in srgb, var(--red) 12%, transparent);
|
||
}
|
||
#workspace-indicator-btn.active {
|
||
opacity: 1;
|
||
color: var(--fg);
|
||
background: color-mix(in srgb, var(--fg) 9%, transparent);
|
||
}
|
||
#workspace-indicator-btn.active:hover {
|
||
opacity: 1;
|
||
background: color-mix(in srgb, var(--fg) 9%, transparent);
|
||
}
|
||
#workspace-indicator-btn.active > svg:not(.tool-indicator-x),
|
||
#workspace-indicator-btn.active > span {
|
||
opacity: 1;
|
||
}
|
||
#workspace-indicator-btn .workspace-indicator-icon {
|
||
opacity: 1 !important;
|
||
color: currentColor !important;
|
||
stroke: currentColor !important;
|
||
flex-shrink: 0;
|
||
}
|
||
#workspace-indicator-btn > svg:first-of-type {
|
||
opacity: 1 !important;
|
||
color: currentColor !important;
|
||
stroke: currentColor !important;
|
||
}
|
||
/* Research button glow while actively running */
|
||
#research-toggle-btn.research-running {
|
||
opacity: 1 !important;
|
||
color: var(--red);
|
||
animation: research-pulse 2s ease-in-out infinite;
|
||
}
|
||
.tool-indicator-x {
|
||
margin-left: 2px;
|
||
opacity: 0.4;
|
||
flex-shrink: 0;
|
||
transition: opacity 0.15s;
|
||
}
|
||
.tool-indicator:hover .tool-indicator-x {
|
||
opacity: 1;
|
||
}
|
||
/* Character indicator — hide name text below 768px, icon always visible */
|
||
@media (max-width: 768px) {
|
||
#character-indicator-name { display: none; }
|
||
}
|
||
|
||
/* Document indicator — hidden by default, shown when docs exist */
|
||
#doc-indicator-btn { display: none !important; }
|
||
#doc-indicator-btn.visible { display: inline-flex !important; }
|
||
/* On mobile, the minimized-dock chip replaces this indicator — keep
|
||
it hidden regardless of `.visible`. */
|
||
@media (max-width: 768px) {
|
||
#doc-indicator-btn,
|
||
#doc-indicator-btn.visible { display: none !important; }
|
||
}
|
||
.doc-indicator-active {
|
||
color: var(--accent, var(--accent-primary, var(--red))) !important;
|
||
opacity: 1 !important;
|
||
}
|
||
#doc-indicator-btn.active {
|
||
color: var(--accent, var(--accent-primary, var(--red))) !important;
|
||
opacity: 1 !important;
|
||
background: color-mix(in srgb, var(--fg) 9%, transparent) !important;
|
||
}
|
||
#overflow-doc-btn.has-docs,
|
||
#overflow-doc-btn.active {
|
||
color: var(--accent, var(--red));
|
||
opacity: 1;
|
||
}
|
||
#overflow-doc-btn.has-docs.active {
|
||
background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
|
||
}
|
||
.send-btn {
|
||
/* Prefer the theme accent (--red). A stored custom `--send-btn-bg`
|
||
override only kicks in if it's set to a non-white value — guards
|
||
against ChatGPT-style themes where the stored override leaked
|
||
to white and rendered the send button invisible. */
|
||
background: var(--send-btn-bg, var(--red));
|
||
color: #fff;
|
||
border: none;
|
||
border-radius: 8px;
|
||
min-width: 32px;
|
||
width: 32px;
|
||
height: 32px !important;
|
||
padding: 0;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: background 0.25s, border-color 0.25s, color 0.25s, width 0.3s cubic-bezier(0.34, 1, 0.64, 1), padding 0.3s, border-radius 0.3s, opacity 0.1s;
|
||
flex-shrink: 0;
|
||
overflow: hidden;
|
||
}
|
||
/* Instant feedback while the send handler is spinning up before streaming begins */
|
||
.send-btn.send-pending {
|
||
opacity: 0.55;
|
||
pointer-events: none;
|
||
animation: send-pending-pulse 0.9s ease-in-out infinite;
|
||
}
|
||
@keyframes send-pending-pulse {
|
||
0%, 100% { opacity: 0.55; }
|
||
50% { opacity: 0.85; }
|
||
}
|
||
/* Send button icon transitions — send mode forces no animation */
|
||
.send-btn[data-mode="send"] svg { animation: none !important; }
|
||
/* Spin out: + rotates away, then arrow spins in via anim-spin */
|
||
.send-btn.anim-spin-swap svg { animation: btn-spin-out 0.15s ease-in forwards; }
|
||
.send-btn.anim-spin-swap .send-btn-label { opacity: 0; transition: opacity 0.1s; }
|
||
@keyframes btn-spin-out {
|
||
0% { transform: rotate(0deg) scale(1); opacity: 1; }
|
||
100% { transform: rotate(90deg) scale(0); opacity: 0; }
|
||
}
|
||
.send-btn.anim-spin svg { animation: btn-spin-in 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); }
|
||
.send-btn.anim-launch svg { animation: btn-launch 0.3s cubic-bezier(0.6, 0, 0.4, 1) forwards; }
|
||
.send-btn.anim-land svg { animation: btn-land 0.25s cubic-bezier(0.34, 1.56, 0.64, 1); }
|
||
@keyframes btn-spin-in {
|
||
0% { transform: rotate(-180deg) scale(0); opacity: 0; }
|
||
100% { transform: rotate(0deg) scale(1); opacity: 1; }
|
||
}
|
||
/* Arrow flies UP and OUT of the button before the stop icon lands in.
|
||
Stays opaque most of the way so it reads as a real launch instead of a
|
||
fade-in-place. .anim-launch temporarily lifts the button's overflow so
|
||
the icon escapes the 32px box. */
|
||
@keyframes btn-launch {
|
||
0% { transform: translateY(0) scale(1); opacity: 1; }
|
||
70% { transform: translateY(-30px) scale(0.95); opacity: 1; }
|
||
100% { transform: translateY(-58px) scale(0.55); opacity: 0; }
|
||
}
|
||
.send-btn.anim-launch { overflow: visible !important; }
|
||
.send-btn.anim-launch svg { transform-origin: 50% 50%; }
|
||
@keyframes btn-land {
|
||
0% { transform: translateY(10px) scale(0.3); opacity: 0; }
|
||
100% { transform: translateY(0) scale(1); opacity: 1; }
|
||
}
|
||
/* Stop button — Processing: Siren pulse, Receiving: Quarter turn spin */
|
||
.send-btn[data-mode="streaming"][data-phase="processing"] svg {
|
||
animation: siren-icon 1.5s ease-in-out infinite;
|
||
}
|
||
.send-btn[data-mode="streaming"][data-phase="receiving"] svg {
|
||
animation: quarter-turn 2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
||
}
|
||
@keyframes siren-icon {
|
||
0%, 100% { transform: scale(1); }
|
||
50% { transform: scale(0.8); }
|
||
}
|
||
@keyframes quarter-turn {
|
||
0%, 15% { transform: rotate(0deg); }
|
||
25%, 40% { transform: rotate(90deg); }
|
||
50%, 65% { transform: rotate(180deg); }
|
||
75%, 90% { transform: rotate(270deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
.send-btn:hover {
|
||
background: var(--send-btn-hover, color-mix(in srgb, var(--red) 80%, white));
|
||
color: #fff;
|
||
}
|
||
.send-btn.mic-mode,
|
||
.send-btn.newchat-mode {
|
||
/* Idle / new-chat / mic states blend the picked Send Btn color into
|
||
the panel so the user's color choice is visible across every state,
|
||
not only briefly while typing. */
|
||
background: color-mix(in srgb, var(--send-btn-bg, var(--red)) 30%, var(--panel, #2a2a2a));
|
||
color: var(--fg);
|
||
border: 1px solid var(--border);
|
||
transition: width 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.25s, border-color 0.25s, color 0.25s, border-radius 0.3s;
|
||
}
|
||
.send-btn.mic-mode:hover,
|
||
.send-btn.newchat-mode:hover {
|
||
background: color-mix(in srgb, var(--send-btn-hover, var(--send-btn-bg, var(--red))) 85%, var(--panel, #2a2a2a));
|
||
color: #fff;
|
||
}
|
||
/* Hover: just spin the + 90° (no size change). The "New chat" affordance
|
||
is the tooltip (title="New chat") plus the icon rotation.
|
||
Gated on data-mode="newchat" so the arrow variant (empty-session state
|
||
which keeps the newchat-mode class but shows the send arrow) does NOT
|
||
rotate on hover. */
|
||
.send-btn[data-mode="newchat"] svg {
|
||
transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||
}
|
||
.send-btn[data-mode="newchat"]:hover svg {
|
||
transform: rotate(90deg);
|
||
}
|
||
/* "New Session" expanding label */
|
||
.send-btn-label {
|
||
width: 0;
|
||
max-width: 0;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
letter-spacing: 0.3px;
|
||
opacity: 0;
|
||
padding: 0;
|
||
margin: 0;
|
||
transition: max-width 0.35s cubic-bezier(0.34, 1.2, 0.64, 1), width 0.35s, opacity 0.25s, margin-left 0.25s;
|
||
}
|
||
.send-btn.newchat-expanded .send-btn-label {
|
||
width: auto;
|
||
max-width: 50px;
|
||
opacity: 1;
|
||
margin-left: 4px;
|
||
}
|
||
.send-btn.newchat-expanded {
|
||
width: 68px;
|
||
padding: 0 10px 0 8px;
|
||
}
|
||
.send-btn.recording {
|
||
background: var(--red) !important;
|
||
color: #fff !important;
|
||
border: none !important;
|
||
animation: pulse-recording 1.5s infinite;
|
||
}
|
||
@keyframes pulse-recording {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.7; }
|
||
}
|
||
/* Mode toggle — Agent / Chat */
|
||
.mode-toggle {
|
||
display: flex;
|
||
flex-shrink: 0;
|
||
height: 28px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
.mode-toggle::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 50%;
|
||
height: 100%;
|
||
background: color-mix(in srgb, var(--fg) 10%, transparent);
|
||
border-radius: 9px;
|
||
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||
z-index: 0;
|
||
}
|
||
.mode-toggle.mode-chat::before,
|
||
.mode-toggle.mode-right::before {
|
||
transform: translateX(100%);
|
||
}
|
||
.mode-toggle.mode-toggle-three::before {
|
||
width: 33.3333%;
|
||
}
|
||
.mode-toggle.mode-toggle-three.mode-mid::before {
|
||
transform: translateX(100%);
|
||
}
|
||
.mode-toggle.mode-toggle-three.mode-third::before {
|
||
transform: translateX(200%);
|
||
}
|
||
#mode-agent-btn {
|
||
border-radius: 10px 0 0 10px;
|
||
}
|
||
#mode-chat-btn {
|
||
border-radius: 0 10px 10px 0;
|
||
}
|
||
.mode-toggle-btn {
|
||
background: none;
|
||
border: none;
|
||
color: color-mix(in srgb, var(--fg) 40%, transparent);
|
||
cursor: pointer;
|
||
padding: 0 10px;
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
font-family: inherit;
|
||
transition: color 0.2s;
|
||
white-space: nowrap;
|
||
height: 100%;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
.mode-toggle-btn:not(.active):hover {
|
||
color: color-mix(in srgb, var(--fg) 60%, transparent);
|
||
}
|
||
.mode-toggle-btn:hover {
|
||
background: none !important;
|
||
border-color: transparent !important;
|
||
}
|
||
.mode-toggle-btn.active,
|
||
.mode-toggle-btn.active:hover,
|
||
.mode-toggle-btn.active:focus {
|
||
color: var(--fg) !important;
|
||
background: none !important;
|
||
border-color: transparent !important;
|
||
cursor: default;
|
||
}
|
||
.mode-toggle-btn + .mode-toggle-btn {
|
||
border-left: none;
|
||
}
|
||
@media (max-width: 768px) {
|
||
.mode-toggle.mode-toggle-three [data-llama-mode="unified"] > span {
|
||
font-size: 0;
|
||
}
|
||
.mode-toggle.mode-toggle-three [data-llama-mode="unified"] > span::after {
|
||
content: 'Unif';
|
||
font-size: 11px;
|
||
}
|
||
}
|
||
/* Message count badge in the chat-meta header (next to the title).
|
||
Auto-hides when empty so a brand-new chat doesn't show "0 msgs". */
|
||
.chat-meta-count {
|
||
font-size: inherit;
|
||
font-weight: 400;
|
||
color: color-mix(in srgb, var(--fg) 35%, transparent);
|
||
white-space: nowrap;
|
||
user-select: none;
|
||
margin-left: 6px;
|
||
}
|
||
.chat-meta-count:empty { display: none; }
|
||
/* Session cost indicator (next to chevron in header) */
|
||
.session-cost-display {
|
||
font-size: inherit;
|
||
font-weight: 400;
|
||
color: color-mix(in srgb, var(--fg) 35%, transparent);
|
||
white-space: nowrap;
|
||
user-select: none;
|
||
margin-left: 7px;
|
||
margin-right: 5px;
|
||
}
|
||
.chat-context-pill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 3px;
|
||
height: auto;
|
||
padding: 0;
|
||
margin-left: 7px;
|
||
border: 0;
|
||
border-radius: 0;
|
||
background: transparent;
|
||
color: color-mix(in srgb, var(--fg) 35%, transparent);
|
||
font-family: inherit;
|
||
font-size: 0.7rem;
|
||
font-weight: 400;
|
||
line-height: 1;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
position: relative;
|
||
z-index: 2;
|
||
--ctx-stroke: var(--color-muted, #888);
|
||
transition: color 0.15s, opacity 0.15s;
|
||
}
|
||
.chat-context-pill[hidden] {
|
||
display: none !important;
|
||
}
|
||
.chat-context-pill:hover,
|
||
.chat-context-pill.open {
|
||
color: color-mix(in srgb, var(--fg) 70%, transparent);
|
||
--ctx-stroke: var(--ctx-color);
|
||
border: 0;
|
||
background: transparent;
|
||
}
|
||
.chat-context-pill.loading {
|
||
opacity: 0.55;
|
||
}
|
||
.chat-context-pill.warn {
|
||
color: var(--orange, #f59e0b);
|
||
background: transparent;
|
||
}
|
||
.chat-context-pill.danger {
|
||
color: var(--red, #ef4444);
|
||
background: transparent;
|
||
}
|
||
.chat-context-pill svg {
|
||
flex-shrink: 0;
|
||
}
|
||
.chat-context-pill .ctx-ring-pct {
|
||
color: currentColor;
|
||
font-size: inherit;
|
||
font-weight: inherit;
|
||
line-height: 1;
|
||
}
|
||
.chat-context-dot {
|
||
display: none;
|
||
}
|
||
.chat-context-popup {
|
||
position: fixed;
|
||
z-index: 10060;
|
||
width: 238px;
|
||
padding: 10px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
background: var(--panel);
|
||
color: var(--fg);
|
||
box-shadow: 0 12px 34px color-mix(in srgb, #000 26%, transparent);
|
||
font-size: 11px;
|
||
line-height: 1.35;
|
||
}
|
||
.chat-context-popup.warn {
|
||
color: var(--orange, #f59e0b);
|
||
}
|
||
.chat-context-popup.danger {
|
||
color: var(--red, #ef4444);
|
||
}
|
||
.chat-context-popup-title {
|
||
font-weight: 650;
|
||
margin-bottom: 8px;
|
||
color: var(--accent, var(--red));
|
||
}
|
||
.chat-context-popup-bar {
|
||
height: 6px;
|
||
overflow: hidden;
|
||
border-radius: 999px;
|
||
background: color-mix(in srgb, var(--fg) 10%, transparent);
|
||
margin-bottom: 6px;
|
||
}
|
||
.chat-context-popup-fill {
|
||
height: 100%;
|
||
width: 0;
|
||
border-radius: inherit;
|
||
background: var(--red, var(--fg));
|
||
}
|
||
.chat-context-popup-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 10px;
|
||
margin-top: 4px;
|
||
color: color-mix(in srgb, var(--fg) 70%, transparent);
|
||
}
|
||
.chat-context-popup-row span:last-child {
|
||
color: var(--fg);
|
||
text-align: right;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
max-width: 148px;
|
||
}
|
||
.chat-context-toggle-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 10px;
|
||
margin-top: 8px;
|
||
padding-top: 8px;
|
||
border-top: 1px solid color-mix(in srgb, var(--border) 45%, transparent);
|
||
}
|
||
.chat-context-toggle-copy {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1px;
|
||
min-width: 0;
|
||
color: color-mix(in srgb, var(--fg) 76%, transparent);
|
||
}
|
||
.chat-context-toggle-copy > span:first-child {
|
||
white-space: nowrap;
|
||
color: var(--accent, var(--red));
|
||
}
|
||
.chat-context-toggle-state {
|
||
font-size: 10px;
|
||
color: color-mix(in srgb, var(--fg) 45%, transparent);
|
||
}
|
||
.chat-context-toggle {
|
||
position: relative;
|
||
flex: 0 0 auto;
|
||
width: 32px;
|
||
height: 18px;
|
||
border: 1px solid color-mix(in srgb, var(--border) 75%, transparent);
|
||
border-radius: 999px;
|
||
background: color-mix(in srgb, var(--fg) 8%, transparent);
|
||
cursor: pointer;
|
||
transition: background 0.15s, border-color 0.15s, opacity 0.15s;
|
||
}
|
||
.chat-context-toggle::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: 3px;
|
||
left: 3px;
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
background: color-mix(in srgb, var(--fg) 62%, transparent);
|
||
transition: transform 0.15s, background 0.15s;
|
||
}
|
||
.chat-context-toggle.active {
|
||
border-color: color-mix(in srgb, var(--accent) 55%, transparent);
|
||
background: color-mix(in srgb, var(--accent) 18%, transparent);
|
||
}
|
||
.chat-context-toggle.active::after {
|
||
transform: translateX(14px);
|
||
background: var(--fg);
|
||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--fg) 18%, transparent);
|
||
}
|
||
.chat-context-toggle:disabled {
|
||
opacity: 0.6;
|
||
cursor: default;
|
||
}
|
||
.chat-context-threshold-row {
|
||
margin-top: 8px;
|
||
}
|
||
.chat-context-threshold-top {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 10px;
|
||
margin-bottom: 5px;
|
||
color: color-mix(in srgb, var(--fg) 70%, transparent);
|
||
}
|
||
.chat-context-threshold-top span:last-child {
|
||
color: var(--fg);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.chat-context-threshold-slider {
|
||
width: 100%;
|
||
margin: 0;
|
||
}
|
||
.chat-context-compact-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 1px;
|
||
width: 100%;
|
||
height: 26px;
|
||
margin-top: 9px;
|
||
border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
|
||
border-radius: 6px;
|
||
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
||
color: var(--accent);
|
||
cursor: pointer;
|
||
font: inherit;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
}
|
||
.chat-context-compact-btn svg { display: block; flex: 0 0 auto; margin-left: -2px; }
|
||
.chat-context-compact-btn svg circle:last-child {
|
||
stroke: var(--accent, var(--red));
|
||
transition: stroke-dasharray 0.22s ease;
|
||
}
|
||
.chat-context-compact-btn:hover svg circle:last-child {
|
||
stroke-dasharray: 35 0;
|
||
}
|
||
.chat-context-compact-btn:hover {
|
||
background: color-mix(in srgb, var(--accent) 18%, transparent);
|
||
}
|
||
.chat-context-popup,
|
||
.chat-context-popup * {
|
||
font-size: 10px !important;
|
||
}
|
||
/* Model picker — input bar drop-up */
|
||
.model-picker-wrap {
|
||
position: relative;
|
||
flex-shrink: 0;
|
||
}
|
||
.model-picker-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
height: 21px;
|
||
padding: 0 6px;
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
font-family: inherit;
|
||
background: none;
|
||
border: 1px solid transparent;
|
||
border-radius: 4px;
|
||
/* 65% mix lifts the model label above the WCAG AA 4.5:1 threshold
|
||
against the dark chat-bar (40% only reached ~2.9:1). */
|
||
color: color-mix(in srgb, var(--fg) 65%, transparent);
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
transition: background 0.15s, color 0.15s, border-color 0.15s;
|
||
}
|
||
.model-picker-btn:hover {
|
||
border-color: var(--border);
|
||
background: color-mix(in srgb, var(--fg) 8%, transparent);
|
||
color: var(--fg);
|
||
}
|
||
.model-picker-btn #model-picker-label {
|
||
max-width: 120px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.model-picker-btn svg {
|
||
flex-shrink: 0;
|
||
opacity: 0.5;
|
||
}
|
||
.model-picker-logo {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
vertical-align: -2px;
|
||
}
|
||
.model-picker-logo svg {
|
||
width: 12px;
|
||
height: 12px;
|
||
opacity: 0.7;
|
||
}
|
||
.model-picker-menu {
|
||
position: absolute;
|
||
bottom: calc(100% + 16px);
|
||
right: 0;
|
||
z-index: 250;
|
||
min-width: 260px;
|
||
max-width: 360px;
|
||
background: var(--panel);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
box-shadow: 0 -4px 20px rgba(0,0,0,0.4);
|
||
padding: 6px;
|
||
animation: picker-roll-up 0.2s ease-out;
|
||
transform-origin: bottom right;
|
||
}
|
||
.model-picker-menu.closing {
|
||
animation: picker-roll-down 0.15s ease-in forwards;
|
||
}
|
||
.model-picker-menu.hidden { display: none; }
|
||
.model-picker-search-row {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) 30px;
|
||
gap: 4px;
|
||
align-items: center;
|
||
margin-bottom: 4px;
|
||
transition: grid-template-columns 0.18s ease, gap 0.18s ease;
|
||
}
|
||
.model-picker-menu.no-models .model-picker-search-row {
|
||
margin-bottom: 0;
|
||
}
|
||
.model-picker-search-row.searching {
|
||
grid-template-columns: minmax(0, 1fr) 0px;
|
||
gap: 0;
|
||
}
|
||
.model-picker-search-row.searching .model-picker-action-btn {
|
||
opacity: 0;
|
||
transform: translateX(10px) scale(0.88);
|
||
pointer-events: none;
|
||
}
|
||
.model-picker-search-wrap {
|
||
position: relative;
|
||
min-width: 0;
|
||
}
|
||
.model-picker-menu input[type="text"] {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
padding: 6px 30px 6px 8px;
|
||
font-size: 0.82em;
|
||
border: 1px solid var(--border);
|
||
border-radius: 4px;
|
||
background: var(--bg);
|
||
color: var(--fg);
|
||
font-family: inherit;
|
||
outline: none;
|
||
min-width: 0;
|
||
transition: border-color 0.15s, padding 0.18s ease, background 0.18s ease;
|
||
}
|
||
.model-picker-menu input[type="text"]:focus {
|
||
border-color: var(--red);
|
||
}
|
||
.model-picker-menu input[type="text"]::placeholder {
|
||
color: color-mix(in srgb, var(--fg) 30%, transparent);
|
||
}
|
||
.model-picker-refresh-btn {
|
||
appearance: none;
|
||
position: absolute;
|
||
top: 50%;
|
||
right: 4px;
|
||
transform: translateY(-50%);
|
||
width: 22px;
|
||
height: 22px;
|
||
border: 0;
|
||
border-radius: 4px;
|
||
background: transparent;
|
||
color: color-mix(in srgb, var(--fg) 54%, transparent);
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0;
|
||
cursor: pointer;
|
||
}
|
||
.model-picker-refresh-btn:hover,
|
||
.model-picker-refresh-btn:focus-visible {
|
||
color: var(--fg);
|
||
background: color-mix(in srgb, var(--fg) 8%, transparent);
|
||
outline: none;
|
||
}
|
||
.model-picker-refresh-btn:disabled {
|
||
opacity: 0.7;
|
||
cursor: default;
|
||
}
|
||
.model-picker-refresh-btn.spinning svg {
|
||
animation: model-picker-refresh-spin 0.75s linear infinite;
|
||
}
|
||
.model-picker-action-btn {
|
||
appearance: none;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 30px;
|
||
height: 30px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 4px;
|
||
background: color-mix(in srgb, var(--fg) 4%, transparent);
|
||
color: color-mix(in srgb, var(--fg) 66%, transparent);
|
||
cursor: pointer;
|
||
padding: 0;
|
||
overflow: hidden;
|
||
transform: translateX(0) scale(1);
|
||
transition: opacity 0.16s ease, transform 0.18s ease, border-color 0.15s, color 0.15s, background 0.15s;
|
||
}
|
||
.model-picker-action-btn:hover,
|
||
.model-picker-action-btn:focus-visible {
|
||
border-color: var(--red);
|
||
color: var(--fg);
|
||
background: color-mix(in srgb, var(--red) 10%, var(--panel));
|
||
outline: none;
|
||
}
|
||
.model-picker-action-btn.primary {
|
||
color: var(--red);
|
||
background: color-mix(in srgb, var(--red) 8%, transparent);
|
||
}
|
||
.model-picker-action-btn svg {
|
||
width: 14px;
|
||
height: 14px;
|
||
transition: transform 0.28s ease;
|
||
}
|
||
.model-picker-action-btn:hover svg,
|
||
.model-picker-action-btn:focus-visible svg {
|
||
transform: rotate(90deg);
|
||
}
|
||
.model-picker-list {
|
||
max-height: min(280px, 50dvh);
|
||
overflow-y: auto;
|
||
}
|
||
.model-picker-list.is-loading {
|
||
max-height: min(120px, 28dvh);
|
||
overflow: hidden;
|
||
}
|
||
.model-picker-list.is-empty {
|
||
max-height: 0;
|
||
overflow: hidden;
|
||
}
|
||
.model-picker-list .model-picker-loading-row {
|
||
justify-content: center;
|
||
gap: 7px;
|
||
min-height: 38px;
|
||
padding: 8px 10px;
|
||
color: color-mix(in srgb, var(--fg) 58%, transparent);
|
||
font-size: 0.82em;
|
||
}
|
||
.model-picker-list .model-picker-loading-row .ai-spinner-whirlpool {
|
||
position: relative;
|
||
top: 1px;
|
||
}
|
||
.model-picker-list .model-switch-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 5px 8px;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 0.82em;
|
||
color: var(--fg);
|
||
gap: 6px;
|
||
}
|
||
.model-picker-list .model-switch-item:hover {
|
||
background: color-mix(in srgb, var(--red) 8%, transparent);
|
||
}
|
||
.model-picker-list .model-switch-empty {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 5px 8px;
|
||
color: color-mix(in srgb, var(--fg) 50%, transparent);
|
||
font-size: 0.82em;
|
||
}
|
||
.model-picker-list .mp-section-label {
|
||
font-size: 0.72em;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
opacity: 0.4;
|
||
padding: 6px 8px 2px;
|
||
}
|
||
.model-picker-list .mp-section-label:first-child {
|
||
padding-top: 2px;
|
||
}
|
||
/* Model name takes the slack so the endpoint label + favorite dot sit on the right. */
|
||
.model-picker-list .model-switch-item .mp-model-name {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.model-picker-list .model-switch-item .model-switch-ep {
|
||
flex: 0 1 auto;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
font-size: 0.9em;
|
||
opacity: 0.45;
|
||
}
|
||
/* Keyboard navigation highlight (Arrow keys in the search box). */
|
||
.model-picker-list .model-switch-item.kb-active {
|
||
background: color-mix(in srgb, var(--red) 14%, transparent);
|
||
}
|
||
/* Inline favorite bookmark — always visible (works on touch), active when on. */
|
||
.model-picker-list .mp-fav-dot {
|
||
flex: 0 0 auto;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 30px;
|
||
height: 24px;
|
||
margin: -5px -8px -5px 8px;
|
||
padding: 0;
|
||
border: none;
|
||
background: transparent;
|
||
cursor: pointer;
|
||
color: color-mix(in srgb, var(--fg) 22%, transparent);
|
||
font-family: inherit;
|
||
font-size: 13px;
|
||
line-height: 1;
|
||
transition: color 0.15s ease, opacity 0.15s ease, transform 0.12s ease;
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
.model-picker-list .mp-fav-dot:hover {
|
||
color: color-mix(in srgb, var(--fg) 68%, transparent);
|
||
}
|
||
.model-picker-list .mp-fav-dot:focus-visible {
|
||
outline: none;
|
||
color: color-mix(in srgb, var(--fg) 68%, transparent);
|
||
}
|
||
.model-picker-list .mp-fav-dot.active {
|
||
color: var(--accent, var(--red));
|
||
opacity: 1;
|
||
}
|
||
.model-picker-list .mp-fav-dot svg { display: block; }
|
||
.model-picker-list .mp-fav-dot.active:hover {
|
||
color: var(--accent, var(--red));
|
||
opacity: 0.72;
|
||
}
|
||
.model-picker-list .mp-fav-dot.pulse {
|
||
animation: mpFavPulse 0.32s ease-out;
|
||
}
|
||
@keyframes mpFavPulse {
|
||
0% { text-shadow: 0 0 0 color-mix(in srgb, var(--accent, var(--red)) 0%, transparent); }
|
||
45% { text-shadow: 0 0 10px color-mix(in srgb, var(--accent, var(--red)) 60%, transparent); }
|
||
100% { text-shadow: 0 0 0 color-mix(in srgb, var(--accent, var(--red)) 0%, transparent); }
|
||
}
|
||
/* First-run hint when a large catalog has no Recent/Favorites yet. */
|
||
.model-picker-list .mp-empty-hint {
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
padding: 14px 8px;
|
||
text-align: center;
|
||
}
|
||
.model-picker-list .mp-empty-hint .mp-empty-title {
|
||
font-size: 1.05em;
|
||
color: color-mix(in srgb, var(--fg) 70%, transparent);
|
||
}
|
||
.model-picker-list .mp-empty-hint .mp-empty-sub {
|
||
font-size: 0.92em;
|
||
opacity: 0.7;
|
||
}
|
||
/* Provider group headers */
|
||
.model-picker-list .mp-provider-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 5px 8px;
|
||
cursor: pointer;
|
||
font-size: 0.78em;
|
||
font-weight: 500;
|
||
color: var(--fg);
|
||
border-radius: 4px;
|
||
user-select: none;
|
||
}
|
||
.model-picker-list .mp-provider-header:hover {
|
||
background: color-mix(in srgb, var(--fg) 6%, transparent);
|
||
}
|
||
.model-picker-list .mp-provider-chevron {
|
||
display: inline-flex;
|
||
opacity: 0.4;
|
||
transition: transform 0.2s, opacity 0.15s;
|
||
flex-shrink: 0;
|
||
}
|
||
.model-picker-list .mp-provider-header:hover .mp-provider-chevron {
|
||
opacity: 0.7;
|
||
}
|
||
.model-picker-list .mp-provider-chevron.collapsed {
|
||
transform: rotate(-90deg);
|
||
}
|
||
.model-picker-list .mp-provider-name { flex: 1; }
|
||
.model-picker-list .mp-provider-count { font-size: 0.85em; opacity: 0.4; }
|
||
/* Domino expand (15% faster than sidebar) */
|
||
.mp-provider-group.mp-just-expanded .model-switch-item {
|
||
animation: mp-domino-in 0.31s cubic-bezier(0.22, 1.61, 0.36, 1) backwards;
|
||
}
|
||
.mp-provider-group.mp-just-expanded .model-switch-item:nth-child(1) { animation-delay: 0.035s; }
|
||
.mp-provider-group.mp-just-expanded .model-switch-item:nth-child(2) { animation-delay: 0.07s; }
|
||
.mp-provider-group.mp-just-expanded .model-switch-item:nth-child(3) { animation-delay: 0.105s; }
|
||
.mp-provider-group.mp-just-expanded .model-switch-item:nth-child(4) { animation-delay: 0.14s; }
|
||
.mp-provider-group.mp-just-expanded .model-switch-item:nth-child(5) { animation-delay: 0.175s; }
|
||
.mp-provider-group.mp-just-expanded .model-switch-item:nth-child(6) { animation-delay: 0.21s; }
|
||
.mp-provider-group.mp-just-expanded .model-switch-item:nth-child(7) { animation-delay: 0.245s; }
|
||
.mp-provider-group.mp-just-expanded .model-switch-item:nth-child(8) { animation-delay: 0.28s; }
|
||
.mp-provider-group.mp-just-expanded .model-switch-item:nth-child(9) { animation-delay: 0.315s; }
|
||
.mp-provider-group.mp-just-expanded .model-switch-item:nth-child(10) { animation-delay: 0.35s; }
|
||
@keyframes mp-domino-in {
|
||
0% { opacity: 0; transform: translateY(6px) scale(0.94); }
|
||
60% { opacity: 1; }
|
||
100% { opacity: 1; transform: translateY(0) scale(1); }
|
||
}
|
||
/* Comfortable touch targets on phones / narrow screens. */
|
||
@media (hover: none) and (pointer: coarse), (max-width: 768px) {
|
||
.model-picker-list .model-switch-item {
|
||
padding-top: 8px;
|
||
padding-bottom: 8px;
|
||
}
|
||
.model-picker-list .mp-fav-dot {
|
||
width: 30px;
|
||
height: 30px;
|
||
margin: -7px -8px -7px 8px;
|
||
}
|
||
}
|
||
/* Overflow "+" menu */
|
||
.overflow-wrapper {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
.plus-active-dot {
|
||
position: absolute;
|
||
top: 4px;
|
||
right: 2px;
|
||
width: 6px;
|
||
height: 6px;
|
||
background: var(--fg);
|
||
border-radius: 50%;
|
||
display: none;
|
||
}
|
||
.overflow-plus-btn.has-active .plus-active-dot {
|
||
display: block;
|
||
}
|
||
.overflow-menu {
|
||
position: fixed;
|
||
background: var(--panel);
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
padding: 4px;
|
||
min-width: 170px;
|
||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
|
||
z-index: 1000;
|
||
/* Container spring-in: the rounded panel scales/grows out of the
|
||
chevron's position before the menu items domino in on top. */
|
||
transform-origin: bottom left;
|
||
animation: overflow-menu-pop 0.08s ease-out;
|
||
}
|
||
@keyframes overflow-menu-pop {
|
||
0% { transform: scale(0.96) translateY(2px); opacity: 0; }
|
||
100% { transform: scale(1) translateY(0); opacity: 1; }
|
||
}
|
||
.overflow-menu.hidden {
|
||
display: none;
|
||
}
|
||
/* Closing state: JS adds `.closing` to play the fold-in animation,
|
||
waits for it to finish, then flips to `.hidden`. Container
|
||
scales/translates back into the chevron while items peel off from
|
||
the top down so the menu collapses into its anchor. */
|
||
.overflow-menu.closing {
|
||
animation: overflow-menu-pop-out 0.08s ease-in forwards;
|
||
animation-delay: 0s;
|
||
}
|
||
@keyframes overflow-menu-pop-out {
|
||
0% { transform: scale(1) translateY(0); opacity: 1; }
|
||
100% { transform: scale(0.96) translateY(2px); opacity: 0; }
|
||
}
|
||
.overflow-menu-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
width: 100%;
|
||
padding: 8px 12px;
|
||
background: none;
|
||
border: none;
|
||
color: var(--fg);
|
||
opacity: 0.7;
|
||
cursor: pointer;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
font-family: inherit;
|
||
transition: background 0.15s, opacity 0.15s, color 0.15s;
|
||
/* Domino-style cascade: each item slides up + fades in with a tiny
|
||
delay after the previous one (set via nth-last-child below so the
|
||
BOTTOM item appears first and the cascade rolls upward — visually
|
||
feels like the menu is "stacking up" from the chevron). The
|
||
container itself springs in via .overflow-menu's keyframe. */
|
||
animation: overflow-item-in 0.08s ease-out backwards;
|
||
animation-delay: 0s !important;
|
||
}
|
||
.overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(1) { animation-delay: 0.06s; }
|
||
.overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(2) { animation-delay: 0.10s; }
|
||
.overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(3) { animation-delay: 0.14s; }
|
||
.overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(4) { animation-delay: 0.18s; }
|
||
.overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(5) { animation-delay: 0.22s; }
|
||
.overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(6) { animation-delay: 0.26s; }
|
||
.overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(7) { animation-delay: 0.30s; }
|
||
.overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(8) { animation-delay: 0.34s; }
|
||
.overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(9) { animation-delay: 0.38s; }
|
||
.overflow-menu:not(.hidden):not(.closing) .overflow-menu-item:nth-last-child(10) { animation-delay: 0.42s; }
|
||
@keyframes overflow-item-in {
|
||
0% { opacity: 0; transform: translateY(10px) translateX(-6px) scale(0.9); }
|
||
60% { opacity: 1; }
|
||
100% { opacity: 1; transform: translateY(0) translateX(0) scale(1); }
|
||
}
|
||
/* Fold-in: items peel off top-down (mirror of the open's bottom-up
|
||
cascade) so the menu visibly empties before the container scales
|
||
back into the chevron. */
|
||
.overflow-menu.closing .overflow-menu-item {
|
||
animation: overflow-item-out 0.06s ease-in forwards;
|
||
animation-delay: 0s !important;
|
||
}
|
||
.overflow-menu.closing .overflow-menu-item:nth-child(1) { animation-delay: 0.00s; }
|
||
.overflow-menu.closing .overflow-menu-item:nth-child(2) { animation-delay: 0.02s; }
|
||
.overflow-menu.closing .overflow-menu-item:nth-child(3) { animation-delay: 0.04s; }
|
||
.overflow-menu.closing .overflow-menu-item:nth-child(4) { animation-delay: 0.06s; }
|
||
.overflow-menu.closing .overflow-menu-item:nth-child(5) { animation-delay: 0.08s; }
|
||
.overflow-menu.closing .overflow-menu-item:nth-child(6) { animation-delay: 0.10s; }
|
||
.overflow-menu.closing .overflow-menu-item:nth-child(7) { animation-delay: 0.12s; }
|
||
.overflow-menu.closing .overflow-menu-item:nth-child(8) { animation-delay: 0.14s; }
|
||
.overflow-menu.closing .overflow-menu-item:nth-child(9) { animation-delay: 0.16s; }
|
||
.overflow-menu.closing .overflow-menu-item:nth-child(10) { animation-delay: 0.18s; }
|
||
@keyframes overflow-item-out {
|
||
0% { opacity: 1; transform: translateY(0) translateX(0) scale(1); }
|
||
100% { opacity: 0; transform: translateY(6px) translateX(-3px) scale(0.92); }
|
||
}
|
||
.overflow-menu-item:hover {
|
||
opacity: 1;
|
||
background: color-mix(in srgb, var(--red) 10%, transparent);
|
||
}
|
||
#overflow-attach-btn {
|
||
position: relative;
|
||
font-weight: 600;
|
||
}
|
||
#overflow-attach-btn svg {
|
||
transition: transform 0.16s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||
}
|
||
.overflow-menu-item.active {
|
||
opacity: 1;
|
||
color: var(--fg);
|
||
}
|
||
.overflow-active-dot {
|
||
width: 6px;
|
||
height: 6px;
|
||
background: var(--fg);
|
||
border-radius: 50%;
|
||
margin-left: auto;
|
||
display: none;
|
||
flex-shrink: 0;
|
||
}
|
||
.overflow-menu-item.active .overflow-active-dot {
|
||
display: block;
|
||
}
|
||
.attach-strip {
|
||
display: flex;
|
||
gap: 6px;
|
||
flex-wrap: wrap;
|
||
margin: 0 0 8px;
|
||
min-height: 0;
|
||
}
|
||
.attach-strip:empty {
|
||
display: none;
|
||
}
|
||
.attach-strip {
|
||
display: flex;
|
||
gap: 6px;
|
||
flex-wrap: wrap;
|
||
margin: 6px 0 0;
|
||
min-height: 32px;
|
||
padding: 2px;
|
||
}
|
||
.attachment-placeholder {
|
||
display: flex;
|
||
align-items: center;
|
||
color: var(--fg);
|
||
opacity: 0.7;
|
||
font-style: italic;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
background: color-mix(in srgb, var(--fg) 6%, transparent);
|
||
}
|
||
.attach-btn { width:32px; display:grid; place-items:center; }
|
||
.hidden { display:none; }
|
||
.toggle { position:relative; display:inline-block; width:30px; height:16px; vertical-align:middle; }
|
||
.toggle input { opacity:0; width:0; height:0; }
|
||
.toggle .slider {
|
||
position:absolute; cursor:pointer; top:0; left:0; right:0; bottom:0;
|
||
background:color-mix(in srgb, var(--fg) 15%, transparent); transition:background .08s; border-radius:8px;
|
||
}
|
||
.toggle .slider:before {
|
||
position:absolute; content:""; height:12px; width:12px; left:2px; top:2px;
|
||
background:var(--panel); border-radius:50%; transform:translateX(0); transition:transform .08s;
|
||
box-shadow:0 1px 2px rgba(0,0,0,0.25);
|
||
}
|
||
.toggle input:checked + .slider { background:var(--red); }
|
||
.toggle input:checked + .slider:before { transform:translateX(14px); }
|
||
.copy-btn {
|
||
position:absolute; top:6px; right:6px;
|
||
background:var(--bg); color:var(--fg);
|
||
border:1px solid var(--border); border-radius:6px;
|
||
font-size:12px; height:24px; padding:0 8px; cursor:pointer;
|
||
opacity:0; transition:.15s;
|
||
}
|
||
.msg:hover .copy-btn { opacity:1; }
|
||
.role-timestamp {
|
||
font-size:0.7rem; color:var(--color-muted-alt); font-weight:normal; margin-left:6px;
|
||
}
|
||
.msg-footer {
|
||
display:flex; align-items:center; gap:6px;
|
||
flex-wrap: wrap;
|
||
margin-top:6px;
|
||
color:var(--color-muted-alt); font-size:0.75rem;
|
||
position: relative;
|
||
}
|
||
.msg-footer .timestamp {
|
||
font-size:0.75rem; color:var(--color-muted-alt);
|
||
margin:0; opacity:1;
|
||
}
|
||
.msg-footer-enter {
|
||
animation: msg-footer-enter 180ms ease-out both;
|
||
}
|
||
@keyframes msg-footer-enter {
|
||
from { opacity: 0; transform: translateY(2px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.msg-footer-enter { animation: none; }
|
||
}
|
||
.msg-footer .response-metrics {
|
||
font-size:0.75rem; color:var(--color-muted-alt);
|
||
transition: color .15s;
|
||
}
|
||
.msg-footer .response-metrics:hover { color:var(--fg); }
|
||
.agent-thread > .agent-thread-footer {
|
||
margin-top: 4px;
|
||
margin-left: 2px;
|
||
}
|
||
/* Keep the context ring and percentage together at the footer's left edge. */
|
||
.ctx-ring {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 3px;
|
||
margin-left: 0;
|
||
line-height: 0;
|
||
opacity: 0.6;
|
||
cursor: default;
|
||
transition: opacity 0.15s;
|
||
--ctx-stroke: var(--color-muted, #888);
|
||
}
|
||
.ctx-ring .ctx-ring-pct {
|
||
color: var(--color-muted, #888);
|
||
transition: color 0.1s ease;
|
||
}
|
||
.ctx-ring svg circle {
|
||
transition: stroke 0.1s ease;
|
||
}
|
||
.ctx-ring:hover {
|
||
opacity: 1;
|
||
--ctx-stroke: var(--ctx-color);
|
||
}
|
||
.ctx-ring:hover .ctx-ring-pct {
|
||
color: var(--ctx-color);
|
||
}
|
||
.ctx-ring-pct {
|
||
font-size: 0.7rem;
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
}
|
||
/* Context detail popup */
|
||
.ctx-detail-popup {
|
||
position: fixed;
|
||
z-index: 200;
|
||
background: var(--panel, var(--bg));
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
padding: 12px 14px;
|
||
box-shadow: 0 4px 20px rgba(0,0,0,0.4);
|
||
min-width: 220px;
|
||
max-width: 280px;
|
||
font-size: 0.85rem;
|
||
color: var(--fg);
|
||
}
|
||
.ctx-bar-wrap {
|
||
width: 100%;
|
||
height: 6px;
|
||
background: var(--border);
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
}
|
||
.ctx-bar-fill {
|
||
height: 100%;
|
||
border-radius: 4px;
|
||
transition: width 0.3s;
|
||
}
|
||
.ctx-compact-btn {
|
||
display: block;
|
||
width: 100%;
|
||
margin-top: 10px;
|
||
padding: 6px 0;
|
||
background: none;
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
color: var(--fg);
|
||
font-size: 0.8rem;
|
||
cursor: pointer;
|
||
transition: border-color 0.15s, color 0.15s;
|
||
}
|
||
.ctx-compact-btn:hover {
|
||
border-color: var(--accent, var(--red));
|
||
color: var(--accent, var(--red));
|
||
}
|
||
.ctx-compact-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: default;
|
||
}
|
||
.compact-wave {
|
||
display: inline-block;
|
||
color: var(--accent, var(--red));
|
||
letter-spacing: 1px;
|
||
font-size: 0.9em;
|
||
}
|
||
/* Memory-used indicator pill */
|
||
.memory-used-pill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
background: var(--panel);
|
||
border: 1px solid var(--border);
|
||
color: var(--fg);
|
||
font-size: 0.7rem;
|
||
padding: 2px 8px;
|
||
border-radius: 10px;
|
||
cursor: pointer;
|
||
opacity: 0.6;
|
||
transition: opacity 0.15s, background 0.15s;
|
||
white-space: nowrap;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
.memory-used-pill:hover { opacity: 1; background: var(--border); }
|
||
/* Nudge label text 1px down so it visually centers with the icon. */
|
||
.memory-used-pill-text { position: relative; top: 1px; display: inline-block; }
|
||
.memory-used-detail {
|
||
position: fixed;
|
||
background: var(--panel);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
padding: 6px 8px;
|
||
min-width: 200px;
|
||
max-width: min(360px, calc(100vw - 16px));
|
||
max-height: 50vh;
|
||
overflow-y: auto;
|
||
z-index: 300;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||
font-size: 0.75rem;
|
||
}
|
||
.memory-used-row {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 6px;
|
||
padding: 3px 0;
|
||
line-height: 1.3;
|
||
}
|
||
.memory-used-row + .memory-used-row {
|
||
border-top: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
|
||
}
|
||
.memory-used-badge {
|
||
flex-shrink: 0;
|
||
font-size: 0.7rem;
|
||
width: 16px;
|
||
text-align: center;
|
||
}
|
||
.memory-used-badge.pinned { color: var(--red); }
|
||
.memory-used-badge.recalled { color: var(--fg); opacity: 0.5; }
|
||
.memory-used-text {
|
||
color: var(--fg);
|
||
opacity: 0.85;
|
||
}
|
||
|
||
.msg-actions {
|
||
display:inline-flex; align-items:center; gap:4px;
|
||
}
|
||
.footer-copy-btn {
|
||
background:none; border:none;
|
||
color:var(--color-muted-alt); cursor:pointer;
|
||
padding:2px 6px; border-radius:4px;
|
||
transition: color .15s;
|
||
line-height:1;
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
}
|
||
.footer-copy-btn:hover { color:var(--accent); }
|
||
.footer-open-gallery-btn {
|
||
gap: 4px;
|
||
font-size: 11px;
|
||
}
|
||
/* Delete action — same chrome as copy/download/edit but hover reveals
|
||
the destructive red tint so the user can tell it's not a benign op. */
|
||
.footer-delete-btn:hover { color: var(--red); }
|
||
.regen-btn {
|
||
background:none; border:none;
|
||
color:var(--color-muted-alt); font-size:1.1rem; cursor:pointer;
|
||
padding:2px 6px; border-radius:4px;
|
||
transition: color .15s;
|
||
line-height:1;
|
||
}
|
||
.regen-btn:hover { color:var(--accent); }
|
||
.fork-btn {
|
||
background:none; border:none;
|
||
color:var(--color-muted-alt); font-size:1.1rem; cursor:pointer;
|
||
padding:2px 6px; border-radius:4px;
|
||
transition: color .15s;
|
||
line-height:1;
|
||
}
|
||
.fork-btn:hover { color:var(--accent); }
|
||
.msg-action-btn {
|
||
background:none; border:none;
|
||
color:var(--muted, var(--color-muted-alt)); font-size:1.1rem; cursor:pointer;
|
||
padding:2px 6px; border-radius:4px;
|
||
transition: color .15s;
|
||
line-height:1;
|
||
}
|
||
.msg-action-btn:hover { color:var(--accent); }
|
||
.msg-action-btn .resend-message-icon {
|
||
display:block;
|
||
width:13px;
|
||
height:13px;
|
||
transform:translateY(1px);
|
||
}
|
||
.msg-action-btn[data-action="shorten"] { position:relative; top:1px; font-size:1.25rem; }
|
||
.msg-delete-btn { position:relative; top:1px; }
|
||
.msg-delete-btn:hover { color:var(--red); }
|
||
.msg-more-btn {
|
||
font-size:0.7rem; letter-spacing:0.5px;
|
||
}
|
||
.msg-overflow-menu {
|
||
position:fixed;
|
||
background:var(--panel);
|
||
border:1px solid var(--border);
|
||
border-radius:6px;
|
||
padding:4px;
|
||
box-shadow:0 4px 16px rgba(0,0,0,0.4);
|
||
z-index:100;
|
||
min-width:150px;
|
||
}
|
||
.msg-overflow-item {
|
||
display:flex;
|
||
align-items:center;
|
||
gap:6px;
|
||
width:100%;
|
||
background:none;
|
||
border:none;
|
||
border-bottom:1px solid color-mix(in srgb, var(--border) 40%, transparent);
|
||
color:var(--fg);
|
||
font-size:0.8rem;
|
||
padding:5px 8px;
|
||
border-radius:4px;
|
||
cursor:pointer;
|
||
text-align:left;
|
||
font-family:inherit;
|
||
transition:background .1s;
|
||
}
|
||
.msg-overflow-item:last-child { border-bottom:none; }
|
||
.msg-overflow-item:hover {
|
||
background:color-mix(in srgb, var(--red) 8%, transparent);
|
||
}
|
||
.overflow-icon {
|
||
width:16px;
|
||
text-align:center;
|
||
flex-shrink:0;
|
||
font-size:1rem;
|
||
}
|
||
.overflow-icon .resend-message-icon {
|
||
display:block;
|
||
width:13px;
|
||
height:13px;
|
||
margin:0 auto;
|
||
}
|
||
.variant-nav {
|
||
display:inline-flex;
|
||
align-items:center;
|
||
gap:0px;
|
||
margin-left:auto;
|
||
font-size:0.85em;
|
||
font-family:inherit;
|
||
opacity:0.6;
|
||
}
|
||
.variant-divider {
|
||
opacity:0.25;
|
||
margin:0 4px 0 2px;
|
||
}
|
||
.variant-tag {
|
||
font-size:1.1em;
|
||
opacity:0.8;
|
||
margin-right:2px;
|
||
position:relative;
|
||
top:-1px;
|
||
}
|
||
.variant-tag-scissors {
|
||
top:-1px;
|
||
}
|
||
/* The ✂ "Rewrite shorter" action button glyph sits a touch low against the
|
||
other footer icons — nudge it up 2px. */
|
||
.msg-action-btn[data-action="shorten"] {
|
||
position: relative;
|
||
top: -2px;
|
||
}
|
||
/* The "?" Explain-simpler glyph also sits slightly low — nudge it up 1px. */
|
||
.msg-action-btn[data-action="explain"] {
|
||
position: relative;
|
||
top: -1px;
|
||
}
|
||
.variant-btn {
|
||
background:none;
|
||
border:none;
|
||
color:var(--fg);
|
||
cursor:pointer;
|
||
padding:4px 6px;
|
||
font-size:1em;
|
||
font-family:inherit;
|
||
opacity:0.7;
|
||
line-height:1;
|
||
}
|
||
.variant-btn:hover { opacity:1; color:var(--fg); }
|
||
.variant-btn:disabled { opacity:0.3; cursor:default; }
|
||
.variant-num {
|
||
background:none;
|
||
border:none;
|
||
color:var(--fg);
|
||
cursor:pointer;
|
||
padding:4px 4px;
|
||
font-size:1em;
|
||
font-family:inherit;
|
||
opacity:0.7;
|
||
line-height:1;
|
||
}
|
||
.variant-num:hover { opacity:1; color:var(--fg); }
|
||
.variant-num:disabled { opacity:0.5; cursor:default; }
|
||
.variant-slash {
|
||
opacity:0.4;
|
||
font-size:1em;
|
||
}
|
||
.continue-separator {
|
||
display:inline;
|
||
opacity:0.35;
|
||
font-size:0.85em;
|
||
font-style:italic;
|
||
}
|
||
.stopped-indicator {
|
||
color:var(--red);
|
||
margin-top:8px;
|
||
font-size:0.85em;
|
||
opacity:0.8;
|
||
display:flex;
|
||
align-items:center;
|
||
gap:8px;
|
||
flex-wrap:wrap;
|
||
}
|
||
|
||
/* Message edit UI */
|
||
.msg-edit-textarea {
|
||
background:var(--bg);
|
||
color:var(--fg);
|
||
border:1px solid var(--border);
|
||
border-radius:6px;
|
||
padding:10px;
|
||
font-family:inherit;
|
||
font-size:inherit;
|
||
line-height:1.6;
|
||
resize:vertical;
|
||
box-sizing:border-box;
|
||
}
|
||
.msg-edit-textarea:focus { outline:1px solid var(--hl-function, #61afef); border-color:var(--hl-function, #61afef); }
|
||
.msg-edit-bar {
|
||
display:flex;
|
||
gap:8px;
|
||
margin-top:6px;
|
||
}
|
||
.msg-edit-save, .msg-edit-cancel {
|
||
background:var(--bg);
|
||
color:var(--fg);
|
||
border:1px solid var(--border);
|
||
border-radius:6px;
|
||
padding:4px 14px;
|
||
cursor:pointer;
|
||
font-size:0.85em;
|
||
}
|
||
.msg-edit-save:hover { border-color:var(--color-save-green, #4caf50); color:var(--color-save-green, #4caf50); }
|
||
.msg-edit-cancel:hover { border-color:var(--red); color:var(--red); }
|
||
|
||
/* Edited indicator — similar to stopped-indicator */
|
||
.edited-indicator {
|
||
color:var(--fg);
|
||
margin-top:8px;
|
||
font-size:0.85em;
|
||
opacity:0.4;
|
||
font-style:italic;
|
||
}
|
||
.continue-btn {
|
||
background:none;
|
||
border:none;
|
||
color:var(--fg);
|
||
opacity:0.5;
|
||
cursor:pointer;
|
||
font-size:2.6em;
|
||
padding:2px 2px 0;
|
||
line-height:1;
|
||
}
|
||
.continue-btn:hover {
|
||
opacity:0.8;
|
||
}
|
||
.continue-btn.resume-btn {
|
||
display:inline-flex;
|
||
align-items:center;
|
||
justify-content:space-between;
|
||
gap:8px;
|
||
padding:3px 8px 3px 5px;
|
||
border:none;
|
||
border-radius:5px;
|
||
font-size:0.85em;
|
||
font-weight:400;
|
||
line-height:1.2;
|
||
position:relative;
|
||
top:2px;
|
||
}
|
||
.continue-btn.resume-btn:hover {
|
||
border:none;
|
||
}
|
||
.agent-finish-editor {
|
||
margin: 4px 0 6px 16px;
|
||
font-size: 0.8em;
|
||
}
|
||
.agent-finish-editor:disabled {
|
||
cursor: default;
|
||
opacity: 0.5;
|
||
}
|
||
.continue-btn.resume-btn .resume-btn-icon {
|
||
display:block;
|
||
width:16px;
|
||
height:16px;
|
||
flex:0 0 16px;
|
||
margin-left:auto;
|
||
}
|
||
|
||
/* Round-cap "Continue" affordance — a cohesive centered pill at the chat
|
||
bottom (not the bare red in-message stopped style). */
|
||
.rounds-exhausted {
|
||
justify-content:center;
|
||
gap:12px;
|
||
width:fit-content;
|
||
max-width:90%;
|
||
margin:14px auto 4px;
|
||
padding:7px 8px 7px 16px;
|
||
border:1px solid var(--border);
|
||
border-radius:999px;
|
||
background:color-mix(in srgb, var(--fg) 4%, transparent);
|
||
opacity:1;
|
||
}
|
||
.rounds-exhausted .rounds-exhausted-label {
|
||
color:color-mix(in srgb, var(--fg) 60%, transparent);
|
||
font-size:0.95em;
|
||
}
|
||
.rounds-exhausted .continue-btn {
|
||
font-size:0.9em;
|
||
font-weight:600;
|
||
opacity:1;
|
||
color:var(--bg);
|
||
background:var(--accent, var(--red));
|
||
border-radius:999px;
|
||
padding:4px 14px;
|
||
line-height:1.3;
|
||
}
|
||
.rounds-exhausted .continue-btn:hover {
|
||
opacity:0.88;
|
||
}
|
||
.ctx-indicator {
|
||
display:inline-flex; align-items:center; gap:1px;
|
||
font-size:0.75rem;
|
||
}
|
||
.ctx-popup {
|
||
position:fixed;
|
||
z-index:250;
|
||
background:var(--panel);
|
||
border:1px solid var(--border);
|
||
border-radius:8px;
|
||
padding:9px 12px 10px;
|
||
font-size:0.78rem;
|
||
color:var(--fg);
|
||
box-shadow:0 8px 24px rgba(0,0,0,0.4);
|
||
min-width:280px;
|
||
max-width:min(360px, calc(100vw - 16px));
|
||
line-height:1.45;
|
||
}
|
||
.ctx-label {
|
||
display:block;
|
||
width:auto;
|
||
color:var(--color-muted-alt);
|
||
font-size:0.75rem;
|
||
}
|
||
.ctx-popup-title {
|
||
display:flex;
|
||
align-items:center;
|
||
gap:6px;
|
||
margin-bottom:8px;
|
||
padding-bottom:7px;
|
||
border-bottom:1px solid color-mix(in srgb, var(--border) 65%, transparent);
|
||
font-weight:650;
|
||
letter-spacing:.01em;
|
||
}
|
||
.ctx-popup-title::before {
|
||
content:'';
|
||
width:6px;
|
||
height:6px;
|
||
flex:0 0 6px;
|
||
border-radius:50%;
|
||
background:var(--accent, var(--red));
|
||
box-shadow:0 0 0 3px color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
|
||
}
|
||
.ctx-stat-section {
|
||
display:flex;
|
||
flex-direction:column;
|
||
gap:2px;
|
||
}
|
||
.ctx-stat-section + .ctx-stat-section {
|
||
margin-top:6px;
|
||
padding-top:6px;
|
||
border-top:1px solid color-mix(in srgb, var(--border) 45%, transparent);
|
||
}
|
||
.ctx-stat-row {
|
||
display:grid;
|
||
grid-template-columns:minmax(82px, 1fr) minmax(0, 2fr);
|
||
align-items:baseline;
|
||
gap:10px;
|
||
min-height:16px;
|
||
}
|
||
.ctx-stat-row .ctx-label {
|
||
white-space:nowrap;
|
||
}
|
||
.ctx-stat-value {
|
||
color:var(--fg);
|
||
text-align:right;
|
||
font-variant-numeric:tabular-nums;
|
||
white-space:nowrap;
|
||
overflow:hidden;
|
||
text-overflow:ellipsis;
|
||
}
|
||
.edit-btn {
|
||
background:none; border:none;
|
||
color:var(--color-muted-alt); font-size:1.1rem; cursor:pointer;
|
||
padding:2px 6px; border-radius:4px;
|
||
transition: color .15s;
|
||
line-height:1;
|
||
}
|
||
.edit-btn:hover { color:var(--accent); }
|
||
.edit-textarea {
|
||
width:100%; background:var(--bg); color:var(--fg);
|
||
border:1px solid var(--border); border-radius:6px;
|
||
padding:8px; font-family:inherit; font-size:0.95rem;
|
||
resize:vertical; outline:none;
|
||
min-height:80px;
|
||
}
|
||
.edit-textarea:focus { border-color:var(--red); }
|
||
.edit-save-btn, .edit-cancel-btn {
|
||
background:var(--bg); color:var(--fg);
|
||
border:1px solid var(--border); border-radius:6px;
|
||
padding:4px 12px; cursor:pointer; font-size:0.8rem;
|
||
display:inline-flex; align-items:center; justify-content:center; gap:5px;
|
||
}
|
||
.edit-save-btn:hover { background:var(--panel); }
|
||
.edit-cancel-btn:hover { background:var(--panel); }
|
||
pre .copy-code {
|
||
position:absolute; right:6px;
|
||
background:var(--bg); color:var(--fg);
|
||
border:1px solid var(--border); border-radius:6px;
|
||
width:28px; height:28px; padding:0; cursor:pointer;
|
||
opacity:0; transition: opacity .15s, color .15s, border-color .15s;
|
||
display:flex; align-items:center; justify-content:center;
|
||
}
|
||
pre .copy-code { top:6px; }
|
||
pre .copy-code.bottom { top:auto; bottom:6px; }
|
||
pre:hover .copy-code { opacity:0.7; }
|
||
pre .copy-code:hover { opacity:1; }
|
||
pre .copy-code.copied {
|
||
opacity: 1;
|
||
color: var(--color-save-green, #4caf50);
|
||
border-color: var(--color-save-green, #4caf50);
|
||
background: color-mix(in srgb, var(--color-save-green, #4caf50) 18%, var(--bg));
|
||
animation: code-copy-pulse 0.36s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||
}
|
||
@keyframes code-copy-pulse {
|
||
0% { transform: scale(1); }
|
||
50% { transform: scale(1.15); }
|
||
100% { transform: scale(1); }
|
||
}
|
||
/* Slim text-button variant: swap "Copy" → "✓ Copied" via the
|
||
::before content while still inheriting the green flash + pulse. */
|
||
pre.pre-compact .copy-code.copied::before { content: '✓ Copied'; }
|
||
|
||
/* Edit code button — positioned left of copy button */
|
||
pre .edit-code {
|
||
position:absolute; right:42px; top:6px;
|
||
background:var(--bg); color:var(--fg);
|
||
border:1px solid var(--border); border-radius:6px;
|
||
width:28px; height:28px; padding:0; cursor:pointer;
|
||
opacity:0; transition: opacity .15s, color .15s, border-color .15s;
|
||
display:flex; align-items:center; justify-content:center;
|
||
}
|
||
pre .edit-code.bottom { top:auto; bottom:6px; }
|
||
pre:hover .edit-code { opacity:0.7; }
|
||
pre .edit-code:hover { opacity:1; }
|
||
/* When the edit-code button is in "save" mode (checkmark), use the
|
||
theme accent so it matches the EDITING outline + label that are
|
||
also accent-coloured — clearer that this is the confirm action. */
|
||
pre .edit-code.active {
|
||
opacity: 1;
|
||
color: var(--accent-primary, var(--red));
|
||
border-color: var(--accent-primary, var(--red));
|
||
background: color-mix(in srgb, var(--accent-primary, var(--red)) 12%, var(--bg));
|
||
}
|
||
pre .use-code {
|
||
position:absolute; right:42px; top:6px;
|
||
background:var(--bg); color:var(--fg);
|
||
border:1px solid var(--border); border-radius:6px;
|
||
width:28px; height:28px; padding:0; cursor:pointer;
|
||
opacity:0; transition: opacity .15s, color .15s, border-color .15s;
|
||
display:flex; align-items:center; justify-content:center;
|
||
}
|
||
pre .use-code.bottom { top:auto; bottom:6px; }
|
||
pre:hover .use-code { opacity:0.7; }
|
||
pre .use-code:hover { opacity:1; }
|
||
pre .use-code.used {
|
||
opacity: 1;
|
||
color: var(--color-save-green, #4caf50);
|
||
border-color: var(--color-save-green, #4caf50);
|
||
background: color-mix(in srgb, var(--color-save-green, #4caf50) 18%, var(--bg));
|
||
animation: code-copy-pulse 0.36s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||
}
|
||
.setup-trigger-link, .setup-clickable-provider, .setup-clickable-code {
|
||
transition: color 0.15s ease, opacity 0.15s ease;
|
||
}
|
||
.setup-trigger-link:hover,
|
||
.setup-clickable-provider:hover,
|
||
.setup-clickable-code:hover {
|
||
color: var(--accent, var(--red)) !important;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
/* Tapping the code body (not a button) toggles the overlay buttons off so
|
||
they stop covering the text on touch screens. Tap again to bring back. */
|
||
pre.buttons-hidden .copy-code,
|
||
pre.buttons-hidden .edit-code,
|
||
pre.buttons-hidden .run-code { opacity:0 !important; pointer-events:none !important; }
|
||
|
||
/* Editing state — subtle border on the code block */
|
||
/* Editing state: was a 1px subtle outline that was almost invisible on
|
||
mobile, so users couldn't tell their tap-to-edit had actually engaged.
|
||
Use the accent colour + a tinted background so it reads at a glance. */
|
||
pre.editing {
|
||
outline: 2px solid var(--accent-primary, var(--red));
|
||
outline-offset: -2px;
|
||
background: color-mix(in srgb, var(--accent-primary, var(--red)) 6%, var(--bg)) !important;
|
||
}
|
||
pre.editing code.editing { outline:none; cursor:text; }
|
||
pre.editing::before {
|
||
content: 'EDITING';
|
||
position: absolute; top: 0; left: 0;
|
||
padding: 2px 8px;
|
||
font-size: 9px; font-weight: 700; letter-spacing: 0.5px;
|
||
background: var(--accent-primary, var(--red));
|
||
color: #fff;
|
||
border-radius: 0 0 4px 0;
|
||
z-index: 2;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Run code button — positioned left of edit button */
|
||
pre .run-code {
|
||
position:absolute; right:78px; top:6px;
|
||
background:var(--bg); color:var(--fg);
|
||
border:1px solid var(--border); border-radius:6px;
|
||
width:28px; height:28px; padding:0; cursor:pointer;
|
||
opacity:0; transition: opacity .15s, color .15s, border-color .15s;
|
||
display:flex; align-items:center; justify-content:center;
|
||
}
|
||
pre .run-code.bottom { top:auto; bottom:6px; }
|
||
pre:hover .run-code { opacity:0.7; }
|
||
pre .run-code:hover { opacity:1; color:var(--hl-function, #61afef); border-color:var(--hl-function, #61afef); }
|
||
|
||
/* Compact (single-line) code blocks: slim buttons so the row doesn't
|
||
double the height of a 1-line bash. Copy is text ("Copy" → "✓ Copied"),
|
||
Run and Edit keep their icons but at smaller sizes. Edit swaps to
|
||
a "Save" text label when its .active state is on. */
|
||
pre.pre-compact { padding-right: 200px; min-height: 0; }
|
||
pre.pre-compact .copy-code,
|
||
pre.pre-compact .edit-code,
|
||
pre.pre-compact .run-code {
|
||
height: 20px;
|
||
padding: 0;
|
||
font-size: 10px;
|
||
font-weight: 500;
|
||
line-height: 20px;
|
||
top: 3px;
|
||
}
|
||
/* Copy: text-only, hide SVG */
|
||
pre.pre-compact .copy-code {
|
||
width: auto;
|
||
padding: 0 8px;
|
||
right: 6px;
|
||
gap: 0;
|
||
}
|
||
pre.pre-compact .copy-code svg { display: none; }
|
||
pre.pre-compact .copy-code::before { content: 'Copy'; }
|
||
pre.pre-compact .copy-code.copied::before { content: '✓ Copied'; }
|
||
/* Edit: icon + "Edit" label, swap to "Save" when editing */
|
||
pre.pre-compact .edit-code {
|
||
width: auto;
|
||
padding: 0 8px 0 6px;
|
||
gap: 3px;
|
||
right: 64px;
|
||
}
|
||
pre.pre-compact .edit-code svg { width: 12px; height: 12px; }
|
||
pre.pre-compact .edit-code::after { content: 'Edit'; }
|
||
pre.pre-compact .edit-code.active::after { content: 'Save'; }
|
||
/* Run: icon + "Run" label */
|
||
pre.pre-compact .run-code {
|
||
width: auto;
|
||
padding: 0 8px 0 6px;
|
||
gap: 3px;
|
||
right: 126px;
|
||
}
|
||
pre.pre-compact .run-code svg { width: 12px; height: 12px; }
|
||
pre.pre-compact .run-code::after { content: 'Run'; }
|
||
/* Bottom-positioned slim buttons (when pre is near the top of the
|
||
viewport, the existing JS toggles .bottom to flip them down). */
|
||
pre.pre-compact .copy-code.bottom,
|
||
pre.pre-compact .edit-code.bottom,
|
||
pre.pre-compact .run-code.bottom { top: auto; bottom: 3px; }
|
||
|
||
/* Touch devices: no hover, so always show copy/run/edit buttons */
|
||
@media (hover: none) {
|
||
pre .copy-code { opacity:0.7; }
|
||
pre .edit-code { opacity:0.7; }
|
||
pre .run-code { opacity:0.7; }
|
||
}
|
||
|
||
/* Code runner output panel */
|
||
.code-runner-output {
|
||
position:relative;
|
||
border:1px solid var(--border); border-top:2px solid var(--hl-function, #61afef);
|
||
border-radius:0 0 4px 4px;
|
||
background:var(--bg);
|
||
margin:-4px 0 8px 0;
|
||
padding:8px 12px;
|
||
max-height:400px;
|
||
overflow:auto;
|
||
}
|
||
|
||
/* Chat-native visual explanations render in an isolated preview. */
|
||
.chat-svg-visual {
|
||
width: 100%;
|
||
max-width: 760px;
|
||
margin: 12px 0 16px;
|
||
}
|
||
.chat-svg-preview {
|
||
display: block;
|
||
width: 100%;
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
background: var(--bg);
|
||
}
|
||
.code-runner-pre {
|
||
margin:0; padding:0;
|
||
font-family:'Fira Code', 'Courier New', monospace;
|
||
font-size:0.9em; line-height:1.5;
|
||
white-space:pre-wrap; word-break:break-word;
|
||
color:var(--fg);
|
||
background:none !important;
|
||
border:none !important;
|
||
}
|
||
.code-runner-error { color:var(--red); }
|
||
.code-runner-loading { font-style:italic; color:var(--red); padding:4px 0; }
|
||
.code-runner-close {
|
||
position:absolute; top:4px; right:4px;
|
||
background:none; border:none; color:var(--fg);
|
||
cursor:pointer; opacity:0.5; font-size:14px; padding:2px 6px;
|
||
}
|
||
.code-runner-close:hover { opacity:1; }
|
||
/* Labeled copy pill — pinned top-right INSIDE the run-output panel, not
|
||
in a separate footer. Panel is position:relative so absolute works. */
|
||
.code-runner-copy-inline {
|
||
position: absolute; top: 6px; right: 32px; /* sits LEFT of the X close (top:4 right:4 ~24px wide) */
|
||
z-index: 2;
|
||
background: var(--panel); color: var(--fg);
|
||
border: 1px solid var(--border); border-radius: 6px;
|
||
padding: 3px 10px; font-size: 11px; cursor: pointer;
|
||
display: inline-flex; align-items: center;
|
||
transition: border-color 0.15s, color 0.15s, background 0.15s;
|
||
}
|
||
.code-runner-copy-inline:hover {
|
||
border-color: var(--accent-primary, var(--red));
|
||
color: var(--accent-primary, var(--red));
|
||
}
|
||
/* Reserve room on the right so the output text doesn't slide under
|
||
either the Copy pill or the Close X. Applies to both the chat run
|
||
panel AND the document panel (doc-run-output reuses the same children). */
|
||
.code-runner-output,
|
||
.doc-run-output { padding-right: 110px; }
|
||
|
||
.toast {
|
||
position:fixed;
|
||
top: 16px; right: 16px;
|
||
left: auto; bottom: auto;
|
||
background:var(--panel); color:var(--fg);
|
||
border:1px solid color-mix(in srgb, var(--accent) 30%, transparent);
|
||
border-left: 3px solid var(--accent);
|
||
padding:9px 9px 9px 10px; border-radius:8px; font-size:12px; opacity:0;
|
||
/* Off-screen to the right by default; .show slides to 0;
|
||
removing .show transitions to -120% (off-screen left). */
|
||
transform: translateX(120%);
|
||
transition: opacity .35s cubic-bezier(0.22, 1, 0.36, 1),
|
||
transform .45s cubic-bezier(0.22, 1, 0.36, 1);
|
||
/* Toasts must stay above the chat-context settings popup so a setting
|
||
change is visible while that popup remains open. */
|
||
z-index: 10070; pointer-events: none;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||
backdrop-filter: blur(12px);
|
||
max-width: min(420px, calc(100vw - 32px));
|
||
min-width: min(240px, calc(100vw - 32px));
|
||
min-height: 40px;
|
||
display: grid;
|
||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||
align-items: start;
|
||
column-gap: 8px;
|
||
box-sizing: border-box;
|
||
}
|
||
.toast.show { opacity:1; transform: translateX(0); }
|
||
.toast:not(:has(.toast-checkmark, .toast-whirlpool, .toast-error-icon)) {
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
}
|
||
.toast-message {
|
||
min-width: 0;
|
||
padding: 3px 0 2px;
|
||
position: relative;
|
||
top: 0;
|
||
line-height: 1.35;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.toast.toast-ai-reply-progress .toast-message {
|
||
top: 2px;
|
||
}
|
||
.toast.toast-ai-reply-result .toast-message {
|
||
top: 0;
|
||
}
|
||
.toast.toast-sending .toast-message {
|
||
top: 2px;
|
||
}
|
||
.toast.toast-message-sent .toast-message {
|
||
top: 4px;
|
||
}
|
||
.toast-actions {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
align-self: start;
|
||
gap: 3px;
|
||
margin-left: 2px;
|
||
}
|
||
.toast-action-btn {
|
||
min-height: 26px;
|
||
padding: 3px 7px 3px 9px;
|
||
border: 1px solid color-mix(in srgb, var(--fg) 34%, transparent);
|
||
border-radius: 6px;
|
||
background: color-mix(in srgb, var(--fg) 4%, transparent);
|
||
color: var(--fg);
|
||
cursor: pointer;
|
||
font: inherit;
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
pointer-events: auto;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
transition: border-color 0.15s ease, background 0.15s ease;
|
||
}
|
||
.toast-action-btn:hover {
|
||
border-color: color-mix(in srgb, var(--accent) 65%, var(--border));
|
||
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
||
}
|
||
.toast-action-icon { display: inline-flex; align-items: center; }
|
||
.toast-action-hint {
|
||
padding: 2px 4px;
|
||
border: 0;
|
||
border-radius: 3px;
|
||
background: color-mix(in srgb, var(--fg) 9%, transparent);
|
||
color: inherit;
|
||
font: 500 8px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||
letter-spacing: 0.2px;
|
||
opacity: 0.62;
|
||
text-transform: uppercase;
|
||
pointer-events: none;
|
||
}
|
||
.toast .toast-checkmark {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 16px;
|
||
height: 16px;
|
||
margin-top: 5px;
|
||
margin-right: 0;
|
||
position: relative;
|
||
top: 4px;
|
||
color: var(--accent, var(--red));
|
||
vertical-align: -3px;
|
||
transform: scale(0.65);
|
||
opacity: 0;
|
||
animation: toastCheckPop 360ms cubic-bezier(0.2, 0.9, 0.25, 1.25) forwards;
|
||
}
|
||
.toast .toast-checkmark svg polyline {
|
||
stroke-dasharray: 24;
|
||
stroke-dashoffset: 24;
|
||
animation: toastCheckDraw 420ms ease-out 120ms forwards;
|
||
}
|
||
.toast .toast-whirlpool {
|
||
position: relative;
|
||
top: 4px;
|
||
}
|
||
@keyframes toastCheckPop {
|
||
0% { opacity: 0; transform: scale(0.65); }
|
||
65% { opacity: 1; transform: scale(1.16); }
|
||
100% { opacity: 1; transform: scale(1); }
|
||
}
|
||
@keyframes toastCheckDraw {
|
||
to { stroke-dashoffset: 0; }
|
||
}
|
||
.toast-close-btn {
|
||
margin-left: 0;
|
||
padding: 0;
|
||
width: 22px;
|
||
height: 22px;
|
||
line-height: 1;
|
||
border: none;
|
||
background: none;
|
||
color: var(--fg);
|
||
opacity: 0.5;
|
||
cursor: pointer;
|
||
font-size: 16px;
|
||
border-radius: 50%;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
pointer-events: auto;
|
||
flex-shrink: 0;
|
||
transition: transform 0.22s ease, opacity 0.15s ease, background 0.15s ease;
|
||
}
|
||
.toast-error-icon {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 17px;
|
||
height: 17px;
|
||
margin-top: 4px;
|
||
border: 1px solid currentColor;
|
||
border-radius: 50%;
|
||
font-size: 10px;
|
||
font-weight: 800;
|
||
line-height: 1;
|
||
}
|
||
.toast-close-btn:hover {
|
||
opacity: 1;
|
||
transform: rotate(90deg);
|
||
background: color-mix(in srgb, var(--fg) 8%, transparent);
|
||
}
|
||
.toast.exiting {
|
||
opacity: 0;
|
||
transform: translateX(-120%);
|
||
}
|
||
.toast.error {
|
||
border-color: color-mix(in srgb, var(--color-error) 40%, transparent);
|
||
border-left-color: var(--color-error);
|
||
color: var(--color-error);
|
||
}
|
||
/* When the notes panel is docked to the right, the toast enters from the
|
||
panel side. ui.js supplies the exact right edge and available width. */
|
||
body:has(.notes-pane.modal-right-docked) .toast {
|
||
transform: translateX(-120%);
|
||
}
|
||
body:has(.notes-pane.modal-right-docked) .toast.show { transform: translateX(0); }
|
||
body:has(.notes-pane.modal-right-docked) .toast.exiting { transform: translateX(120%); }
|
||
/* Notes-only panel-aware placement. Documents and Email retain the normal
|
||
top-right toast position. */
|
||
.toast.toast-panel-offset { transform: translateX(120%); }
|
||
.toast.toast-panel-offset.show { transform: translateX(0); }
|
||
.toast.toast-panel-offset.exiting { transform: translateX(-120%); }
|
||
@media (max-width: 768px) {
|
||
.toast {
|
||
top: 12px;
|
||
right: 12px;
|
||
max-width: calc(100vw - 24px);
|
||
/* Receive touches so the swipe-to-dismiss gesture works (desktop keeps
|
||
pointer-events:none so the toast never blocks clicks). Horizontal pan
|
||
only, so it doesn't fight page scroll. */
|
||
pointer-events: auto;
|
||
touch-action: pan-x;
|
||
}
|
||
.toast-action-hint { display: none; }
|
||
}
|
||
|
||
.stop-btn {
|
||
position:absolute; top:2px; right:2px;
|
||
background:var(--panel);
|
||
color:var(--fg);
|
||
border:1px solid var(--fg);
|
||
font-family:inherit;
|
||
font-size:1em; line-height:1; padding:2px 5px;
|
||
cursor:pointer;
|
||
}
|
||
.small-note { font-size:12px; color:color-mix(in srgb, var(--fg) 60%, transparent); margin-top:4px; }
|
||
.row-end { justify-content:flex-end; }
|
||
.model-chat-btn {
|
||
height:32px; padding:0 10px; margin-left:auto;
|
||
}
|
||
/* Nudge the "+ Chat" label down 1px to sit centered in the button. */
|
||
.model-chat-btn-label {
|
||
position: relative;
|
||
top: 1px;
|
||
}
|
||
.openai-row {
|
||
display:flex; align-items:center; gap:6px;
|
||
}
|
||
.models-row {
|
||
display:flex; align-items:center; gap:6px; border:1px solid var(--border); padding:4px; margin:4px 0; border-radius: 4px;
|
||
}
|
||
.models-row .grow,
|
||
.models-row select {
|
||
flex:1;
|
||
display:flex;
|
||
align-items:center;
|
||
font-size: 9.75px;
|
||
}
|
||
.model-fav-btn {
|
||
width: 8px; height: 8px;
|
||
border-radius: 50%;
|
||
border: 1.5px solid color-mix(in srgb, var(--fg) 22%, transparent);
|
||
flex-shrink: 0;
|
||
cursor: pointer;
|
||
transition: all 0.15s;
|
||
position: relative;
|
||
margin-left: 4px;
|
||
}
|
||
.model-fav-btn::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -10px; left: -10px; right: -10px; bottom: -10px;
|
||
}
|
||
.model-fav-btn:hover {
|
||
border-color: var(--fg);
|
||
background: color-mix(in srgb, var(--fg) 27%, transparent);
|
||
transform: scale(1.3);
|
||
}
|
||
.model-fav-btn.active {
|
||
background: var(--fg);
|
||
border-color: var(--fg);
|
||
}
|
||
.model-fav-btn.active:hover {
|
||
opacity: 0.6;
|
||
}
|
||
.model-search-input {
|
||
width: 100%;
|
||
padding: 6px 10px;
|
||
margin-bottom: 4px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
background: var(--bg);
|
||
color: var(--fg);
|
||
font-family: inherit;
|
||
font-size: 0.8rem;
|
||
outline: none;
|
||
box-sizing: border-box;
|
||
transition: border-color 0.15s;
|
||
}
|
||
.model-search-input:focus {
|
||
border-color: var(--red);
|
||
}
|
||
.model-search-input::placeholder {
|
||
color: color-mix(in srgb, var(--fg) 30%, transparent);
|
||
}
|
||
.models-row button {
|
||
font-size: 9px;
|
||
height: 24px;
|
||
padding: 0 8px;
|
||
}
|
||
@media (max-width:768px){
|
||
.box { max-height:none; }
|
||
.chat-container { padding:10px; flex:1; margin-top:0; padding-top:42px; min-height:0; }
|
||
.scroll-nav-btn { width:44px; height:44px; font-size:14px; margin-bottom:0; }
|
||
.send-btn { width:48px; height:48px !important; border-radius:12px; }
|
||
.send-btn svg { width:22px; height:22px; }
|
||
#compare-toggle-btn { display:none !important; }
|
||
.section[draggable] { -webkit-user-drag:none; }
|
||
.drag-handle, .item-drag-handle, .folder-drag-handle { display:none !important; }
|
||
/* Sidebar overlays chat on mobile */
|
||
.sidebar {
|
||
position: fixed !important;
|
||
top: 0; bottom: 0; left: 0;
|
||
z-index: 400;
|
||
width: 80% !important;
|
||
max-width: 340px;
|
||
box-shadow: 4px 0 20px rgba(0,0,0,0.5);
|
||
transition: transform 0.25s ease, opacity 0.25s ease !important;
|
||
opacity: 1 !important;
|
||
overflow: visible !important;
|
||
/* Force opaque background — Firefox mobile renders the
|
||
backdrop-filter+background combo as semi-transparent on
|
||
the slide-out drawer, letting the chat input model picker
|
||
label show through the sidebar. */
|
||
/* Keep the drawer tied to the Sidebar color picker. This used to
|
||
force --panel on mobile, leaving only the header responsive to
|
||
--sidebar-bg. */
|
||
background: var(--sidebar-bg, var(--panel)) !important;
|
||
backdrop-filter: none !important;
|
||
}
|
||
.sidebar.hidden {
|
||
width: 80% !important;
|
||
transform: translateX(-100%);
|
||
pointer-events: none;
|
||
overflow: hidden !important;
|
||
}
|
||
.sidebar.right-side.hidden {
|
||
transform: translateX(100%);
|
||
}
|
||
.sidebar.right-side {
|
||
left: auto; right: 0;
|
||
box-shadow: -4px 0 20px rgba(0,0,0,0.5);
|
||
}
|
||
html.ody-sidebar-off .sidebar,
|
||
html.ody-mobile-startup-sidebar-hidden .sidebar {
|
||
transform: translateX(-100%) !important;
|
||
pointer-events: none !important;
|
||
opacity: 0 !important;
|
||
}
|
||
html.ody-sidebar-off .sidebar.right-side,
|
||
html.ody-mobile-startup-sidebar-hidden .sidebar.right-side {
|
||
transform: translateX(100%) !important;
|
||
}
|
||
html.ody-sidebar-off .icon-rail,
|
||
html.ody-mobile-startup-sidebar-hidden .icon-rail {
|
||
display: none !important;
|
||
}
|
||
/* Backdrop behind sidebar */
|
||
#sidebar-backdrop {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 390;
|
||
background: rgba(0,0,0,0.4);
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 0.35s ease;
|
||
}
|
||
#sidebar-backdrop.visible { opacity: 1; pointer-events: auto; }
|
||
/* Elastic overscroll on sidebar */
|
||
.sidebar-inner {
|
||
-webkit-overflow-scrolling: touch;
|
||
overscroll-behavior-y: auto;
|
||
overflow-y: scroll !important;
|
||
padding-bottom: 40px;
|
||
}
|
||
.sidebar:not(.hidden) {
|
||
overscroll-behavior: auto;
|
||
}
|
||
/* Sidebar header — room for hamburger */
|
||
.sidebar-header {
|
||
padding: 18px 12px 8px 12px;
|
||
min-height: 44px;
|
||
}
|
||
.sidebar-brand-title {
|
||
font-size: 1.2rem;
|
||
left: 0 !important;
|
||
}
|
||
/* Sidebar inner — bigger spacing */
|
||
.sidebar-inner {
|
||
padding: 12px 10px 12px !important;
|
||
gap: 4px !important;
|
||
}
|
||
/* Section headers — match list item sizing */
|
||
.section-header-flex {
|
||
padding: 12px 10px !important;
|
||
height: 48px !important;
|
||
border-radius: 8px;
|
||
box-sizing: border-box;
|
||
}
|
||
.section-header-flex h4,
|
||
.section-header-flex .section-title {
|
||
font-size: 14px !important;
|
||
gap: 11px !important;
|
||
}
|
||
.section-icon,
|
||
.sidebar-action-icon {
|
||
width: 16px !important;
|
||
height: 16px !important;
|
||
left: 0 !important;
|
||
}
|
||
/* List items — bigger touch targets, bigger text */
|
||
.list-item {
|
||
padding: 12px 10px !important;
|
||
min-height: 48px;
|
||
font-size: 14px;
|
||
border-radius: 8px;
|
||
gap: 10px !important;
|
||
}
|
||
.list-item .grow {
|
||
font-size: 14px !important;
|
||
}
|
||
/* Search, New Chat & Assistant */
|
||
#sidebar-search-btn,
|
||
#sidebar-new-chat-btn,
|
||
.sidebar-assistant-entry {
|
||
padding: 12px 10px !important;
|
||
min-height: 48px !important;
|
||
}
|
||
#sidebar-search-btn .grow,
|
||
#sidebar-new-chat-btn .grow,
|
||
.sidebar-assistant-entry .grow {
|
||
font-size: 14px !important;
|
||
left: 0 !important;
|
||
}
|
||
/* Section separator — more breathing room */
|
||
.section {
|
||
margin-top: 4px;
|
||
border-radius: 8px;
|
||
}
|
||
/* Wave accent — slightly bigger on mobile */
|
||
.section-header-flex h4::before {
|
||
height: 18px;
|
||
}
|
||
/* Compact top bar on mobile — align with sidebar header */
|
||
.chat-top-bar {
|
||
padding: 1px 8px;
|
||
min-height: 14px;
|
||
margin-top: -31px;
|
||
padding-top: 31px;
|
||
}
|
||
.chat-top-bar .chat-new-btn { display: none; }
|
||
.chat-meta-overlay {
|
||
font-size: 0.65em;
|
||
left: 50% !important;
|
||
right: auto !important;
|
||
top: 50%;
|
||
transform: translate(-50%, -50%);
|
||
position: absolute;
|
||
width: min(calc(100vw - 104px), 320px);
|
||
max-width: calc(100vw - 104px);
|
||
overflow: hidden;
|
||
justify-content: center;
|
||
}
|
||
.chat-meta-overlay #current-meta {
|
||
max-width: 100%;
|
||
}
|
||
.chat-meta-overlay .export-dl-btn { display: inline-flex; }
|
||
#export-compact-btn {
|
||
position: relative;
|
||
left: -2px;
|
||
}
|
||
/* Incognito — smaller on mobile */
|
||
.incognito-btn {
|
||
padding: 4px 10px;
|
||
font-size: 10px;
|
||
}
|
||
/* Keep the active Nobody indicator inside the chat header on mobile.
|
||
Pinning it next to the hamburger made it collide with the title/splash
|
||
on tight screens and detached it from the chat state it represents. */
|
||
.incognito-indicator {
|
||
display: none !important;
|
||
}
|
||
/* Icon rail on mobile — hidden by default, shown in mini-sidebar state */
|
||
.icon-rail { display: none !important; }
|
||
.icon-rail.mobile-mini {
|
||
display: flex !important;
|
||
position: fixed;
|
||
top: 0; bottom: 0; left: 0;
|
||
z-index: 200;
|
||
width: 48px;
|
||
box-shadow: 2px 0 12px rgba(0,0,0,0.4);
|
||
}
|
||
.icon-rail.mobile-mini.right-side {
|
||
left: auto; right: 0;
|
||
box-shadow: -2px 0 12px rgba(0,0,0,0.4);
|
||
}
|
||
/* Chat bubbles — AI stretches full width, user stays compact */
|
||
.msg { font-size: 0.85em; }
|
||
.msg .body { font-size: 0.9em; }
|
||
.msg-user { max-width: 90% !important; margin-left: auto; margin-right: 4px; }
|
||
.msg-ai, .agent-thread { width: 100% !important; max-width: 100% !important; margin: 8px 0; }
|
||
/* Prevent inner scrollable elements from trapping vertical scroll */
|
||
.msg pre,
|
||
.agent-tool-output pre,
|
||
.agent-thread-cmd,
|
||
.msg details {
|
||
overflow-y: hidden !important;
|
||
max-height: none !important;
|
||
overflow-x: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
touch-action: pan-y pan-x;
|
||
}
|
||
/* Models row — match list items */
|
||
.models-row {
|
||
padding: 12px 10px;
|
||
min-height: 48px;
|
||
}
|
||
/* Input icon buttons — bigger touch targets */
|
||
.input-icon-btn {
|
||
padding: 10px;
|
||
min-width: 44px;
|
||
min-height: 44px;
|
||
}
|
||
/* Tool indicators — icon only on mobile (hide text, keep x) */
|
||
.tool-indicator > span {
|
||
display: none !important;
|
||
}
|
||
.tool-indicator .tool-indicator-x {
|
||
display: inline-block !important;
|
||
opacity: 0.6;
|
||
}
|
||
/* Hamburger — always right on mobile */
|
||
.hamburger-btn {
|
||
width: 44px;
|
||
height: 44px;
|
||
top: 6px;
|
||
left: auto !important;
|
||
right: 4px !important;
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
.hamburger-btn:hover,
|
||
.hamburger-btn:active {
|
||
background: none !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
}
|
||
/* New chat — always left on mobile */
|
||
.mobile-new-chat-btn {
|
||
display: flex;
|
||
position: fixed;
|
||
top: 12px;
|
||
left: 8px;
|
||
z-index: 210;
|
||
width: 32px;
|
||
height: 32px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: none;
|
||
border: none;
|
||
color: var(--fg);
|
||
cursor: pointer;
|
||
opacity: 0.5;
|
||
padding: 0;
|
||
}
|
||
/* Modal close button — bigger on mobile */
|
||
.close-btn,
|
||
.modal-close {
|
||
min-width: 44px;
|
||
min-height: 44px;
|
||
width: 44px;
|
||
height: 44px;
|
||
font-size: 14px;
|
||
}
|
||
/* Code block buttons — slightly bigger touch targets */
|
||
pre .copy-code,
|
||
pre .edit-code,
|
||
pre .run-code {
|
||
width: 44px;
|
||
height: 44px;
|
||
}
|
||
/* Touch-friendly targets for small buttons */
|
||
.export-dl-btn {
|
||
min-width: 44px;
|
||
min-height: 44px;
|
||
}
|
||
.section-header-btn {
|
||
min-width: 44px;
|
||
min-height: 44px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
/* 44×44 touch buttons starting from right:6 ends at 50. ~8px gap is
|
||
enough to be distinguishable without spreading them too far apart. */
|
||
pre .edit-code {
|
||
right: 58px;
|
||
}
|
||
pre .run-code {
|
||
right: 110px;
|
||
}
|
||
/* Dropdowns — bigger touch targets on mobile */
|
||
.dropdown-item-compact {
|
||
padding: 12px 12px !important;
|
||
font-size: 14px !important;
|
||
min-height: 44px;
|
||
gap: 10px !important;
|
||
}
|
||
.dropdown-item-compact .dropdown-icon {
|
||
width: 18px !important;
|
||
height: 18px !important;
|
||
}
|
||
.dropdown-item-compact .dropdown-icon svg {
|
||
width: 16px !important;
|
||
height: 16px !important;
|
||
}
|
||
.dropdown,
|
||
.session-dropdown {
|
||
padding: 6px !important;
|
||
border-radius: 12px !important;
|
||
}
|
||
/* Safe area padding for notched devices */
|
||
.chat-input-bar {
|
||
padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
|
||
}
|
||
/* Mode toggle — larger touch targets */
|
||
.mode-toggle {
|
||
height: 34px;
|
||
border-radius: 12px;
|
||
}
|
||
.mode-toggle::before {
|
||
border-radius: 11px;
|
||
}
|
||
.mode-toggle-btn {
|
||
padding: 0 14px;
|
||
font-size: 12px;
|
||
min-height: 34px;
|
||
}
|
||
#mode-agent-btn { border-radius: 12px 0 0 12px; }
|
||
#mode-chat-btn { border-radius: 0 12px 12px 0; }
|
||
/* Diff mode — stack toolbar, bigger buttons */
|
||
.diff-toolbar {
|
||
padding: 8px 12px;
|
||
gap: 6px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.diff-toolbar-btn {
|
||
padding: 6px 12px;
|
||
font-size: 12px;
|
||
min-height: 36px;
|
||
}
|
||
.diff-chunk-btn {
|
||
width: 28px;
|
||
height: 28px;
|
||
font-size: 14px;
|
||
}
|
||
/* Document/email/gallery/research library — full-height bottom-sheet
|
||
on mobile. Keep the rounded top corners + border-top so they match
|
||
the cookbook/calendar/compare look instead of looking like raw
|
||
full-bleed panels. */
|
||
.doclib-modal-content,
|
||
.gallery-modal-content {
|
||
width: 100vw !important;
|
||
max-width: 100vw !important;
|
||
/* vh fallback, dvh override so the modal adapts to mobile
|
||
URL-bar show/hide. Order matters: later same-specificity rule
|
||
wins, so dvh must come after vh. */
|
||
max-height: 100vh !important;
|
||
max-height: 100dvh !important;
|
||
height: 100vh;
|
||
height: 100dvh;
|
||
border-radius: 14px 14px 0 0 !important;
|
||
border: none !important;
|
||
border-top: 1px solid var(--border) !important;
|
||
box-shadow: none !important;
|
||
padding: 6px !important;
|
||
padding-bottom: env(safe-area-inset-bottom, 6px) !important;
|
||
margin: 0 !important;
|
||
overflow-y: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
#email-lib-modal .doclib-grid {
|
||
max-height: none;
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
/* Library modal layout: pin the header + tab strip, give the active
|
||
panel (admin-card) the remaining height, and let the grid scroll
|
||
internally. The load-more button sits below the grid as a sibling
|
||
inside admin-card so it stays visible at the bottom of the panel
|
||
without competing with the grid for scroll. */
|
||
#doclib-modal .doclib-modal-content {
|
||
display: flex !important;
|
||
flex-direction: column !important;
|
||
overflow: hidden !important;
|
||
}
|
||
#doclib-modal .modal-header,
|
||
#doclib-modal .lib-tabs {
|
||
flex: 0 0 auto !important;
|
||
}
|
||
#doclib-modal .modal-body {
|
||
flex: 1 1 0 !important;
|
||
min-height: 0 !important;
|
||
overflow: hidden !important;
|
||
}
|
||
#doclib-modal .admin-card {
|
||
flex: 1 1 0 !important;
|
||
min-height: 0 !important;
|
||
}
|
||
/* :not(:has(.doclib-card-expanded)) scopes these to the normal list
|
||
state — when a document card is expanded, the existing expand-state
|
||
rules take over (grid claims flex:1 with overflow:hidden so the
|
||
expanded card can scroll itself). */
|
||
#doclib-modal .doclib-grid:not(:has(.doclib-card-expanded)) {
|
||
flex: 1 1 0 !important;
|
||
min-height: 0 !important;
|
||
max-height: none !important;
|
||
overflow-y: auto !important;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
#doclib-modal .doclib-load-more {
|
||
margin: 8px auto 12px !important;
|
||
}
|
||
/* Squeeze a little more height for the preview content by tightening
|
||
the spacing inside an expanded doc card on mobile. */
|
||
#doclib-modal .doclib-card.doclib-card-expanded,
|
||
#email-lib-modal .doclib-card.doclib-card-expanded,
|
||
#memory-modal .doclib-card.doclib-card-expanded {
|
||
gap: 2px !important;
|
||
padding: 4px 6px !important;
|
||
height: 100% !important;
|
||
}
|
||
/* Skills preview kept stopping at partial height because the flex
|
||
chain (modal → tabs → panel → admin-card → grid → card) wouldn't
|
||
reliably hand the card a full-height parent. But the skills LIST
|
||
already scrolls correctly inside #skills-list, so that grid box
|
||
already has the right bounded height (the area below the tabs).
|
||
Anchor the expanded card to THAT box with position:absolute so it
|
||
fills the list area exactly — header + tabs stay visible.
|
||
|
||
NOTE: position:relative here is UNCONDITIONAL (not gated behind
|
||
:has(.doclib-card-expanded)). Firefox mobile builds without :has()
|
||
support never applied the gated rule, so the absolute card lost its
|
||
anchor and only filled ~50% — while Chromium/desktop-narrow worked.
|
||
Relative-when-collapsed is harmless (no abs children then). */
|
||
#memory-modal #skills-list.doclib-grid {
|
||
position: relative !important;
|
||
}
|
||
#memory-modal .doclib-card.skill-card.doclib-card-expanded {
|
||
position: absolute !important;
|
||
inset: 0 !important;
|
||
/* Height is set in JS (skills.js _fillSkillCardHeight) as an explicit
|
||
px value — Firefox does NOT treat inset:0 stretch OR height:100%
|
||
(against the flex-sized grid) as a definite height, so grid/flex
|
||
children never filled. An explicit px height is unambiguous. */
|
||
margin: 0 !important;
|
||
padding: 8px 10px calc(8px + env(safe-area-inset-bottom, 0px)) !important;
|
||
background: var(--bg) !important;
|
||
/* Flex column. JS (_fillSkillCardHeight) sets EXPLICIT px heights on
|
||
the preview + <pre>; in a flex column an explicit-height item
|
||
(flex:0 0 auto + height) is honoured. (Grid was worse here — the
|
||
collapsed 1fr track overrode the preview's explicit height.) */
|
||
display: flex !important;
|
||
flex-direction: column !important;
|
||
overflow: hidden !important;
|
||
border: none !important;
|
||
border-radius: 0 !important;
|
||
box-sizing: border-box !important;
|
||
}
|
||
/* Preview is the 1fr grid track — give it a definite-height flex column
|
||
so the <pre> (flex:1) fills and the footer pins at the bottom. */
|
||
#memory-modal .doclib-card.skill-card.doclib-card-expanded > .doclib-card-preview {
|
||
display: flex !important;
|
||
flex-direction: column !important;
|
||
min-height: 0 !important;
|
||
overflow: hidden !important;
|
||
}
|
||
/* The card now fills the screen, but a base rule (.skill-card...
|
||
.doclib-card-preview { flex: 0 1 auto }) sizes the preview to its
|
||
CONTENT — so a medium SKILL.md left the preview at ~half the card
|
||
(the "only 50%" the debug confirmed: card was full, content wasn't).
|
||
Force the preview AND the <pre> to FILL the card. Extra .skill-card
|
||
in the selector out-specifies both the base and the generic mobile
|
||
rule so this wins without ambiguity. */
|
||
#memory-modal .doclib-card.skill-card.doclib-card-expanded > .doclib-card-preview {
|
||
flex: 1 1 auto !important;
|
||
min-height: 0 !important;
|
||
}
|
||
#memory-modal .doclib-card.skill-card.doclib-card-expanded .skill-md-pre {
|
||
flex: 1 1 auto !important;
|
||
min-height: 0 !important;
|
||
}
|
||
/* Flatten the expanded doc/email card on mobile — drop the inner
|
||
border + background so it doesn't read as a "subwindow" inside the
|
||
modal (the chat preview doesn't have that nested-card look). The
|
||
body prefix beats the desktop email rule later in the file that
|
||
paints a 2px accent border + box-shadow with !important. */
|
||
body #doclib-modal .doclib-card.doclib-card-expanded,
|
||
body #email-lib-modal .doclib-card.doclib-card-expanded,
|
||
body #memory-modal .doclib-card.doclib-card-expanded {
|
||
background: transparent !important;
|
||
border: none !important;
|
||
border-radius: 0 !important;
|
||
box-shadow: none !important;
|
||
}
|
||
/* Same layout pattern the chat preview uses: preview itself clips,
|
||
the <pre> (or PDF iframe) inside owns the scroll, and the action
|
||
bar pins to the bottom with extra bottom padding so it floats
|
||
above the iOS safe-area / home-indicator strip. */
|
||
#doclib-modal .doclib-card.doclib-card-expanded .doclib-card-preview,
|
||
#email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview,
|
||
#memory-modal .doclib-card.doclib-card-expanded .doclib-card-preview {
|
||
padding: 4px 6px 20px !important;
|
||
flex: 1 1 auto !important;
|
||
/* The preview lives inside an already bounded expanded card. Giving
|
||
it a viewport-sized minimum pushes its footer below that card,
|
||
where overflow:hidden clips it. The text child owns scrolling. */
|
||
min-height: 0 !important;
|
||
overflow: hidden !important;
|
||
display: flex !important;
|
||
flex-direction: column !important;
|
||
border-top: none !important;
|
||
}
|
||
/* The "Load more" button is flex-shrink:0 and sits in the panel AFTER the
|
||
grid, so even when a card is expanded it reserves ~40px at the panel
|
||
bottom — shrinking the visible grid and clipping the action bar by that
|
||
much (the "black bar"). The global collapse rule's max-height:0 can't
|
||
remove a flex-shrink:0 item, so hide it outright on expand. */
|
||
#doclib-modal [data-doclib-panel="documents"]:has(.doclib-card-expanded) .doclib-load-more,
|
||
#doclib-modal [data-doclib-panel="documents"]:has(.doclib-card-expanded) .doclib-inline-load-more {
|
||
display: none !important;
|
||
}
|
||
/* Small bottom padding now that the load-more no longer steals space —
|
||
the action bar sits low, just clearing the home-indicator safe area. */
|
||
#doclib-modal [data-doclib-panel="documents"] .doclib-card.doclib-card-expanded .doclib-card-preview {
|
||
padding-bottom: calc(18px + env(safe-area-inset-bottom, 0px)) !important;
|
||
}
|
||
#doclib-modal .doclib-card.doclib-card-expanded .doclib-card-preview pre,
|
||
#doclib-modal .doclib-card.doclib-card-expanded .doclib-card-preview .doclib-card-pdf-frame,
|
||
#email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview pre,
|
||
#email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview .email-reader-body,
|
||
#memory-modal .doclib-card.doclib-card-expanded .doclib-card-preview pre,
|
||
#memory-modal .doclib-card.doclib-card-expanded .doclib-card-preview .skill-md-editor {
|
||
flex: 1 1 0 !important;
|
||
min-height: 0 !important;
|
||
overflow-y: auto !important;
|
||
-webkit-overflow-scrolling: touch;
|
||
/* Strip the code-box visual treatment so the <pre> / reader body
|
||
doesn't read as a "sub-window" inside the modal. */
|
||
background: transparent !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
padding: 0 !important;
|
||
margin: 0 !important;
|
||
border-radius: 0 !important;
|
||
}
|
||
/* The skill editor textarea keeps a light frame on mobile (it's an
|
||
input, not read-only text) — override the strip-down above. */
|
||
#memory-modal .doclib-card.doclib-card-expanded .doclib-card-preview .skill-md-editor {
|
||
background: var(--bg) !important;
|
||
border: 1px solid var(--border) !important;
|
||
padding: 8px !important;
|
||
}
|
||
#doclib-modal .doclib-card.doclib-card-expanded .doclib-card-preview pre code,
|
||
#doclib-modal .doclib-card.doclib-card-expanded .doclib-card-preview code.hljs,
|
||
#email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview pre code,
|
||
#email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview code.hljs,
|
||
#memory-modal .doclib-card.doclib-card-expanded .doclib-card-preview pre code {
|
||
background: transparent !important;
|
||
border: none !important;
|
||
padding: 0 !important;
|
||
box-shadow: none !important;
|
||
}
|
||
#doclib-modal .doclib-card.doclib-card-expanded .doclib-card-expanded-actions,
|
||
#email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-expanded-actions,
|
||
#memory-modal .doclib-card.doclib-card-expanded .doclib-card-expanded-actions {
|
||
padding: 6px 4px 0 !important;
|
||
margin-top: 4px !important;
|
||
flex-shrink: 0 !important;
|
||
}
|
||
/* Email reader on mobile inherits the library modal's horizontal
|
||
padding (the .doclib-modal-content default — 6px). No extra
|
||
overrides for the modal-content / modal-body / admin-card / grid
|
||
chain; we just clean up the inner email reader header/body
|
||
padding so the From / To lines align with the email subject. */
|
||
#email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview .email-reader-header,
|
||
#email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview .email-reader-atts,
|
||
#email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview .email-reader-body {
|
||
padding-left: 6px !important;
|
||
padding-right: 6px !important;
|
||
}
|
||
#email-lib-modal .doclib-card.doclib-card-expanded .doclib-card-preview .email-reader-body {
|
||
margin-left: -6px !important;
|
||
margin-right: -6px !important;
|
||
padding-left: 12px !important;
|
||
padding-right: 12px !important;
|
||
background: var(--panel) !important;
|
||
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--border) 65%, transparent) !important;
|
||
}
|
||
/* Mobile email-reader header: meta on the left, actions on the right
|
||
(two stacked rows). The two .email-reader-actions-row siblings
|
||
render as their own flex-row strips so primary (reply/reply-all/
|
||
forward) sits above secondary (AI/summary/more), instead of
|
||
flattening into one line that collided with the recipient chips. */
|
||
#email-lib-modal .email-reader-header,
|
||
.email-reader-tab-modal .email-reader-header,
|
||
.email-window-modal .email-reader-header {
|
||
padding: 8px 8px !important;
|
||
gap: 6px !important;
|
||
flex-direction: row !important;
|
||
/* Top-align meta + actions on mobile. With the two-row action
|
||
cluster (Summary/More above Reply/Forward/AI), align-items:
|
||
center pulled the meta's From:/To: rows down into the visual
|
||
middle of the header, leaving them visibly off-axis. */
|
||
align-items: flex-start !important;
|
||
}
|
||
#email-lib-modal .email-reader-actions,
|
||
.email-reader-tab-modal .email-reader-actions,
|
||
.email-window-modal .email-reader-actions {
|
||
display: flex !important;
|
||
flex-direction: column !important;
|
||
align-items: flex-end !important;
|
||
gap: 4px !important;
|
||
flex-shrink: 0 !important;
|
||
margin-left: auto !important;
|
||
order: 0 !important;
|
||
}
|
||
#email-lib-modal .email-reader-actions-row,
|
||
.email-reader-tab-modal .email-reader-actions-row,
|
||
.email-window-modal .email-reader-actions-row {
|
||
display: flex !important;
|
||
flex-direction: row !important;
|
||
flex-wrap: nowrap !important;
|
||
align-items: center !important;
|
||
justify-content: flex-end !important;
|
||
gap: 4px !important;
|
||
}
|
||
/* Meta on mobile: From / To / Cc each on their own row, label
|
||
tight to chips, chips scroll horizontally per row. */
|
||
#email-lib-modal .email-reader-meta,
|
||
.email-reader-tab-modal .email-reader-meta,
|
||
.email-window-modal .email-reader-meta {
|
||
display: flex !important;
|
||
flex-direction: column !important;
|
||
gap: 2px !important;
|
||
min-width: 0 !important;
|
||
width: 100% !important;
|
||
}
|
||
#email-lib-modal .email-reader-meta-row,
|
||
.email-reader-tab-modal .email-reader-meta-row,
|
||
.email-window-modal .email-reader-meta-row {
|
||
display: flex !important;
|
||
grid-template-columns: none !important;
|
||
align-items: center !important;
|
||
gap: 6px !important;
|
||
padding-right: 0 !important;
|
||
min-width: 0 !important;
|
||
}
|
||
#email-lib-modal .email-reader-meta-row .recipient-chips,
|
||
.email-reader-tab-modal .email-reader-meta-row .recipient-chips,
|
||
.email-window-modal .email-reader-meta-row .recipient-chips {
|
||
display: inline-flex !important;
|
||
flex-wrap: nowrap !important;
|
||
overflow-x: auto !important;
|
||
overflow-y: hidden !important;
|
||
max-width: 100% !important;
|
||
scrollbar-width: none !important;
|
||
gap: 4px !important;
|
||
}
|
||
#email-lib-modal .email-reader-meta-row .recipient-chips::-webkit-scrollbar,
|
||
.email-reader-tab-modal .email-reader-meta-row .recipient-chips::-webkit-scrollbar,
|
||
.email-window-modal .email-reader-meta-row .recipient-chips::-webkit-scrollbar {
|
||
display: none !important;
|
||
}
|
||
#email-lib-modal .email-reader-actions .memory-toolbar-btn.reader-icon-btn,
|
||
.email-reader-tab-modal .email-reader-actions .memory-toolbar-btn.reader-icon-btn,
|
||
.email-window-modal .email-reader-actions .memory-toolbar-btn.reader-icon-btn {
|
||
width: auto !important;
|
||
height: 44px !important;
|
||
flex: 0 0 auto !important;
|
||
display: inline-flex !important;
|
||
flex-direction: column !important;
|
||
align-items: center !important;
|
||
justify-content: center !important;
|
||
gap: 3px !important;
|
||
padding: 4px 2px !important;
|
||
}
|
||
#email-lib-modal .email-reader-actions .memory-toolbar-btn.reader-icon-btn svg,
|
||
.email-reader-tab-modal .email-reader-actions .memory-toolbar-btn.reader-icon-btn svg,
|
||
.email-window-modal .email-reader-actions .memory-toolbar-btn.reader-icon-btn svg {
|
||
width: 16px !important;
|
||
height: 16px !important;
|
||
}
|
||
/* Gallery-style label under each button — only on mobile. The
|
||
global rule below the @media block hides them on desktop. */
|
||
#email-lib-modal .reader-btn-label,
|
||
.email-reader-tab-modal .reader-btn-label,
|
||
.email-window-modal .reader-btn-label {
|
||
display: inline-block !important;
|
||
font-size: 8.5px !important;
|
||
font-weight: 500 !important;
|
||
line-height: 1 !important;
|
||
letter-spacing: 0.02em !important;
|
||
opacity: 0.75 !important;
|
||
white-space: nowrap !important;
|
||
}
|
||
/* List-view cards keep the framed look from their own .doclib-card /
|
||
.memory-item base styles — no extra grid padding here, so the
|
||
spacing matches the documents/library tab exactly. */
|
||
/* Tighten the top of the email sheet — modal header + description +
|
||
account row + toolbar were stacking with full desktop spacing and
|
||
pushed the email subjects way down. */
|
||
#email-lib-modal .modal-header {
|
||
padding: 4px 8px !important;
|
||
min-height: 0 !important;
|
||
}
|
||
#email-lib-modal .modal-header h4 {
|
||
/* Match the other tool headers (base .modal-header h4 = 1rem); was
|
||
13px, which read noticeably smaller than Calendar/Tasks/etc. */
|
||
font-size: 1rem !important;
|
||
line-height: 1.2 !important;
|
||
}
|
||
#email-lib-modal .modal-body {
|
||
gap: 4px !important;
|
||
}
|
||
#email-lib-modal .admin-card {
|
||
gap: 4px !important;
|
||
}
|
||
#email-lib-modal .admin-card > .doclib-desc {
|
||
display: none !important;
|
||
}
|
||
/* When an email is expanded the list-mode toolbar siblings are
|
||
already hidden by the existing :has rule. Hide the modal-header
|
||
entirely AND zero out the modal-content top padding so the email
|
||
reader claims the full sheet height. The swipe-down gesture (and
|
||
the dock chip) still dismiss the modal. */
|
||
#email-lib-modal:has(.doclib-card-expanded) .modal-header,
|
||
#email-lib-modal.email-reading .modal-header {
|
||
display: none !important;
|
||
}
|
||
#email-lib-modal:has(.doclib-card-expanded) .doclib-modal-content,
|
||
#email-lib-modal.email-reading .doclib-modal-content {
|
||
padding-top: 0 !important;
|
||
}
|
||
#email-lib-modal:has(.doclib-card-expanded) .modal-body,
|
||
#email-lib-modal.email-reading .modal-body {
|
||
gap: 0 !important;
|
||
}
|
||
/* Flatten the From / To bar so it doesn't read as a cut-off framed
|
||
strip with a hard background change. Remove the background, drop
|
||
the border-bottom to a faint divider, and let it blend with the
|
||
sheet. */
|
||
#email-lib-modal .email-reader-header {
|
||
background: transparent !important;
|
||
border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent) !important;
|
||
}
|
||
#email-lib-modal .email-card-nav-btn {
|
||
padding: 6px 10px !important;
|
||
min-width: 40px !important;
|
||
height: 38px !important;
|
||
}
|
||
#email-lib-modal .email-card-nav-btn svg {
|
||
width: 18px !important;
|
||
height: 18px !important;
|
||
}
|
||
/* Nudge the prev/next arrow cluster ~4px to the right so it sits
|
||
comfortably out at the edge instead of crowding the subject text. */
|
||
#email-lib-modal .email-card-nav-arrows {
|
||
transform: translateX(4px);
|
||
}
|
||
/* Done check — extend the actual TAP area via padding + negative
|
||
margin (a transparent ::before doesn't change the parent's hit
|
||
box). Layout stays the same height as library cards because the
|
||
margin cancels the padding visually, but the clickable region is
|
||
~37px square. */
|
||
#email-lib-modal .email-card-done {
|
||
position: relative !important;
|
||
z-index: 5 !important;
|
||
pointer-events: auto !important;
|
||
touch-action: manipulation !important;
|
||
padding: 12px !important;
|
||
margin: -12px !important;
|
||
}
|
||
#email-lib-modal .email-card-done svg {
|
||
width: 13px !important;
|
||
height: 13px !important;
|
||
}
|
||
/* Make sure no overlay or pseudo intercepts the tap. */
|
||
#email-lib-modal .email-card-done * { pointer-events: none; }
|
||
/* Tighten the From / To meta bar and the email body — they had
|
||
desktop padding (10–14px) that wasted real estate on phones. */
|
||
#email-lib-modal .email-reader-header {
|
||
padding: 6px 4px !important;
|
||
gap: 4px !important;
|
||
}
|
||
#email-lib-modal .email-reader-meta {
|
||
font-size: 11px !important;
|
||
}
|
||
#email-lib-modal .email-reader-meta-row strong {
|
||
min-width: 28px !important;
|
||
}
|
||
#email-lib-modal .email-reader-atts {
|
||
padding: 6px 4px !important;
|
||
}
|
||
#email-lib-modal .email-reader-body {
|
||
padding: 8px 4px !important;
|
||
}
|
||
/* Make sure the grid claims the full admin-card height when a doc
|
||
is expanded — the existing rule sets flex:1, but on mobile we also
|
||
need a hard height fallback because the parent uses dvh which
|
||
desktop-tuned selectors don't always trickle through. */
|
||
#doclib-modal .admin-card:has(.doclib-card-expanded) > .doclib-grid,
|
||
#memory-modal .admin-card:has(.doclib-card-expanded) > .doclib-grid {
|
||
height: 100% !important;
|
||
}
|
||
/* Documents need the same bounded flex chain as skills when a card is
|
||
opened. Without this, the grid falls back to its list layout and the
|
||
expanded preview only grows to its content height on mobile. */
|
||
#doclib-modal .admin-card:has(.doclib-card-expanded) > .doclib-grid {
|
||
display: flex !important;
|
||
flex: 1 1 0 !important;
|
||
flex-direction: column !important;
|
||
min-height: 0 !important;
|
||
overflow: hidden !important;
|
||
}
|
||
#doclib-modal .doclib-card.doclib-card-expanded {
|
||
flex: 1 1 auto !important;
|
||
height: 100% !important;
|
||
min-height: 0 !important;
|
||
}
|
||
#doclib-modal .doclib-card.doclib-card-expanded .doclib-card-preview {
|
||
flex: 1 1 0 !important;
|
||
min-height: 0 !important;
|
||
}
|
||
#doclib-modal .doclib-card.doclib-card-expanded .doclib-card-pdf-frame {
|
||
height: auto !important;
|
||
flex: 1 1 0 !important;
|
||
min-height: 0 !important;
|
||
}
|
||
/* Skills modal: keep the header + tab strip visible; the expanded
|
||
card fills the skills-list area below them via position:absolute
|
||
(see the #skills-list rule above). Just make sure the tab-panel +
|
||
its card give the list its full height. */
|
||
#memory-modal .memory-tab-panel[data-memory-panel="skills"] > .admin-card:has(.doclib-card-expanded) {
|
||
flex: 1 1 auto !important;
|
||
min-height: 0 !important;
|
||
}
|
||
.doclib-card-header {
|
||
padding: 10px 8px;
|
||
gap: 4px;
|
||
}
|
||
.doclib-card-session,
|
||
.doclib-card-time {
|
||
display: none;
|
||
}
|
||
.doclib-card-expanded-actions {
|
||
flex-wrap: nowrap;
|
||
}
|
||
/* Keep the footer buttons identical to the chat/research footers on
|
||
mobile too — those have no mobile enlargement, so neither should
|
||
these (otherwise the doc footer reads in a larger/different font). */
|
||
.doclib-card-action-btn {
|
||
font-size: 10px;
|
||
padding: 3px 8px;
|
||
}
|
||
/* On phones, keep Delete explicit while the other secondary actions
|
||
stay available from the kebab. */
|
||
#doclib-modal .doclib-card-expanded-actions > .doclib-expanded-archive-btn,
|
||
#doclib-modal .doclib-card-expanded-actions .doclib-expanded-clone-btn,
|
||
#doclib-modal .doclib-card-expanded-actions .doclib-expanded-export-btn {
|
||
display: none !important;
|
||
}
|
||
#doclib-modal .doclib-card-expanded-actions > .doclib-expanded-delete-btn {
|
||
display: inline-flex !important;
|
||
order: 0;
|
||
left: 0 !important;
|
||
margin-left: 0;
|
||
}
|
||
#doclib-modal .doclib-card-expanded-actions > .doclib-action-group {
|
||
order: 2;
|
||
margin-left: 6px !important;
|
||
min-width: 0;
|
||
}
|
||
#doclib-modal .doclib-card.doclib-card-expanded .doclib-card-expanded-actions {
|
||
box-sizing: border-box;
|
||
width: 100%;
|
||
max-width: 100%;
|
||
min-width: 0;
|
||
overflow: visible;
|
||
}
|
||
#doclib-modal .doclib-card-expanded-actions .doclib-expanded-open-btn {
|
||
min-height: 30px;
|
||
padding: 5px 10px;
|
||
max-width: 100%;
|
||
}
|
||
#doclib-modal .doclib-card-expanded-actions > .doclib-expanded-mobile-more {
|
||
display: inline-flex;
|
||
order: 1;
|
||
width: 32px;
|
||
min-width: 32px;
|
||
min-height: 30px;
|
||
margin-left: auto;
|
||
padding: 0;
|
||
}
|
||
/* Chat top bar — adjusted for reduced height */
|
||
/* Suggestion nav — bigger touch targets */
|
||
.doc-suggestion-nav-btn {
|
||
padding: 6px 8px;
|
||
font-size: 18px;
|
||
}
|
||
.doc-suggestion-close {
|
||
padding: 10px 12px;
|
||
margin: -10px -12px;
|
||
}
|
||
}
|
||
#mobile-backdrop, #mobile-menu-btn { display:none !important; }
|
||
#sidebar-backdrop { display:none !important; }
|
||
/* ----- Loading spinner ----- */
|
||
|
||
.spinner {
|
||
width: 24px;
|
||
height: 24px;
|
||
margin: 8px auto;
|
||
border: 3px solid var(--border);
|
||
border-top-color: var(--red);
|
||
border-radius: 50%;
|
||
animation: spin 0.9s linear infinite;
|
||
}
|
||
/* Inline spinner for buttons */
|
||
.btn-spinner {
|
||
display: inline-block;
|
||
width: 12px;
|
||
height: 12px;
|
||
border: 2px solid transparent;
|
||
border-top-color: currentColor;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
margin-right: 6px;
|
||
}
|
||
.search-status {
|
||
font-size: 0.85em;
|
||
color: var(--red);
|
||
margin-top: 4px;
|
||
padding: 4px;
|
||
border-left: 2px solid var(--red);
|
||
background: color-mix(in srgb, var(--red) 5%, transparent);
|
||
}
|
||
/* Loading indicator for messages */
|
||
.loading-indicator {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 10px;
|
||
}
|
||
.loading-dots {
|
||
display: flex;
|
||
gap: 4px;
|
||
}
|
||
.loading-dot {
|
||
width: 6px;
|
||
height: 6px;
|
||
background-color: var(--red);
|
||
border-radius: 50%;
|
||
}
|
||
.loading-dot:nth-child(1) {
|
||
animation: loading-bounce 1.4s infinite ease-in-out both;
|
||
}
|
||
.loading-dot:nth-child(2) {
|
||
animation: loading-bounce 1.4s infinite ease-in-out both;
|
||
animation-delay: -0.32s;
|
||
}
|
||
.loading-dot:nth-child(3) {
|
||
animation: loading-bounce 1.4s infinite ease-in-out both;
|
||
animation-delay: -0.64s;
|
||
}
|
||
.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; }
|
||
}
|
||
|
||
/* 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-ok {
|
||
position: relative;
|
||
top: 2px;
|
||
}
|
||
#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;
|
||
}
|
||
|
||
|
||
|
||
#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%;
|
||
}
|