mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-06 06:52:20 +02:00
static/style.css was 47,530 lines. It is now nine files under static/css/: tokens.css holds the :root custom properties, light theme and density classes, and style-part-01..08 carry the rest in their original order. Nothing was rewritten; every line moved verbatim and the <link> order in index.html reproduces the original file byte for byte. Cut points are brace-depth zero and outside block comments, so no rule or comment is split. All 47,531 source lines are accounted for across the fragments. The computed-style baseline recorded before the split is reproduced exactly, which is the evidence that the cascade is unchanged rather than an argument that it should be. Three things the split broke and this fixes: - The harness self-test swapped two conflicting .attach-strip blocks inside style.css to prove the digest is order-sensitive. It rewrote one hardcoded URL, so with the file gone it silently measured an unmodified page. It now searches every stylesheet, rewrites whichever holds the pair, and fails loudly if none does. - Four tests asserted the cache-bust token by matching /static/style.css?v=. They check the real invariant now, that every app stylesheet shares one token with app.js, through stylesheet_cache_version(). - The three panel stylesheets carried their own tokens, so a browser could hold half an old cascade and half a new one. All app stylesheets now bust together.
6013 lines
204 KiB
CSS
6013 lines
204 KiB
CSS
/* Fragment 2/9 of the former static/style.css.
|
||
*
|
||
* This is a mechanical, order-preserving slice: rules were moved verbatim
|
||
* and this file's position in the <link> order of static/index.html is what
|
||
* keeps the cascade identical. Reordering the links, or moving a rule between
|
||
* fragments, can change which of two competing declarations wins.
|
||
*
|
||
* static/index.html and the PRECACHE list in static/sw.js must list every
|
||
* fragment in the same order. tests/test_static_stylesheet_manifest.py and
|
||
* tests/test_css_computed_style_snapshot.py both fail if that drifts.
|
||
*/
|
||
#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); }
|
||
}
|
||
@keyframes spin { to { transform: rotate(360deg); } }
|
||
.row { display:flex; gap:6px; align-items:center; }
|
||
.list-item:hover,
|
||
.models-row:hover {
|
||
background: color-mix(in srgb, var(--red) 8%, transparent);
|
||
border-color: var(--red);
|
||
}
|
||
#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: var(--color-success, #4caf50);
|
||
border-color: var(--color-success, #4caf50);
|
||
color: #fff;
|
||
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: '\2713';
|
||
font-family: system-ui, sans-serif;
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
line-height: 1;
|
||
transform: translateY(-0.5px);
|
||
}
|
||
.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 pill / dropdown in composer */
|
||
.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;
|
||
}
|
||
@container chatbar (max-width: 480px) {
|
||
.reasoning-effort-prefix { display: none; }
|
||
}
|
||
@media (max-width: 480px) {
|
||
.reasoning-effort-prefix { display: none; }
|
||
}
|
||
.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 .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;
|
||
}
|
||
.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:11px 13px 12px;
|
||
font-size:0.78rem;
|
||
color:var(--fg);
|
||
box-shadow:0 8px 24px rgba(0,0,0,0.4);
|
||
min-width:250px;
|
||
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:4px;
|
||
}
|
||
.ctx-stat-section + .ctx-stat-section {
|
||
margin-top:9px;
|
||
padding-top:8px;
|
||
border-top:1px solid color-mix(in srgb, var(--border) 45%, transparent);
|
||
}
|
||
.ctx-stat-row {
|
||
display:grid;
|
||
grid-template-columns:minmax(0, 1fr) auto;
|
||
align-items:baseline;
|
||
gap:14px;
|
||
min-height:18px;
|
||
}
|
||
.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;
|
||
}
|
||
.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 ----- */
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
.spinner {
|
||
width: 24px;
|
||
height: 24px;
|
||
margin: 8px auto;
|
||
border: 3px solid var(--border);
|
||
border-top-color: var(--red);
|
||
border-radius: 50%;
|
||
animation: spin 0.9s linear infinite;
|
||
}
|
||
/* Inline spinner for buttons */
|
||
.btn-spinner {
|
||
display: inline-block;
|
||
width: 12px;
|
||
height: 12px;
|
||
border: 2px solid transparent;
|
||
border-top-color: currentColor;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
margin-right: 6px;
|
||
}
|
||
.search-status {
|
||
font-size: 0.85em;
|
||
color: var(--red);
|
||
margin-top: 4px;
|
||
padding: 4px;
|
||
border-left: 2px solid var(--red);
|
||
background: color-mix(in srgb, var(--red) 5%, transparent);
|
||
}
|
||
/* Loading indicator for messages */
|
||
.loading-indicator {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 10px;
|
||
}
|
||
.loading-dots {
|
||
display: flex;
|
||
gap: 4px;
|
||
}
|
||
.loading-dot {
|
||
width: 6px;
|
||
height: 6px;
|
||
background-color: var(--red);
|
||
border-radius: 50%;
|
||
}
|