Files
odysseus/static/css/05-documents.css
T
Alexandre Teixeira f74a262f73 merge: reconcile PR 40 with current lab
Integrate lab fff55a78 into PR #40 (cc25d5ba). Lab's modular email
backend/frontend, modular settings, split stylesheets (static/style.css
stays deleted), procfs compatibility, and request-scoped TurnContract
authority win; PR #40's routing classifiers, editor/email/task features,
and style.css changes are ported into lab's module and stylesheet homes.

Integration fixes:
- settings/api.js imports ui.js under its canonical versioned URL
- browser observations keep legacy CAPTCHA/access-block evidence
- artifact turns do not re-trigger broad-web research recovery
- env reference documents PR test-tool variables; page regenerated

PR #40 defects surfaced by lab gates and fixed here:
- web_fetch generic schema drops top-level anyOf (OpenAI contract);
  the compact preview contract still requires url or urls
- get_weather registered as a brokered network read
- new lazy editor modules precached for offline use
- SearXNG pin mirrored into GPU standalone compose files
- image model picker again skips offline endpoints

Tests updated where PR #40 changed behaviour on purpose, and PR tests
moved onto lab's document_source helpers.
2026-10-01 05:03:58 +01:00

3203 lines
91 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 05-documents - part of the former static/style.css.
*
* The stylesheet is split by area, but the split is mechanical: rules were
* moved verbatim and NOT regrouped. The numeric prefix is the load order, and
* that order is what preserves the cascade. A rule sits in the file its
* original position fell into, so an area's rules are not all in its file and
* a file is not only that area's rules.
*
* Do not reorder the <link> tags in static/index.html, and do not move a rule
* between files: with 1,151 redeclarations and 1,886 !important in this
* cascade, either can change which declaration wins.
*
* static/index.html and the PRECACHE list in static/sw.js must agree on this
* order. tests/test_static_stylesheet_manifest.py and
* tests/test_css_computed_style_snapshot.py fail if that drifts.
*/
.memory-suggestions-actions {
position: relative;
left: -4px;
}
.memory-suggestions-actions .memory-item-btn,
.memory-suggestion-actions .memory-item-btn {
background: color-mix(in srgb, var(--fg) 6%, transparent);
border-color: color-mix(in srgb, var(--border) 85%, transparent);
}
.memory-suggestions-actions .memory-item-btn.save,
.memory-suggestion-actions .memory-item-btn.save {
background: color-mix(in srgb, var(--red) 9%, transparent);
border-color: color-mix(in srgb, var(--red) 28%, transparent);
}
.memory-suggestion-item {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 6px;
padding: 5px 8px;
border: 1px solid var(--border);
border-radius: 6px;
background: color-mix(in srgb, var(--red) 3%, transparent);
}
/* Tidy animations */
.memory-tidy-editing {
border-color: color-mix(in srgb, var(--hl-function) 40%, transparent);
background: color-mix(in srgb, var(--hl-function) 6%, transparent);
transition: all 0.3s;
}
.memory-tidy-text-old {
opacity: 0.4;
text-decoration: line-through;
transition: opacity 0.25s;
}
.memory-tidy-text-new {
color: var(--hl-function);
transition: color 0.4s;
}
.memory-tidy-removing {
text-decoration: line-through;
opacity: 0;
max-height: 0;
padding-top: 0;
padding-bottom: 0;
margin-top: 0;
margin-bottom: 0;
border-color: transparent;
overflow: hidden;
transition: opacity 0.3s, max-height 0.4s 0.1s, padding 0.4s 0.1s, border-color 0.3s;
}
/* ===== DOCUMENT EDITOR (ARTIFACTS) PANEL ===== */
/* Doc editor is a body-level sibling of chat-container */
/* Divider between chat and doc editor */
.doc-divider {
width: 1px;
flex-shrink: 0;
background: color-mix(in srgb, var(--fg) 11%, transparent);
cursor: col-resize;
transition: background 0.15s;
position: relative;
z-index: 10;
}
.doc-divider::before {
content: '';
position: absolute;
top: 0; bottom: 0;
left: -10px;
width: 21px;
cursor: col-resize;
}
.doc-divider:hover {
background: color-mix(in srgb, var(--fg) 30%, transparent);
}
/* Always-visible "›" handle on the drag divider — clickable to collapse the
panel, and signals the divider is interactive. */
.doc-divider-collapse {
position: absolute;
top: 50%;
left: 2px;
transform: translateY(-50%);
width: 20px;
height: 34px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--border);
border-radius: 7px;
background: var(--panel);
color: var(--fg);
font-size: 16px;
font-weight: 700;
line-height: 1;
opacity: 0.6;
cursor: pointer;
transition: opacity 0.1s, border-color 0.1s, left 0.08s ease;
z-index: 12;
}
.doc-divider.doc-divider-handle-idle:hover .doc-divider-collapse,
.doc-divider.doc-divider-handle-idle:focus-within .doc-divider-collapse {
opacity: 0.92;
pointer-events: auto;
}
/* When in fullscreen mode (cursor outside the doc), the flap itself slides
to the OUTSIDE edge of the divider — visually belonging to the chat side
the cursor is on, not the doc. The left/right shift pairs with the glyph
rotation for a single coordinated transition. */
.doc-divider-collapse[data-mode="fullscreen"] {
left: -22px;
}
.doc-divider-collapse:hover { opacity: 1 !important; border-color: var(--accent, var(--red)); }
/* The same `›` glyph is in the markup; CSS rotates 180° for the left-pointing
(fullscreen) state. Smooth transition pairs with the chevron's slide. */
.doc-divider-collapse > span {
display: inline-block;
transition: transform 0.08s ease, opacity 0.08s ease;
}
.doc-divider-collapse[data-mode="fullscreen"] > span {
transform: rotate(180deg);
}
/* Secondary "hide panel" X button in the divider — invisible until the pane
is fullscreen, then floats just below the unfullscreen chevron so the user
has a one-tap escape that minimises the pane instead of just exiting
fullscreen. */
.doc-divider-hide {
position: absolute;
top: 50%;
left: 2px;
width: 20px;
height: 20px;
margin-top: 22px;
display: none;
align-items: center;
justify-content: center;
border: 1px solid var(--border);
border-radius: 50%;
background: var(--panel);
color: var(--accent, var(--red));
cursor: pointer;
padding: 0;
z-index: 12;
opacity: 0.85;
transition: opacity 0.15s, border-color 0.15s;
}
.doc-divider-hide:hover {
opacity: 1;
border-color: var(--accent, var(--red));
}
/* Copy / Export split button — main click copies, the caret opens the export menu. */
.doc-split-btn {
display: inline-flex;
align-items: stretch;
border: 1px solid var(--border);
border-radius: 7px;
overflow: hidden;
height: 22px;
flex-shrink: 0;
}
.doc-split-btn .doc-split-main,
.doc-split-btn .doc-split-caret {
border: none !important;
border-radius: 0 !important;
height: 100% !important;
min-height: 0 !important;
/* Keep the element fully opaque so the divider line stays crisp; dim the
glyph via colour instead (the base .doc-action-icon-btn fades the whole
element to 0.3, which also hides the divider). */
opacity: 1 !important;
color: color-mix(in srgb, var(--fg) 55%, transparent) !important;
background: none !important;
transition: color 0.12s, background 0.12s;
}
.doc-split-btn:hover .doc-split-main,
.doc-split-btn:hover .doc-split-caret { color: var(--fg) !important; }
.doc-split-btn .doc-split-caret {
border-left: 1px solid var(--border) !important;
padding: 0 5px 0 7px !important;
}
.doc-split-btn .doc-split-main:hover,
.doc-split-btn .doc-split-caret:hover {
background: color-mix(in srgb, var(--fg) 8%, transparent) !important;
color: var(--fg) !important;
}
/* Editor pane — body-level flex sibling */
.doc-editor-pane {
flex: 1;
min-width: 0;
max-width: 70vw;
container-type: inline-size;
container-name: docpane;
display: flex;
flex-direction: column;
background: var(--bg);
border-left: 1px solid var(--border);
box-shadow: -10px 0 22px rgba(0, 0, 0, 0.16);
overflow: hidden;
height: 100%;
position: relative;
z-index: 1;
color-scheme: dark;
/* Desktop entrance is driven by openPanel() so the pane has one transform
owner. The mobile sheet has its own animation below. */
animation: none;
transform-origin: right center;
will-change: clip-path, opacity;
}
@media (prefers-reduced-motion: reduce) {
.doc-editor-pane,
.doc-editor-pane.doc-pane-leaving { animation: none; }
}
.doc-loading-overlay {
position: absolute;
inset: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg);
}
/* ---- Tab bar ---- */
.doc-tab-bar {
display: flex;
align-items: stretch;
background: var(--bg);
border-bottom: 1px solid var(--border);
flex-shrink: 0;
height: 36px;
}
.doc-tab-scroll {
display: flex;
align-items: stretch;
flex: 1;
min-width: 0;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
justify-content: flex-start;
/* Fade tabs into the bar's background at the edges (next to the scroll
arrows) so an overflowing tab dissolves instead of being hard-cut. The
fade is conditional — when we're at an edge there's nothing to fade to,
so the mask gradient becomes flat on that side (no shadow). */
-webkit-mask-image: linear-gradient(to right, transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%);
mask-image: linear-gradient(to right, transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%);
}
.doc-tab-arrow {
background: none;
border: none;
color: var(--fg);
opacity: 0.55;
cursor: pointer;
font-size: 18px;
padding: 0 6px;
flex-shrink: 0;
transition: opacity 0.15s;
line-height: 1;
display: flex;
align-items: center;
position: relative;
top: -2px;
}
.doc-tab-arrow:hover {
opacity: 1;
}
/* Mobile swipe-down grab handle at the top of the doc sheet. */
.doc-mobile-grabber { display: none; }
@media (max-width: 768px) {
body.doc-view .doc-mobile-grabber {
display: block;
flex-shrink: 0;
height: 18px;
position: relative;
background: transparent;
background-color: transparent;
background-image: none;
touch-action: none;
cursor: grab;
}
body.doc-view .doc-mobile-grabber::before {
content: '';
position: absolute;
top: 7px;
left: 50%;
transform: translateX(-50%);
width: 36px;
height: 4px;
background: var(--fg);
opacity: 0.25;
border-radius: 2px;
}
}
.doc-tab {
display: flex;
align-items: center;
gap: 6px;
padding: 0 10px;
font-family: inherit;
font-size: 11px;
color: var(--fg);
opacity: 0.4;
cursor: pointer;
white-space: nowrap;
transition: opacity 0.1s, background 0.1s;
flex-shrink: 0;
border-right: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
position: relative;
}
.doc-tab:hover {
opacity: 0.7;
background: color-mix(in srgb, var(--fg) 3%, transparent);
}
.doc-tab.active {
opacity: 1;
background: color-mix(in srgb, var(--fg) 5%, transparent);
border-radius: 7px 7px 0 0;
}
.doc-tab-title {
max-width: 140px;
overflow: hidden;
text-overflow: ellipsis;
}
.doc-tab-title-input {
background: transparent;
border: 1px solid var(--fg);
border-radius: 2px;
color: var(--fg);
font-family: inherit;
font-size: 11px;
padding: 0 4px;
height: 18px;
width: 120px;
max-width: 180px;
outline: none;
}
.doc-tab-lang {
font-size: 9px;
opacity: 0.5;
}
.doc-tab-close {
display: inline-flex;
align-items: center;
justify-content: center;
background: none;
border: 1px solid transparent;
border-radius: 999px;
color: var(--fg);
opacity: 0.4;
cursor: pointer;
font-size: 18px;
line-height: 1;
width: 24px;
height: 24px;
padding: 0;
box-sizing: border-box;
margin-left: 3px;
flex-shrink: 0;
align-self: center;
transition: opacity 0.12s, color 0.12s, background 0.12s, border-color 0.12s;
}
.doc-tab-close:hover {
opacity: 1;
color: var(--fg);
background: color-mix(in srgb, var(--fg) 10%, transparent);
border-color: color-mix(in srgb, var(--fg) 12%, transparent);
}
/* Mobile-only footer (Close + Copy); hidden on desktop and in email mode. */
.doc-mobile-footer { display: none; }
.doc-tab-new {
background: none;
border: none;
color: var(--fg);
opacity: 0.25;
cursor: pointer;
font-size: 11px;
font-weight: 600;
padding: 0 10px;
transition: opacity 0.1s;
flex-shrink: 0;
display: flex;
align-items: center;
height: 100%;
}
.doc-tab-new:hover {
opacity: 0.7;
}
.doc-tab-play {
background: none;
border: none;
color: var(--fg);
opacity: 0.55;
cursor: pointer;
padding: 0 2px;
font-size: 10px;
line-height: 1;
transition: opacity 0.1s, color 0.1s;
flex-shrink: 0;
}
.doc-tab-play:hover {
opacity: 1;
color: var(--green, #4ec970);
}
.doc-tab-play.active {
opacity: 1;
color: var(--green, #4ec970);
}
.doc-tab.dragging {
opacity: 0.3;
}
.doc-tab.drag-over {
border-left: 2px solid var(--fg);
}
/* ---- HTML preview iframe ---- */
.doc-html-preview {
flex: 1;
width: 100%;
border: none;
background: #fff;
}
/* ---- Editor header ---- */
.doc-editor-header {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
/* Moved to the bottom as a footer: flex order pushes it last in the pane
column, and the divider flips to the top edge. */
order: 99;
border-top: 1px solid var(--border);
flex-shrink: 0;
flex-wrap: nowrap;
min-height: 36px;
background: var(--bg);
}
/* Version now lives in the document tab, not the footer. */
#doc-version-badge { display: none !important; }
/* Language icon chip on doc tabs — sits between the version pill and title.
Hidden when empty so docs without a language don't get awkward spacing. */
.doc-tab-lang {
display: inline-flex; align-items: center;
flex-shrink: 0;
align-self: center;
}
.doc-tab-lang:empty { display: none; }
.doc-tab-lang svg { display: block; }
.doc-tab-version {
font-size: 9px;
font-weight: 600;
padding: 1px 6px;
cursor: pointer;
flex-shrink: 0;
align-self: center;
line-height: 1.4;
/* Sits to the LEFT of the title now — space it off the title text. */
margin-right: 6px;
/* Accent pill so it's obvious the version is a clickable control. */
color: var(--accent-primary, var(--red));
border: 1px solid color-mix(in srgb, var(--accent-primary, var(--red)) 45%, transparent);
background: color-mix(in srgb, var(--accent-primary, var(--red)) 12%, transparent);
border-radius: 9px;
}
.doc-tab-version:hover {
border-color: var(--accent-primary, var(--red));
background: color-mix(in srgb, var(--accent-primary, var(--red)) 22%, transparent);
}
.doc-close-btn {
order: -1;
opacity: 0.5;
flex-shrink: 0;
}
.doc-close-btn:hover {
opacity: 1;
}
.doc-editor-actions {
display: flex;
align-items: center;
gap: 6px;
flex: 1;
justify-content: flex-end;
}
.doc-left .doc-editor-actions {
margin-left: 0;
}
.doc-version-badge {
background: color-mix(in srgb, var(--red) 12%, transparent);
border: 1px solid color-mix(in srgb, var(--red) 55%, transparent);
color: var(--red);
padding: 1px 8px;
border-radius: 999px;
font-size: 10px;
font-weight: 600;
line-height: 1.4;
cursor: pointer;
user-select: none;
transition: background 0.1s, border-color 0.1s;
opacity: 0.9;
}
.doc-version-badge:hover {
opacity: 1;
background: color-mix(in srgb, var(--red) 20%, transparent);
border-color: var(--red);
}
.doc-stream-indicator {
display: flex;
align-items: center;
gap: 5px;
font-size: 11px;
color: var(--accent);
opacity: 0.9;
white-space: nowrap;
animation: doc-stream-pulse 1.5s ease-in-out infinite;
}
.doc-stream-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--accent);
}
/* In the doc footer the type picker sits next to the accent Copy/Export split —
match its 28px height and 6px radius so the right-hand controls line up. */
.doc-actions-footer #doc-language-select {
height: 28px;
border-radius: 6px;
font-size: 11px;
top: 0;
}
/* Lang-type icon shown to the LEFT of the language select. Browsers won't
render SVG inside <option>, so this surface the current selection's icon
externally. */
#doc-language-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px; height: 18px;
color: var(--accent-primary, var(--red));
flex-shrink: 0;
}
#doc-language-icon:empty { display: none; }
#doc-language-icon svg { display: block; }
/* Visually hidden but available to assistive tech (screen readers, axe).
Use for content that should be announced/structural but not painted —
e.g. the persistent page <h1>. */
.a11y-visually-hidden {
position: absolute !important;
width: 1px !important; height: 1px !important;
padding: 0 !important; margin: -1px !important;
overflow: hidden !important; clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important; border: 0 !important;
}
/* ── Custom language type picker (replaces visible chrome of native <select>
— <option>s can't render SVG). Hidden select stays as the source of truth. */
.doc-langpicker-native-hidden {
position: absolute !important;
width: 1px !important; height: 1px !important;
padding: 0 !important; margin: -1px !important;
overflow: hidden !important; clip: rect(0,0,0,0) !important;
border: 0 !important;
}
.doc-langpicker-trigger {
display: inline-flex; align-items: center; gap: 6px;
height: 28px; padding: 0 8px 0 10px;
background: var(--bg); color: var(--fg);
border: 1px solid var(--border); border-radius: 6px;
font-size: 11px; cursor: pointer;
transition: border-color 0.12s, background 0.12s;
}
.doc-langpicker-trigger:hover {
border-color: color-mix(in srgb, var(--accent-primary, var(--red)) 60%, var(--border));
}
.doc-langpicker-trigger.doc-langpicker-email-compact .doc-langpicker-label,
.doc-langpicker-trigger.doc-langpicker-email-compact > svg:last-child {
display: none;
}
.doc-langpicker-trigger svg { display: block; flex-shrink: 0; }
.doc-langpicker-label {
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
max-width: 90px;
}
.doc-langpicker-ico-blank {
display: inline-block; width: 14px; height: 14px; flex-shrink: 0;
}
.doc-langpicker-menu {
background: var(--bg);
border: 1px solid var(--border); border-radius: 8px;
padding: 4px;
box-shadow: 0 10px 28px rgba(0,0,0,0.32);
max-height: 60vh; overflow-y: auto;
z-index: 10000;
min-width: 160px;
}
.doc-langpicker-item {
display: flex; align-items: center; gap: 8px;
width: 100%;
padding: 6px 10px;
background: none; color: var(--fg);
border: none; border-radius: 5px;
font-size: 12px; text-align: left;
cursor: pointer;
}
.doc-langpicker-item:hover {
background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.doc-langpicker-group {
padding: 6px 10px 3px;
color: var(--fg-muted, var(--fg));
font-size: 9px;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
opacity: 0.55;
}
.doc-langpicker-divider {
height: 1px;
margin: 4px 6px;
background: var(--border);
opacity: 0.65;
}
.doc-langpicker-item.is-selected {
background: color-mix(in srgb, var(--accent-primary, var(--red)) 14%, transparent);
color: var(--accent-primary, var(--red));
}
.doc-langpicker-item svg { display: block; flex-shrink: 0; }
.doc-langpicker-item .doc-langpicker-label { max-width: 220px; }
.doc-language-select {
background-color: var(--bg);
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239cdef2' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 15 12 9 18 15'/></svg>");
background-repeat: no-repeat;
background-position: right 4px center;
color: var(--fg);
color-scheme: dark;
border: 1px solid var(--border);
border-radius: 5px;
position: relative;
top: 0;
font-family: inherit;
font-size: 10px;
padding: 2px 20px 2px 8px;
height: 22px;
/* Fixed width so the right-anchored chevron doesn't shift when the selected
option's text width changes. */
width: 96px;
text-overflow: ellipsis;
opacity: 0.8;
cursor: pointer;
-moz-appearance: none;
appearance: none;
}
/* Light theme: tint the chevron with the light foreground instead of cyan. */
:root.light .doc-language-select {
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%232b2b2b' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 15 12 9 18 15'/></svg>");
}
/* New-tab "+" spins on hover, like the library sidebar "+". */
.doc-tab-new svg { transition: transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1); }
.doc-tab-new:hover svg { transform: rotate(180deg) scale(1.15); }
.doc-action-btn {
background: none;
color: var(--fg);
border: none;
font-family: inherit;
font-size: 10px;
padding: 2px 8px;
height: 22px;
cursor: pointer;
opacity: 0.45;
transition: opacity 0.1s;
}
.doc-action-btn:hover {
opacity: 1;
}
.doc-action-icon-btn {
background: none;
border: none;
color: var(--fg);
opacity: 0.3;
cursor: pointer;
padding: 3px;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.1s;
}
.doc-action-icon-btn:hover {
opacity: 1;
}
.doc-action-icon-btn:disabled {
opacity: 0.18;
cursor: default;
pointer-events: none;
}
#doc-undo-btn:not(:disabled),
#doc-redo-btn:not(:disabled) {
opacity: 0.68;
}
.doc-fontsize-levels {
position: absolute;
right: 6px;
bottom: 3px;
display: inline-flex;
line-height: 1;
pointer-events: none;
}
.doc-fontsize-levels i {
font-style: normal;
font-weight: 700;
font-size: 7px;
/* Bare --accent is undefined in this codebase, was falling back to the
hardcoded blue #4a9eff — use the real theme accent. */
color: var(--accent-primary, var(--red));
opacity: 1;
}
.doc-fontsize-levels i.active { opacity: 1; }
/* Collapsed buttons hidden in header, shown in overflow menu */
.doc-collapsible-btn.doc-collapsed { display: none !important; }
.doc-overflow-toggle:hover { opacity: 1; }
.doc-overflow-menu {
display: none;
position: fixed;
z-index: 9999;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 4px;
padding: 4px 0;
min-width: 140px;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.doc-overflow-menu.open { display: block; }
.doc-overflow-item {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 5px 12px;
background: none;
border: none;
color: var(--fg);
font-family: inherit;
font-size: 11px;
cursor: pointer;
white-space: nowrap;
transition: background 0.1s;
}
.doc-overflow-item:hover,
.doc-overflow-item:focus-visible {
background: color-mix(in srgb, var(--accent-primary, var(--red)) 12%, transparent);
outline: none;
}
.doc-overflow-item .rich-align-preview {
display: inline-block;
white-space: nowrap;
}
.doc-overflow-item:disabled {
opacity: 0.28;
cursor: default;
}
.doc-overflow-item.is-current {
color: var(--accent-primary, var(--red));
background: color-mix(in srgb, var(--accent-primary, var(--red)) 9%, transparent);
}
.md-dd-current {
display: inline-flex;
align-items: center;
justify-content: center;
width: 14px;
margin-left: auto;
opacity: 0;
}
.doc-overflow-item.is-current .md-dd-current { opacity: 1; }
.doc-overflow-item.rich-table-danger { color: var(--color-danger, #c0392b); }
.doc-overflow-item svg { flex-shrink: 0; opacity: 0.5; }
.doc-ai-writing-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 2px;
color: var(--accent-primary, var(--red));
}
.doc-ai-writing-btn svg:first-child { color: var(--accent-primary, var(--red)); opacity: 1; }
.doc-ai-writing-whirlpool { position: relative; top: -4px; }
.doc-ai-writing-menu { min-width: 206px; }
.doc-ai-writing-menu-title {
display: flex;
align-items: center;
gap: 6px;
padding: 5px 12px 6px;
color: var(--accent-primary, var(--red));
font-size: 10px;
font-weight: 600;
border-bottom: 1px solid var(--border);
}
.doc-ai-writing-menu-title svg { opacity: 1; }
.doc-ai-writing-item-icon {
width: 16px;
flex: 0 0 16px;
text-align: center;
color: var(--accent-primary, var(--red));
font-size: 11px;
font-weight: 600;
}
/* Markdown Edit/Preview two-icon switch — segmented, styled to match the Copy
split button. The active half gets a static highlight (no sliding). */
.md-view-toggle {
display: inline-flex;
align-items: center;
border: 1px solid var(--border);
border-radius: 7px;
overflow: hidden;
flex-shrink: 0;
height: 22px;
}
.md-view-toggle .md-view-opt {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 4px;
min-width: 26px;
width: auto;
height: 100%;
padding: 0 7px !important;
border: none !important;
border-radius: 0 !important;
background: none !important;
color: var(--fg);
opacity: 0.45;
cursor: pointer;
transition: opacity 0.12s, color 0.12s, background 0.12s;
}
.md-view-label {
font-size: 10px;
font-weight: 600;
line-height: 1;
}
.md-view-toggle .md-view-opt:hover { opacity: 0.8; }
.md-view-toggle .md-view-opt.active {
opacity: 1;
color: var(--fg);
background: var(--bg) !important;
/* "Punch" pop when a side becomes active (only fires on an actual switch —
re-applying .active without a change doesn't restart the animation). */
animation: md-view-punch 0.28s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes md-view-punch {
0% { transform: scale(0.8); }
55% { transform: scale(1.18); }
100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
.md-view-toggle .md-view-opt.active { animation: none; }
}
.doc-md-toolbar.md-preview-active {
border-bottom-color: var(--bg);
}
.doc-md-toolbar.md-write-active {
border-bottom-color: var(--sidebar-bg, var(--panel));
}
.doc-md-toolbar.md-preview-active #doc-md-view-toggle,
.doc-md-toolbar.md-write-active #doc-md-view-toggle {
align-self: flex-end;
height: 28px;
margin-bottom: -6px;
border-radius: 7px 7px 0 0;
box-shadow: 0 -1px 0 var(--border), -1px 0 0 var(--border), 1px 0 0 var(--border);
}
.doc-md-toolbar.md-preview-active #doc-md-view-toggle {
background: var(--bg);
border-bottom-color: var(--bg);
}
.doc-md-toolbar.md-write-active #doc-md-view-toggle {
background: var(--sidebar-bg, var(--panel));
border-bottom-color: var(--sidebar-bg, var(--panel));
}
.doc-md-toolbar.md-preview-active #doc-md-view-toggle .md-view-opt.active {
background: var(--bg) !important;
}
.doc-md-toolbar.md-write-active #doc-md-view-toggle .md-view-opt.active {
background: var(--bg) !important;
}
.doc-md-preview.md-preview-active {
background: var(--bg);
border-top: 0;
margin-top: 0;
}
/* Write and Preview are separate tabs that sit out from the toolbar. */
.md-view-toggle {
position: relative;
gap: 2px;
height: 28px;
border: 0;
border-radius: 0;
overflow: visible;
background: var(--bg) !important;
box-shadow: none !important;
}
/* The toolbar owns the boundary beneath the editor. A second full-width
baseline here would remain visible directly under the active tab. */
.md-view-toggle::after { content: none; }
.md-view-toggle .md-view-opt {
height: 28px;
border: 1px solid var(--border) !important;
border-bottom-color: transparent !important;
border-radius: 7px 7px 0 0 !important;
position: relative;
z-index: 2;
}
.md-view-toggle .md-view-opt.active {
z-index: 3;
}
.md-view-toggle .md-view-opt:first-child,
#doc-render-view-toggle .md-view-opt:first-child {
background: var(--sidebar-bg, var(--panel)) !important;
}
.md-view-toggle .md-view-opt:last-child,
#doc-render-view-toggle .md-view-opt:last-child {
background: var(--sidebar-bg, var(--panel)) !important;
}
/* Keep code's Edit/Run switch in the same visual language as Write/Preview:
Edit is attached to the sidebar-colored editor surface, while Run/Preview
is attached to the lighter content surface. */
#doc-render-view-toggle .md-view-opt {
color: var(--fg);
border-color: var(--border) !important;
}
#doc-render-view-toggle .md-view-opt.active {
opacity: 1;
color: var(--fg);
}
/* Code/Run uses the same tab shape and baseline as Write/Preview. */
.doc-md-toolbar #doc-render-view-toggle {
align-self: flex-end;
height: 28px;
margin-bottom: -6px;
}
.doc-md-toolbar #doc-render-view-toggle .md-view-opt:first-child {
background: var(--sidebar-bg, var(--panel)) !important;
}
.doc-md-toolbar #doc-render-view-toggle .md-view-opt:last-child {
background: var(--bg) !important;
}
/* Mobile: the doc/email footer controls (format toggle, export button, export
menu) were slimmer than the Send button, so the row looked ragged. Bump them
to match the Send button height (and give the export/overflow menu bigger,
touch-friendly rows). */
@media (max-width: 768px) {
.md-view-toggle { height: 28px; }
.md-view-toggle .md-view-opt { min-width: 38px; padding: 0 8px !important; }
.doc-action-icon-btn { padding: 6px; }
.email-send-btn { height: 28px; }
/* The type/language picker was the slim one stuck at 22px — match it to the
rest of the row. */
.doc-language-select { height: 28px; font-size: 13px; padding: 2px 22px 2px 10px; }
.doc-overflow-item { font-size: 13px; padding: 9px 14px; }
.doc-overflow-item .overflow-icon svg,
.doc-overflow-item svg { width: 16px; height: 16px; }
.email-more-menu .dropdown-item-compact { padding: 9px 12px; font-size: 13px; }
/* The doc footer is tight once the run/preview toggle joins it, so on mobile
go icon-only for Close, Undo & Copy (their icons are clear).
font-size:0 drops the text node next to the SVG (the SVG has fixed w/h, so
it's unaffected). Slightly narrower type picker buys a little more room. */
#doc-actions-footer #doc-undo-btn span,
#doc-actions-footer #doc-footer-close-btn span,
#doc-email-actions #doc-email-discard-btn span { display: none; }
#doc-actions-footer #doc-undo-btn,
#doc-actions-footer #doc-redo-btn,
#doc-actions-footer #doc-footer-close-btn,
#doc-email-actions #doc-email-discard-btn { gap: 0; padding: 0 9px; }
#doc-actions-footer #doc-footer-copy-btn { gap: 0; padding: 0 13px; font-size: 0 !important; }
/* In reply mode the button carries a "Reply" label that should stay visible
(the icon-only treatment above is only for the plain Copy action). */
#doc-actions-footer #doc-footer-copy-btn[data-mode="reply"] { gap: 5px; padding: 0 13px; font-size: 12px; }
#doc-actions-footer #doc-language-select { width: 80px; }
#doc-actions-footer .doc-stats-btn { width: 28px; padding: 0 4px; }
#doc-actions-footer .doc-stats-unit { display: none; }
#doc-actions-footer .doc-stats-popover { right: -34px; }
#doc-actions-footer .doc-save-button-label { display: none; }
}
/* Very narrow document panes: keep the primary Save action visible while
collapsing secondary labels before allowing the footer to clip. This uses
the pane width, not the viewport width, because the editor can be docked. */
@container docpane (max-width: 420px) {
#doc-actions-footer {
gap: 4px;
padding-left: 8px;
padding-right: 8px;
overflow: hidden;
}
#doc-actions-footer > #doc-undo-btn,
#doc-actions-footer > #doc-redo-btn {
flex: 0 0 28px;
padding-left: 4px;
padding-right: 4px;
}
#doc-actions-footer > #doc-undo-btn span,
#doc-actions-footer > #doc-footer-copy-btn,
#doc-actions-footer .doc-save-button-label,
#doc-actions-footer > #doc-stats .doc-stats-unit {
font-size: 0;
}
#doc-actions-footer > #doc-undo-btn span,
#doc-actions-footer .doc-save-button-label,
#doc-actions-footer > #doc-stats .doc-stats-unit {
display: none;
}
#doc-actions-footer > #doc-stats {
flex: 0 0 28px;
}
#doc-actions-footer #doc-copy-export-split {
flex: 0 0 auto;
}
#doc-actions-footer #doc-footer-copy-btn {
flex: 0 0 36px;
width: 36px;
min-width: 36px;
padding-left: 0;
padding-right: 0;
font-size: 0 !important;
align-items: center;
justify-content: center;
}
#doc-actions-footer #doc-footer-export-btn {
flex: 0 0 24px;
width: 24px;
min-width: 24px;
padding-left: 4px;
padding-right: 4px;
}
#doc-actions-footer #doc-copy-export-split {
border: 1px solid color-mix(in srgb, var(--accent-primary, var(--red)) 50%, transparent);
border-radius: 6px;
overflow: hidden;
background: color-mix(in srgb, var(--accent-primary, var(--red)) 20%, transparent);
}
#doc-actions-footer #doc-copy-export-split .email-send-btn {
border-top: 0;
border-bottom: 0;
}
#doc-actions-footer #doc-copy-export-split #doc-footer-copy-btn {
border-left: 0;
}
#doc-actions-footer #doc-copy-export-split #doc-footer-export-btn {
border-right: 0;
}
#doc-actions-footer #doc-footer-export-btn svg {
width: 10px;
height: 10px;
}
}
/* Markdown formatting toolbar */
.doc-md-toolbar [data-md="unlink"] { display: none !important; }
/* In code-mode the toolbar only hosts the view toggles + utility buttons
(font-size, diff). Hide markdown-only formatting controls — bold,
italic, headings, list, link, attach, code-dropdown, emoji, hr, and
the PDF-only buttons. The view toggles + fontsize + diff stay. */
.doc-md-toolbar[data-mode="code"] [data-md],
.doc-md-toolbar[data-mode="code"] .md-dd-toggle,
.doc-md-toolbar[data-mode="code"] #md-toolbar-attach-btn,
.doc-md-toolbar[data-mode="code"] #md-toolbar-emoji-slot,
.doc-md-toolbar[data-mode="code"] .md-toolbar-pdf-only,
.doc-md-toolbar[data-mode="code"] .md-toolbar-sep {
display: none !important;
}
.doc-md-toolbar {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 1px;
padding: 2px 8px;
border-bottom: 1px solid var(--border);
background: var(--sidebar-bg, var(--panel));
flex-shrink: 0;
overflow: hidden;
height: 36px;
position: relative;
/* Same edge fade as the tab strip — toolbar buttons dissolve into the bar's
background at the edges instead of being hard-cut when they overflow. */
-webkit-mask-image: linear-gradient(to right, transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%);
mask-image: linear-gradient(to right, transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%);
}
.doc-md-toolbar button {
background: none;
border: none;
color: var(--fg);
opacity: 0.85;
padding: 4px 7px;
font-family: inherit;
font-size: 11px;
cursor: pointer;
white-space: nowrap;
line-height: 1.3;
transition: opacity 0.1s;
min-height: 28px;
display: flex;
align-items: center;
justify-content: center;
}
.doc-md-toolbar button:hover {
opacity: 1;
}
.doc-md-toolbar button:disabled {
opacity: 0.28;
cursor: default;
pointer-events: none;
}
.doc-link-remove-btn {
color: var(--red, #dc2626);
border-color: color-mix(in srgb, var(--red, #dc2626) 45%, var(--border));
}
.doc-md-toolbar button:active {
opacity: 0.6;
}
/* Active formatting state — set by the email WYSIWYG sync. Mirrors the
selection's current marks (B/I/S, heading level, list) so the toolbar
acts as an indicator, not just a launcher. */
.doc-md-toolbar button.is-active {
opacity: 1;
color: var(--accent, var(--red));
background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
}
.doc-md-toolbar button.is-active:hover {
background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
}
.doc-md-toolbar button:focus:not(:focus-visible),
.doc-tab-new:focus:not(:focus-visible),
.doc-tab-arrow:focus:not(:focus-visible) {
outline: none;
background: none;
}
.doc-md-toolbar button:focus-visible,
.doc-tab-new:focus-visible,
.doc-tab-arrow:focus-visible {
outline: 1px solid var(--accent-primary, var(--red));
outline-offset: -1px;
}
/* Grouped formatting dropdown toggles (heading / code / list) */
.md-dd-toggle { gap: 1px !important; }
/* Tint only dropdown chevrons accent; the main tool icon keeps toolbar color. */
.md-dd-toggle svg { opacity: 1; margin-left: 1px; flex-shrink: 0; transform: translateY(0); }
.md-dd-toggle > svg:last-child { color: var(--accent-primary, var(--red, #4a9eff)); }
.md-dd-ico {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
flex-shrink: 0;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 11px;
opacity: 0.7;
}
.md-toolbar-sep {
width: 1px;
height: 12px;
background: color-mix(in srgb, var(--border) 60%, transparent);
margin: 0 4px;
flex-shrink: 0;
}
.md-toolbar-items {
display: flex;
align-items: center;
gap: 1px;
flex: 1 1 auto;
min-width: 0;
overflow-x: auto;
scrollbar-width: none;
scroll-behavior: auto;
-webkit-overflow-scrolling: touch;
touch-action: pan-x pan-y;
}
.md-toolbar-leading-controls {
display: flex;
align-items: center;
flex: 0 0 auto;
}
.md-toolbar-items::-webkit-scrollbar { display: none; }
/* On a PDF, the annotation tools (text / checkmark / signature) are the primary
actions — pull them to the far left of the toolbar. `order` is visual only
(they're display:none on non-PDF docs), so it has no effect elsewhere. */
.md-toolbar-items #doc-pdf-add-text-btn { order: -3; }
.md-toolbar-items #doc-pdf-add-check-btn { order: -2; }
.md-toolbar-items #doc-pdf-add-sign-btn { order: -1; }
@media (min-width: 769px) {
.md-toolbar-items #doc-pdf-add-text-btn {
margin-left: 14px;
}
}
/* Stack the signature icon over a tiny "sign" caption so the tool reads
clearly (the icon alone is ambiguous). The JS toggles inline display
none/'' for PDF mode, so this flex display only applies when shown. */
#doc-pdf-add-sign-btn {
flex-direction: column;
align-items: center;
gap: 0;
line-height: 1;
}
.doc-pdf-sign-label {
font-size: 6.5px;
letter-spacing: 0.3px;
margin-top: 0;
opacity: 1;
color: var(--accent, var(--red));
}
/* Edge scroll arrows — appear when the toolbar has more icons off-screen. */
.md-scroll-arrow {
position: static;
flex: 0 0 28px;
align-self: stretch;
width: 28px;
display: flex;
align-items: center;
border: none;
cursor: pointer;
color: var(--fg);
padding: 0 !important;
min-height: 0 !important;
opacity: 1 !important;
z-index: 6;
}
.md-scroll-arrow svg { position: relative; top: 2px; opacity: 0.75; transition: opacity 0.1s; }
.md-scroll-arrow:hover svg { opacity: 1; }
@media (min-width: 769px) {
/* On desktop the arrow rides 2px lower than the toolbar baseline. */
.md-scroll-arrow svg { top: 0; }
}
.md-scroll-left {
justify-content: flex-start;
padding-left: 3px;
/* Mostly-solid toolbar bg so the icons behind the arrow are hidden, fading
to transparent only at the inner edge. */
background: linear-gradient(to right, var(--bg) 0%, var(--bg) 80%, transparent 100%);
}
.md-scroll-right {
justify-content: flex-end;
padding-right: 3px;
background: linear-gradient(to left, var(--bg) 0%, var(--bg) 80%, transparent 100%);
}
.md-toolbar-overflow-wrapper {
position: relative;
flex-shrink: 0;
margin-left: auto;
}
.md-toolbar-overflow-toggle {
background: none;
border: 1px solid transparent;
color: var(--fg);
opacity: 0.4;
padding: 2px 4px;
border-radius: 4px;
cursor: pointer;
transition: opacity 0.1s;
}
.md-toolbar-overflow-toggle:hover {
opacity: 1;
}
.md-toolbar-overflow-menu {
display: none;
position: fixed;
z-index: 1000;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
padding: 4px;
min-width: 0;
width: max-content;
box-shadow: 0 8px 24px rgba(0,0,0,0.3);
backdrop-filter: blur(12px);
}
.md-toolbar-overflow-menu.open {
display: flex;
flex-wrap: wrap;
gap: 2px;
max-width: 200px;
}
.md-toolbar-overflow-item {
background: none;
border: 1px solid transparent;
color: var(--fg);
opacity: 0.5;
padding: 4px 8px;
border-radius: 4px;
font-family: inherit;
font-size: 11px;
cursor: pointer;
white-space: nowrap;
line-height: 1.3;
transition: opacity 0.1s, background 0.1s;
}
.md-toolbar-overflow-item:hover {
opacity: 1;
background: color-mix(in srgb, var(--fg) 8%, transparent);
border-color: var(--border);
}
/* Find bar */
.doc-find-bar {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 4px;
padding: 4px 8px;
background: var(--panel, var(--bg));
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.doc-find-row,
.doc-replace-row {
display: flex;
align-items: center;
gap: 4px;
width: 100%;
}
.doc-replace-row[hidden] { display: none; }
.doc-find-input {
flex: 1;
min-width: 0;
padding: 3px 6px;
font-size: 12px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--bg);
color: var(--fg);
outline: none;
}
.doc-find-input:focus { border-color: var(--accent); }
.doc-find-count {
font-size: 11px;
opacity: 0.6;
white-space: nowrap;
min-width: 50px;
text-align: center;
}
.doc-find-nav, .doc-find-close {
display: inline-flex;
align-items: center;
justify-content: center;
background: none;
border: none;
color: var(--fg);
cursor: pointer;
padding: 2px 5px;
font-size: 14px;
border-radius: 3px;
opacity: 0.7;
}
.doc-find-nav:hover, .doc-find-close:hover,
.doc-find-nav.is-active { opacity: 1; background: color-mix(in srgb, var(--fg) 10%, transparent); }
.doc-find-action {
min-height: 25px;
padding: 3px 8px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--bg);
color: var(--fg);
font: inherit;
font-size: 11px;
cursor: pointer;
white-space: nowrap;
}
.doc-find-action:hover,
.doc-find-action:focus-visible {
border-color: var(--accent);
outline: none;
}
/* Live document outline. It is body-portaled so the toolbar's clipping and
edge mask cannot cut it off in the mobile full-screen editor. */
.doc-outline-menu {
position: fixed;
box-sizing: border-box;
max-height: min(420px, calc(100vh - 24px));
overflow: hidden;
display: flex;
flex-direction: column;
background: var(--panel, var(--bg));
border: 1px solid var(--border);
border-radius: 6px;
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.3);
color: var(--fg);
}
.doc-outline-header {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 32px;
padding: 0 10px;
border-bottom: 1px solid var(--border);
font-size: 11px;
font-weight: 600;
}
.doc-outline-list {
overflow-y: auto;
padding: 4px;
overscroll-behavior: contain;
}
.doc-outline-item {
width: 100%;
min-height: 30px;
display: grid;
grid-template-columns: 24px minmax(0, 1fr);
align-items: center;
gap: 4px;
padding: 4px 8px 4px 7px;
border: 0;
border-radius: 4px;
background: transparent;
color: var(--fg);
font: inherit;
font-size: 11px;
text-align: left;
cursor: pointer;
}
.doc-outline-item:hover,
.doc-outline-item:focus-visible {
background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
outline: none;
}
.doc-outline-level {
opacity: 0.42;
font-size: 9px;
font-variant-numeric: tabular-nums;
}
.doc-outline-label {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.doc-outline-empty {
padding: 18px 12px;
text-align: center;
font-size: 11px;
opacity: 0.52;
}
@media (max-width: 768px) {
.doc-outline-menu { max-height: min(52vh, 420px); }
.doc-outline-item { min-height: 38px; font-size: 12px; }
}
/* Selection formatting stays close to the selected text. The controls are
symbols only and mirror the same commands as the full Rich Text toolbar. */
.doc-rich-selection-toolbar {
position: fixed;
display: flex;
align-items: center;
gap: 1px;
padding: 3px;
background: var(--panel, var(--bg));
border: 1px solid var(--border);
border-radius: 6px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.32);
color: var(--fg);
user-select: none;
overflow: visible;
}
.doc-rich-selection-toolbar button {
width: 30px;
height: 28px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
border: 0;
border-radius: 4px;
background: transparent;
color: inherit;
font: inherit;
font-size: 12px;
cursor: pointer;
touch-action: manipulation;
}
.doc-rich-selection-toolbar button:nth-child(5) {
margin-left: 3px;
border-left: 1px solid var(--border);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.doc-rich-selection-toolbar button:hover,
.doc-rich-selection-toolbar button:focus-visible,
.doc-rich-selection-toolbar button.is-active {
color: var(--accent, var(--red));
background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
outline: none;
}
.doc-rich-selection-toolbar .doc-rich-selection-close {
position: absolute;
top: -9px;
right: -9px;
z-index: 2;
width: 18px;
height: 18px;
border: 1px solid var(--border);
border-radius: 50%;
background: var(--panel, var(--bg));
color: var(--fg);
font: 700 15px/1 system-ui, sans-serif;
box-shadow: 0 2px 7px rgba(0, 0, 0, 0.35);
}
.doc-rich-selection-toolbar .doc-rich-selection-close:hover,
.doc-rich-selection-toolbar .doc-rich-selection-close:focus-visible {
color: var(--accent, var(--red));
background: color-mix(in srgb, var(--accent, var(--red)) 16%, var(--panel, var(--bg)));
outline: none;
}
@media (max-width: 768px) {
.doc-rich-selection-toolbar { padding: 4px; }
.doc-rich-selection-toolbar button { width: 38px; height: 34px; font-size: 13px; }
.doc-rich-selection-toolbar .doc-rich-selection-close { width: 20px; height: 20px; top: -10px; right: -10px; }
}
.doc-rich-slash-menu {
position: fixed;
box-sizing: border-box;
display: flex;
flex-direction: column;
max-height: min(420px, calc(100vh - 24px));
overflow: hidden;
background: var(--panel, var(--bg));
border: 1px solid var(--border);
border-radius: 6px;
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.32);
color: var(--fg);
}
.doc-rich-slash-header {
min-height: 32px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10px;
border-bottom: 1px solid var(--border);
font-size: 11px;
font-weight: 600;
}
.doc-rich-slash-list {
overflow-y: auto;
overscroll-behavior: contain;
padding: 4px;
}
.doc-rich-slash-item {
width: 100%;
min-height: 34px;
display: grid;
grid-template-columns: 30px minmax(0, 1fr);
align-items: center;
gap: 5px;
padding: 3px 8px;
border: 0;
border-radius: 4px;
background: transparent;
color: var(--fg);
font: inherit;
font-size: 11px;
text-align: left;
cursor: pointer;
}
.doc-rich-slash-item:hover,
.doc-rich-slash-item:focus-visible,
.doc-rich-slash-item.is-active {
background: color-mix(in srgb, var(--accent, var(--red)) 13%, transparent);
outline: none;
}
.doc-rich-slash-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 26px;
height: 24px;
color: var(--accent, var(--red));
font-size: 10px;
font-weight: 600;
opacity: 0.82;
}
.doc-rich-slash-empty {
padding: 18px 12px;
text-align: center;
font-size: 11px;
opacity: 0.52;
}
@media (max-width: 768px) {
.doc-rich-slash-menu { max-height: min(54vh, 420px); }
.doc-rich-slash-item { min-height: 42px; font-size: 12px; }
}
/* Editor — highlighted overlay + transparent textarea.
Lock the editor wrap to a fixed text-column width, centered horizontally
in the doc pane. This eliminates wrap-related drift bugs as a class:
the textarea + overlay always have the same available width regardless
of viewport, sidebar state, dock state, or window resize. Whitespace on
either side reads like a writing-app column (Bear / iA Writer pattern).
container-type: inline-size lets us hide the line-number gutter via a
container query when the editor's own width is narrow (rather than
viewport-width, which would miss the case of a narrow side-docked
editor inside a wide window). */
.doc-editor-wrap {
position: relative;
flex: 1;
min-height: 0;
/* Cap the column at ~820px outer (~760px content area after the 60px
of horizontal padding the textarea+overlay use for the gutter). */
max-width: 820px;
width: 100%;
margin-left: auto;
margin-right: auto;
overflow: hidden;
background: var(--bg);
container-type: inline-size;
container-name: doceditor;
}
/* When the editor itself is narrow, soft-wrap makes one logical line
span multiple visual rows but the gutter still shows ONE number per
logical line — so the gutter and the visible rows fall out of sync.
Hide the gutter (and reclaim the 48px of left padding it reserved)
below a threshold where the mismatch reads as a glitch. */
@container doceditor (max-width: 360px) {
.doc-line-numbers { display: none !important; }
.doc-editor-textarea,
.doc-editor-highlight {
padding-left: 12px !important;
}
}
.doc-line-numbers {
position: absolute;
top: 0; left: 0; bottom: 0;
width: 36px;
padding: 10px 8px 10px 0;
margin: 0;
font-family: inherit;
font-size: 11px;
line-height: 1.45;
text-align: right;
color: var(--fg);
opacity: 0.18;
background: var(--bg);
overflow: hidden;
white-space: pre;
tab-size: 4;
font-variant-ligatures: none !important;
font-feature-settings: "kern" 0, "liga" 0, "calt" 0, "dlig" 0 !important;
font-kerning: none !important;
text-rendering: geometricPrecision !important;
z-index: 2;
pointer-events: none;
user-select: none;
}
.doc-line-number-content {
display: block;
will-change: transform;
}
.doc-line-number-row {
position: relative;
box-sizing: border-box;
}
.doc-line-number-label {
position: absolute;
top: 0;
left: 0;
width: 36px;
text-align: right;
}
.doc-line-number-measure {
position: absolute !important;
visibility: hidden !important;
pointer-events: none !important;
left: -9999px !important;
top: 0 !important;
height: 0 !important;
min-height: 0 !important;
max-height: none !important;
overflow: hidden !important;
padding: 0 !important;
border: 0 !important;
resize: none !important;
box-sizing: content-box !important;
color: transparent !important;
background: transparent !important;
}
mark.doc-find-mark.current {
background: var(--accent) !important;
color: var(--bg) !important;
box-shadow: 0 0 0 2px var(--fg);
outline: 1px solid var(--fg);
}
/* Find-match overlay rects — drawn on top of the textarea, work in
every doc mode (markdown, email, plain) regardless of whether the
syntax-highlight overlay is shown. Translucent band so the underlying
text stays readable; current match gets a brighter solid band. */
.doc-find-rect {
background: color-mix(in srgb, var(--accent) 25%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 65%, transparent);
}
.doc-find-rect.current {
background: color-mix(in srgb, var(--accent) 55%, transparent);
box-shadow: inset 0 0 0 2px var(--accent);
}
.doc-editor-highlight {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
margin: 0;
padding: 10px 12px 10px 48px;
font-family: inherit;
font-size: 11px !important;
line-height: 1.45 !important;
tab-size: 4;
white-space: pre-wrap;
word-wrap: break-word;
overflow: hidden;
/* `scrollbar-gutter: stable` reserves scrollbar-track width in the
layout even when no scrollbar is shown. The textarea below has the
same setting — without this, the textarea would consume scrollbar
space the moment its content overflows vertically, shrinking its
content width and wrapping lines earlier than the overlay. The
visible-text-drift bug user reports as "after ~16 rows it wraps
even though there's space" was caused by exactly that. */
scrollbar-gutter: stable;
scrollbar-width: none;
pointer-events: none;
z-index: 0;
background: var(--bg) !important;
color: var(--hl-fg, var(--fg));
border: none;
/* Disable ligatures + kerning everywhere in the editor. Monospace fonts
like Fira Code form ligatures for `=>`, `!=`, `->`, `==` etc., but
hljs splits those pairs into separate <span>s in the overlay, which
breaks the ligature on this side while the textarea still forms it.
Result: visible row drift whenever code contains those pairs. Pin
ligatures off in both layers (textarea below) so widths stay equal. */
font-variant-ligatures: none !important;
font-feature-settings: "kern" 0, "liga" 0, "calt" 0, "dlig" 0 !important;
font-kerning: none !important;
text-rendering: geometricPrecision !important;
box-sizing: border-box;
}
/* Document font size options */
.doc-font-m .doc-editor-textarea,
.doc-font-m .doc-editor-highlight,
.doc-font-m .doc-line-numbers { font-size: 13px !important; }
.doc-font-l .doc-editor-textarea,
.doc-font-l .doc-editor-highlight,
.doc-font-l .doc-line-numbers { font-size: 15px !important; }
.doc-email-richbody.doc-font-m { font-size: 15px !important; }
.doc-email-richbody.doc-font-l { font-size: 17px !important; }
/* Markdown base text should match chat text color, not code color */
.doc-editor-highlight .language-markdown {
color: var(--fg) !important;
}
.doc-editor-highlight code,
.doc-editor-highlight code.hljs,
.doc-editor-highlight .hljs {
font-family: inherit;
font-size: inherit !important;
line-height: inherit !important;
background: transparent !important;
padding: 0 !important;
margin: 0 !important;
border-radius: 0 !important;
overflow: hidden !important;
display: block;
pointer-events: none;
}
.doc-editor-textarea {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
width: 100%;
height: 100% !important;
max-height: none !important;
min-height: 0 !important;
z-index: 1;
background: var(--bg) !important;
color: transparent !important;
color-scheme: dark;
caret-color: var(--fg);
border: none;
outline: none;
resize: none;
font-family: inherit;
/* Caret position only matches the underlying highlight if BOTH layers use
identical metrics — !important on font-size + line-height defends against
anything else in the cascade nudging the textarea but not the overlay. */
font-size: 11px !important;
line-height: 1.45 !important;
padding: 10px 12px 10px 48px;
overflow-y: scroll;
/* Pair with .doc-editor-highlight's scrollbar-gutter: stable so the
textarea's content width DOESN'T shrink the moment its scrollbar
appears (overflow-y: scroll keeps scrollbar permanent, gutter
reserves the space layout-wise). Without this, line wrap diverges
between textarea and overlay whenever content exceeds the visible
area — caret stays right, but typed text appears on a different row
than the caret. */
scrollbar-gutter: stable;
/* Show a real scrollbar for long documents. scrollbar-gutter above keeps
the text column stable so the gutter, textarea, and find overlay stay
metrically aligned while the scrollbar is present. */
scrollbar-width: thin;
scrollbar-color: color-mix(in srgb, var(--fg) 28%, transparent) transparent;
-webkit-overflow-scrolling: touch;
tab-size: 4;
white-space: pre-wrap;
word-wrap: break-word;
-moz-appearance: none;
appearance: none;
box-sizing: border-box;
/* Mirror the ligature/kerning lockdown from .doc-editor-highlight above.
Without this the textarea forms `=>`/`!=`/`->`/`==` as single-glyph
ligatures while the overlay can't (hljs splits the pair into
separate spans), so glyph widths diverge and the visible text drifts
down relative to the caret as code accumulates. */
font-variant-ligatures: none !important;
font-feature-settings: "kern" 0, "liga" 0, "calt" 0, "dlig" 0 !important;
font-kerning: none !important;
text-rendering: geometricPrecision !important;
}
.doc-editor-textarea::-webkit-scrollbar { width: 8px; }
.doc-editor-textarea::-webkit-scrollbar-track { background: transparent; }
.doc-editor-textarea::-webkit-scrollbar-thumb {
background: color-mix(in srgb, var(--fg) 24%, transparent);
border-radius: 999px;
}
.doc-editor-textarea::-webkit-scrollbar-thumb:hover {
background: color-mix(in srgb, var(--fg) 36%, transparent);
}
.doc-editor-textarea:hover,
.doc-editor-textarea:focus,
.doc-editor-textarea:active {
background: var(--bg) !important;
/* Used to force `color: transparent` here so the hidden two-layer
textarea wouldn't bleed through on hover/focus. Now that the
textarea renders its OWN visible text (overlay is hidden), forcing
transparent here makes typed text disappear the moment the cursor
enters the page. Keep the visible fg color instead. */
color: var(--fg) !important;
outline: none !important;
}
.doc-editor-textarea::placeholder {
color: var(--fg);
opacity: 0.25;
}
/* Show real text when selecting so copy/paste is visible */
.doc-editor-textarea::selection {
color: var(--fg);
background: color-mix(in srgb, var(--accent, var(--red)) 48%, transparent);
}
/* ---- Selection indicator badge ---- */
.doc-selection-badge {
font-size: 10px;
color: var(--red);
background: color-mix(in srgb, var(--red) 12%, transparent);
border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
border-radius: 4px;
padding: 1px 4px 1px 6px;
white-space: nowrap;
display: flex;
align-items: center;
gap: 4px;
height: 32px;
box-sizing: border-box;
}
.doc-selection-clear {
display: inline-flex;
align-items: center;
justify-content: center;
background: none;
border: 1px solid transparent;
border-radius: 999px;
color: var(--fg);
opacity: 0.4;
cursor: pointer;
font-size: 16px;
line-height: 1;
width: 20px;
height: 20px;
padding: 0;
box-sizing: border-box;
transition: opacity 0.12s, color 0.12s, background 0.12s, border-color 0.12s;
}
.doc-selection-clear:hover {
opacity: 1;
color: var(--fg);
background: color-mix(in srgb, var(--fg) 10%, transparent);
border-color: color-mix(in srgb, var(--fg) 12%, transparent);
}
.doc-selection-chip-clear {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
margin-left: 3px;
padding: 0;
border: 1px solid var(--border);
border-radius: 50%;
background: var(--panel, var(--bg));
color: var(--fg);
font: 700 15px/1 system-ui, sans-serif;
box-shadow: 0 2px 7px rgba(0, 0, 0, 0.35);
cursor: pointer;
}
.doc-selection-chip-clear:hover,
.doc-selection-chip-clear:focus-visible {
color: var(--accent, var(--red));
background: color-mix(in srgb, var(--accent, var(--red)) 16%, var(--panel, var(--bg)));
outline: none;
}
.doc-selection-clear {
width: auto;
height: 18px;
margin-left: 3px;
padding: 0 7px;
border-radius: 999px;
font: 700 11px/1 system-ui, sans-serif;
opacity: 1;
}
.doc-edit-tag {
display: inline-flex;
align-items: center;
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 32%, transparent);
color: var(--accent, var(--red));
cursor: pointer;
font-family: inherit;
font-size: 0.68em;
line-height: 1;
opacity: 0.78;
background: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent);
border-radius: 999px;
padding: 1px 5px;
margin-right: 2px;
white-space: nowrap;
transition: opacity 0.12s, background 0.12s, border-color 0.12s;
}
.doc-edit-tag:hover,
.doc-edit-tag:focus-visible {
opacity: 1;
background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
border-color: color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
}
.doc-edit-tag:focus-visible {
outline: 2px solid color-mix(in srgb, var(--accent, var(--red)) 45%, transparent);
outline-offset: 2px;
}
/* Attachment cards in user messages */
.attach-cards {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
}
.attach-card {
display: flex;
align-items: center;
gap: 6px;
padding: 5px 10px;
border-radius: 6px;
background: color-mix(in srgb, var(--fg) 6%, transparent);
/* Same border as the chat bubbles. */
border: 1px solid var(--bubble-border, var(--border));
font-size: 12px;
transition: background 0.15s;
}
.attach-card[style*="cursor: pointer"]:hover,
.attach-card[data-file-id]:hover {
background: color-mix(in srgb, var(--fg) 12%, transparent);
}
.attach-card-icon {
flex-shrink: 0;
opacity: 0.6;
}
.attach-card-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 200px;
}
.attach-card-size {
opacity: 0.45;
font-size: 11px;
white-space: nowrap;
}
.history-attach-card {
cursor: default;
opacity: 0.82;
}
.history-attach-card .attach-card-size {
text-transform: lowercase;
}
/* Import prompt banner (above chatbar) */
.import-prompt-banner {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
margin: 0 auto 6px;
max-width: 800px;
background: var(--panel);
border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
border-left: 3px solid var(--accent);
border-radius: 6px;
font-size: 12px;
color: var(--fg);
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
backdrop-filter: blur(12px);
animation: modal-enter 0.15s ease-out;
}
.import-prompt-banner span { flex: 1; }
.import-prompt-banner button {
padding: 3px 12px;
border: 1px solid var(--border);
border-radius: 5px;
background: none;
color: var(--fg);
cursor: pointer;
font-size: 12px;
white-space: nowrap;
}
.import-prompt-banner button:hover {
border-color: var(--fg);
}
.import-prompt-dismiss {
border: none !important;
background: none !important;
opacity: 0.5;
font-size: 16px !important;
padding: 0 4px !important;
}
.import-prompt-dismiss:hover { opacity: 1; background: none !important; }
.doc-selection-overlay {
position: absolute;
background: color-mix(in srgb, var(--red) 10%, transparent);
border-left: 2px solid color-mix(in srgb, var(--red) 50%, transparent);
pointer-events: none;
z-index: 2;
transition: top 0.05s;
}
.doc-selection-overlay-clear {
position: absolute;
top: 1px;
right: 3px;
width: 18px;
height: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
border: 0;
border-radius: 50%;
background: color-mix(in srgb, var(--panel) 82%, transparent);
color: var(--fg);
font: 16px/1 inherit;
opacity: 0.7;
cursor: pointer;
pointer-events: auto;
}
.doc-selection-overlay-clear:hover,
.doc-selection-overlay-clear:focus-visible {
opacity: 1;
background: color-mix(in srgb, var(--accent, var(--red)) 18%, var(--panel));
outline: none;
}
.doc-selection-rich-clear {
position: fixed;
z-index: 10040;
width: 18px;
height: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
border: 1px solid var(--border);
border-radius: 50%;
background: var(--panel, var(--bg));
color: var(--fg);
font: 700 15px/1 system-ui, sans-serif;
opacity: 1;
cursor: pointer;
box-shadow: 0 2px 7px rgba(0, 0, 0, 0.35);
}
.doc-selection-rich-clear:hover,
.doc-selection-rich-clear:focus-visible {
color: var(--accent, var(--red));
background: color-mix(in srgb, var(--accent, var(--red)) 16%, var(--panel, var(--bg)));
outline: none;
}
/* ── Suggestion comments (Google Docs style) ── */
.doc-suggestion-highlight {
position: absolute;
box-sizing: border-box;
background: color-mix(in srgb, var(--accent) 32%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 72%, transparent);
border-radius: 2px;
pointer-events: none;
z-index: 3;
transition: top 0.1s, opacity 0.15s;
}
/* Suggestion card — fixed next to editor, anchored to the change */
.doc-suggestion-card {
position: fixed;
width: 250px;
background: var(--panel);
border: 1px solid var(--accent);
border-radius: 10px;
padding: 12px 12px 10px;
font-size: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.25);
animation: suggestion-enter 0.25s ease-out;
z-index: 250;
overflow: visible;
}
.doc-suggestion-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.doc-suggestion-nav {
display: flex;
align-items: center;
gap: 4px;
}
.doc-suggestion-nav-btn {
background: none;
border: none;
color: var(--fg);
cursor: pointer;
font-size: 16px;
line-height: 1;
padding: 2px 4px;
opacity: 0.35;
transition: opacity 0.1s;
}
.doc-suggestion-nav-btn:hover { opacity: 1; }
.doc-suggestion-close {
width: 24px;
height: 24px;
padding: 0;
margin: -2px -2px -2px 0;
display: inline-flex;
align-items: center;
justify-content: center;
background: transparent;
border: 1px solid transparent;
border-radius: 999px;
color: color-mix(in srgb, var(--fg) 58%, transparent);
opacity: 1;
cursor: pointer;
font-size: 0;
line-height: 1;
flex-shrink: 0;
position: relative;
}
.doc-suggestion-close:hover,
.doc-suggestion-close:focus-visible {
background: color-mix(in srgb, var(--fg) 10%, transparent);
border-color: color-mix(in srgb, var(--fg) 12%, transparent);
color: var(--fg);
outline: none;
}
.doc-suggestion-counter {
font-size: 10px;
opacity: 0.4;
font-weight: 600;
letter-spacing: 0.5px;
}
/* Inline diff markers — injected into the code highlight element */
.sugg-inline-del {
background: color-mix(in srgb, var(--red) 20%, transparent);
color: color-mix(in srgb, var(--red) 70%, var(--fg));
text-decoration: line-through;
text-decoration-thickness: 1px;
text-decoration-color: color-mix(in srgb, var(--red) 40%, transparent);
border-radius: 2px;
padding: 0 2px;
}
.sugg-inline-add {
background: color-mix(in srgb, var(--green) 25%, transparent);
color: var(--green);
border-radius: 2px;
padding: 0 2px;
}
/* ---- Diff mode ---- */
.diff-toolbar {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 12px;
background: color-mix(in srgb, var(--fg) 4%, var(--bg));
border-bottom: 1px solid var(--border);
font-size: 11px;
flex-shrink: 0;
}
.diff-toolbar-status {
opacity: 0.5;
font-size: 10px;
margin-right: auto;
}
.diff-toolbar-btn {
background: none;
border: 1px solid var(--border);
color: var(--fg);
font-size: 10px;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
transition: border-color 0.15s, background 0.15s;
}
.diff-toolbar-btn:hover {
border-color: var(--accent, var(--red));
background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent);
}
.diff-toolbar-btn-accept { color: #58a6ff; border-color: color-mix(in srgb, #58a6ff 36%, transparent); }
.diff-toolbar-btn-accept:hover { border-color: #58a6ff; background: color-mix(in srgb, #58a6ff 12%, transparent); }
.diff-toolbar-btn-reject { color: #f2cc60; border-color: color-mix(in srgb, #f2cc60 36%, transparent); }
.diff-toolbar-btn-reject:hover { border-color: #f2cc60; background: color-mix(in srgb, #f2cc60 12%, transparent); }
.diff-line-del {
display: block;
background: color-mix(in srgb, #f2cc60 24%, transparent);
border-left: 3px solid #f2cc60;
color: #f2cc60;
padding-left: 4px;
margin-left: -7px;
text-decoration: line-through;
}
.diff-line-add {
display: block;
background: color-mix(in srgb, #58a6ff 24%, transparent);
border-left: 3px solid #58a6ff;
color: #58a6ff;
padding-left: 4px;
margin-left: -7px;
}
/* Inline diff summary (version history cards) */
.diff-del {
color: #f2cc60;
background: color-mix(in srgb, #f2cc60 16%, transparent);
text-decoration: line-through;
border-radius: 2px;
padding: 0 2px;
}
.diff-add {
color: #58a6ff;
background: color-mix(in srgb, #58a6ff 16%, transparent);
font-weight: 600;
border-radius: 2px;
padding: 0 2px;
}
.diff-line-equal {
display: block;
}
.diff-chunk-resolved {
opacity: 0.3;
transition: opacity 0.3s;
}
.diff-chunk-actions {
position: absolute;
right: 8px;
top: 0;
display: flex;
gap: 4px;
z-index: 5;
pointer-events: auto;
}
/* Diff mode: textarea sits on top of the highlight where chunk buttons live.
Disable its pointer events so clicks reach the buttons in the layer below. */
.doc-editor-wrap.diff-mode .doc-editor-textarea {
pointer-events: none;
color: transparent !important;
caret-color: transparent !important;
}
.doc-editor-wrap.diff-mode .doc-editor-highlight {
display: block !important;
pointer-events: auto;
z-index: 3;
color: var(--fg) !important;
background: var(--sidebar-bg, var(--panel)) !important;
overflow: hidden;
}
.doc-editor-wrap.diff-mode .doc-editor-highlight * {
color: inherit;
}
.doc-editor-wrap.diff-mode .diff-line-del {
color: #f2cc60 !important;
}
.doc-editor-wrap.diff-mode .diff-line-add {
color: #58a6ff !important;
}
.diff-chunk-btn {
width: 20px;
height: 20px;
border-radius: 4px;
border: 1px solid var(--border);
background: var(--bg);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
line-height: 1;
transition: border-color 0.15s, background 0.15s;
opacity: 0.6;
}
.diff-chunk-btn:hover { opacity: 1; }
.diff-chunk-btn-accept { color: #58a6ff; }
.diff-chunk-btn-accept:hover { border-color: #58a6ff; background: color-mix(in srgb, #58a6ff 15%, transparent); }
.diff-chunk-btn-reject { color: #f2cc60; }
.diff-chunk-btn-reject:hover { border-color: #f2cc60; background: color-mix(in srgb, #f2cc60 15%, transparent); }
.doc-suggestion-accept-all {
flex: 1.3;
min-width: 0;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 4px;
padding: 5px 8px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 11px;
font-family: inherit;
white-space: nowrap;
background: var(--accent);
border: 1px solid var(--accent);
color: var(--bg);
transition: background 0.1s;
}
.doc-suggestion-accept-all:hover {
background: color-mix(in srgb, var(--accent) 85%, var(--bg));
}
.doc-suggestion-reason {
opacity: 0.6;
margin-bottom: 6px;
font-size: 11px;
line-height: 1.4;
}
.doc-suggestion-diff {
background: var(--bg);
border-radius: 4px;
padding: 6px 8px;
font-family: var(--code-font, monospace);
font-size: 11px;
margin-bottom: 8px;
max-height: 100px;
overflow-y: auto;
word-break: break-word;
}
.doc-suggestion-del {
color: var(--red);
text-decoration: line-through;
opacity: 0.7;
}
.doc-suggestion-add {
color: var(--green);
}
.doc-suggestion-actions {
display: flex;
gap: 6px;
}
.doc-suggestion-accept,
.doc-suggestion-dismiss {
flex: 1;
padding: 5px 8px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 11px;
font-family: inherit;
font-weight: 500;
transition: background 0.1s;
}
.doc-suggestion-accept {
background: var(--accent, var(--red));
color: #fff;
}
.doc-suggestion-dismiss {
background: transparent;
border: 1px solid var(--border);
color: var(--fg);
}
.doc-suggestion-dismiss:hover {
background: color-mix(in srgb, var(--fg) 8%, transparent);
}
@keyframes suggestion-enter {
from { opacity: 0; transform: translateX(10px); }
to { opacity: 1; transform: translateX(0); }
}
/* Mobile: suggestion card overlays top of editor (no room on side) */
@media (max-width: 768px) {
.doc-suggestion-card {
box-sizing: border-box;
left: 8px;
right: 8px;
width: auto;
overflow-y: auto;
}
.doc-suggestion-card::before { display: none; }
}
/* ---- Streaming animation ---- */
.doc-editor-textarea[readonly] {
caret-color: var(--red);
}
/* Diff overlay */
.doc-diff-overlay {
position: absolute;
inset: 0;
background: var(--bg);
z-index: 5;
overflow-y: auto;
padding: 8px 12px;
font-family: 'Fira Code', monospace;
font-size: 0.85rem;
line-height: 1.5;
opacity: 0;
transition: opacity 0.3s ease;
border-radius: 6px;
}
.doc-diff-overlay.visible { opacity: 1; }
.doc-diff-overlay.fading { opacity: 0; transition: opacity 0.4s ease; }
.doc-diff-stats {
display: flex;
gap: 10px;
padding: 4px 0 8px;
font-size: 0.8rem;
font-weight: 600;
}
.diff-stat-del { color: var(--warn); }
.diff-stat-add { color: var(--green); }
.doc-diff-line {
padding: 1px 8px;
border-radius: 4px;
white-space: pre-wrap;
word-break: break-all;
}
.doc-diff-line.same {
opacity: 0.4;
}
.doc-diff-line.del {
background: color-mix(in srgb, var(--warn) 15%, transparent);
color: var(--warn);
text-decoration: line-through;
text-decoration-color: color-mix(in srgb, var(--warn) 40%, transparent);
}
.doc-diff-line.add {
background: color-mix(in srgb, var(--green) 12%, transparent);
color: var(--green);
}
.doc-diff-sep {
text-align: center;
padding: 2px 0;
font-size: 0.7rem;
opacity: 0.3;
color: var(--fg);
}
/* Version history panel (slide-out) */
.doc-version-panel {
position: fixed;
top: 0;
bottom: 0;
left: 0;
width: 300px;
background: var(--bg);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
z-index: 200;
box-shadow: 4px 0 16px rgba(0,0,0,0.35);
animation: version-slide-in 0.2s ease-out;
/* Match the app modal aesthetic so nothing inherits the large body font. */
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 12px;
letter-spacing: -0.01em;
}
@keyframes version-slide-in {
from { opacity: 0; transform: translateX(-30px); }
to { opacity: 1; transform: translateX(0); }
}
.doc-version-panel.hidden {
display: none;
}
@media (max-width: 768px) {
.doc-version-panel {
left: 0;
right: 0;
top: auto;
bottom: 0;
width: 100%;
height: 50vh;
border-right: none;
border-left: none;
border-top: 1px solid var(--border);
border-radius: 14px 14px 0 0;
box-shadow: 0 -4px 16px rgba(0,0,0,0.3);
/* It's a bottom sheet on mobile — slide UP from the bottom, not in from
the left (the desktop animation looked like a black box janking in). */
animation: version-slide-up 0.2s ease-out;
}
}
@keyframes version-slide-up {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
.doc-version-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 12px;
border-bottom: 1px solid var(--border);
font-size: 12px;
font-weight: 600;
color: var(--fg);
}
.doc-version-list {
flex: 1;
overflow-y: auto;
padding: 8px;
}
.doc-version-item {
padding: 8px;
border: 1px solid var(--border);
border-radius: 6px;
margin-bottom: 6px;
cursor: pointer;
transition: all 0.15s;
background: color-mix(in srgb, var(--fg) 3%, transparent);
}
.doc-version-item:hover {
background: color-mix(in srgb, var(--fg) 6%, transparent);
border-color: var(--fg);
}
.doc-version-info {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.doc-version-num {
font-weight: 600;
font-size: 12px;
color: var(--fg);
}
.doc-version-source {
font-size: 10px;
color: var(--fg);
opacity: 0.5;
background: color-mix(in srgb, var(--fg) 6%, transparent);
padding: 1px 6px;
border-radius: 4px;
}
.doc-version-time {
font-size: 10px;
color: var(--fg);
opacity: 0.4;
margin-left: auto;
}
.doc-version-summary {
font-size: 11px;
color: var(--fg);
opacity: 0.5;
margin-bottom: 4px;
}
.doc-version-restore {
background: none;
border: 1px solid var(--border);
color: var(--fg);
font-size: 10px;
padding: 2px 8px;
border-radius: 4px;
cursor: pointer;
transition: all 0.15s;
}
.doc-version-restore:hover {
background: color-mix(in srgb, var(--fg) 6%, transparent);
border-color: var(--fg);
}
/* "latest" badge */
.doc-version-latest {
font-size: 10px;
font-weight: 600;
color: var(--accent, var(--red));
background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
padding: 1px 6px;
border-radius: 4px;
}
/* Diff preview lines — small and muted so they don't dominate the item. */
.doc-version-diff {
font-size: 10px;
line-height: 1.5;
opacity: 0.8;
margin-top: 2px;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
word-break: break-word;
}
.doc-version-diff .diff-del {
color: #f2cc60;
background: color-mix(in srgb, #f2cc60 16%, transparent);
opacity: 1;
}
.doc-version-diff .diff-add {
color: #58a6ff;
background: color-mix(in srgb, #58a6ff 16%, transparent);
}
/* Mobile: doc editor takes over full screen as toggle */
@media (max-width: 768px) {
body.doc-view .doc-editor-pane {
/* Force its own full-screen window on mobile. !important on these so an
inline width/position left over from desktop drag-resize, or the base
desktop split layout (flex: 1; max-width: 70vw; border-left), can never
render it as a narrow side pane ("sidebar") on a phone. */
position: fixed !important;
inset: 0 !important;
top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important;
max-width: 100% !important;
width: 100% !important;
flex: none !important;
z-index: 170;
/* Stroke the top edge so the rounded corners read as a curved sheet edge. */
border: 1px solid var(--border);
border-bottom: none;
/* Rounded top corners like the other mobile sheet windows. */
border-radius: 14px 14px 0 0;
/* Slide up from the bottom (sheet), not in from the side, on mobile. */
animation: sheet-enter 0.25s cubic-bezier(0.2, 0.8, 0.2, 1) both;
transform-origin: bottom center;
}
/* A document that was already open before refresh should be restored in
place, not replay the mobile sheet entrance and visibly jump upward. */
body.doc-view .doc-editor-pane.doc-pane-restoring {
animation: none;
transform: none;
}
body.doc-view .doc-divider {
display: none;
}
/* Hide chat behind doc panel on mobile */
body.doc-view .chat-container {
display: none;
}
/* Doc + email windows alternate: whichever was opened last is in front.
Default (a doc was opened last) → email windows sit BELOW the full-screen
doc pane (170) so the draft is on top. When the user re-opens an email
window (body.email-front, set by openEmailLibrary / reader open+restore) →
the email windows jump ABOVE the doc. Covers the email library AND any open
reader window (email-reader-<uid>); both are .modal at z-250 by default. */
body.doc-view #email-lib-modal,
body.doc-view .modal[id^="email-reader-"] { z-index: 150 !important; }
body.doc-view.email-front #email-lib-modal,
body.doc-view.email-front .modal[id^="email-reader-"] { z-index: 300 !important; }
body.doc-view:not(.email-front) .doc-editor-pane { z-index: 10010 !important; }
body.doc-view:not(.email-front) #email-lib-modal,
body.doc-view:not(.email-front) .modal[id^="email-reader-"] { z-index: 10000 !important; }
/* Hide new-session button and hamburger when doc editor is open on mobile */
body.doc-view .mobile-new-chat-btn,
/* Hide the global hamburger while Compare Mode is active — the compare
header has its own close button in the top-right, and overlapping the
two looks like a bug. The .compare-active class lives on the chat
container, not body, so use :has(). */
body:has(.compare-active) .hamburger-btn { display: none !important; }
/* Mobile: hide the sidebar hamburger when a document panel (or notes pane)
is open — those sheets cover the whole screen on mobile, so a floating
hamburger sticking out over them is just clutter / mis-tap bait. */
@media (max-width: 768px) {
body.doc-view .hamburger-btn,
body:has(#notes-pane) .hamburger-btn { display: none !important; }
}
/* Make room for hamburger button alongside the tab/header bars (fallback if shown) */
body.doc-view.sidebar-collapsed.hamburger-left .doc-tab-bar,
body.doc-view.sidebar-collapsed.hamburger-left .doc-editor-header,
body.doc-view.sidebar-collapsed.hamburger-left .doc-md-toolbar {
padding-left: 44px;
}
body.doc-view.sidebar-collapsed.hamburger-right .doc-tab-bar,
body.doc-view.sidebar-collapsed.hamburger-right .doc-editor-header,
body.doc-view.sidebar-collapsed.hamburger-right .doc-md-toolbar {
padding-right: 44px;
}
/* ── Tab bar — match header height, bigger touch targets ── */
.doc-tab-bar {
padding: 0;
height: 40px;
}
.doc-tab {
padding: 0 12px;
font-size: 13px;
}
/* Bigger × touch target on mobile. */
.doc-tab-close {
font-size: 22px;
width: 32px;
height: 32px;
padding: 0;
opacity: 0.5;
}
.doc-tab .doc-tab-menu-btn {
opacity: 0.4 !important;
padding: 4px 6px !important;
}
.doc-tab .doc-tab-menu-btn svg {
width: 14px !important;
height: 14px !important;
}
/* Footer is identical to desktop now, so the separate mobile Close/Copy
footer is dropped and the per-tab × stays (matching desktop). */
.doc-mobile-footer { display: none !important; }
.doc-tab-new {
font-size: 13px !important;
padding: 0 14px !important;
gap: 4px;
}
/* No hamburger padding — it's hidden in doc-view */
body.doc-view.sidebar-collapsed.hamburger-left .doc-tab-bar,
body.doc-view.sidebar-collapsed.hamburger-left .doc-editor-header,
body.doc-view.sidebar-collapsed.hamburger-left .doc-md-toolbar {
padding-left: 0 !important;
}
body.doc-view.sidebar-collapsed.hamburger-right .doc-tab-bar,
body.doc-view.sidebar-collapsed.hamburger-right .doc-editor-header,
body.doc-view.sidebar-collapsed.hamburger-right .doc-md-toolbar {
padding-right: 0 !important;
}
/* ── Header — identical layout to desktop (left-aligned), match tab height ── */
.doc-editor-header {
padding: 6px 12px 6px 8px;
gap: 6px;
min-height: 40px;
}
.doc-import-label,
#doc-version-badge {
display: none !important;
}
/* ── Markdown toolbar — same height as tab bar ── */
.doc-md-toolbar {
height: 40px !important;
padding: 4px 8px;
gap: 2px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
/* The toolbar's padding-left is forced to 0 by the hamburger rules, so give
the Edit/Preview toggle its own left margin to clear the screen edge +
the 18px mask fade. Margin isn't overridden by those !important paddings. */
.doc-md-toolbar .md-view-toggle { margin-left: 8px; }
.doc-md-toolbar button {
font-size: 13px !important;
padding: 6px 10px !important;
min-width: 34px;
min-height: 26px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.md-toolbar-sep {
height: 18px;
margin: 0 4px;
}
}
/* Opacity slider in the theme tabs row — lets the user see the page behind
the modal while tweaking colors. JS toggles .hidden based on active tab. */
.theme-opacity-wrap {
display: inline-flex;
align-items: center;
gap: 5px;
margin-left: auto;
margin-right: 6px;
padding: 2px 9px;
border: 1px solid var(--border);
border-radius: 999px;
opacity: 0.65;
transition: opacity 0.15s, border-color 0.15s, background 0.15s, color 0.15s;
height: 22px;
align-self: center;
/* Now a <button> toggle, not a slider wrapper. */
cursor: pointer;
color: var(--fg);
font: inherit;
background: transparent;
}
.theme-opacity-wrap.hidden { display: none; }
.theme-opacity-wrap:hover { opacity: 1; }
/* The title h4 already carries margin-right:auto to group header controls
on the right. BOTH the Peek button AND the injected minimize button also
have margin-left:auto — and multiple competing auto-margins split the
free space, stranding Peek in the middle. Zero their left-autos here so
the h4 alone pushes Peek + minimize + close together, flush right. */
.modal-header .theme-opacity-wrap,
.modal-header .modal-minimize-btn { margin-left: 0 !important; }
/* On = peeking through; highlight with the accent so the state is obvious. */
.theme-opacity-wrap.active {
opacity: 1;
border-color: var(--accent, var(--red));
background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
color: var(--accent, var(--red));
}
.theme-opacity-label { font-size: 10px; font-weight: 600; letter-spacing: 0.02em; }
.theme-opacity-wrap > svg { flex-shrink: 0; opacity: 0.7; }
.theme-opacity-wrap input[type="range"] {
width: 92px;
height: 4px;
-webkit-appearance: none;
appearance: none;
background: color-mix(in srgb, var(--fg) 18%, transparent);
border-radius: 2px;
outline: none;
margin: 0;
}
.theme-opacity-wrap input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--accent, var(--red));
border: none;
cursor: pointer;
}
.theme-opacity-wrap input[type="range"]::-moz-range-thumb {
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--accent, var(--red));
border: none;
cursor: pointer;
}
/* ── Theme editor: hover-to-highlight zone ── */
.theme-zone-highlight {
position: fixed;
pointer-events: none;
z-index: 9998;
border: 2px dashed var(--accent, var(--red));
border-radius: 6px;
background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent, var(--red)) 40%, transparent),
0 0 18px color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
animation: theme-zone-pulse 1.4s ease-in-out infinite;
}
@keyframes theme-zone-pulse {
0%, 100% { opacity: 0.95; }
50% { opacity: 0.55; }
}
/* Highlight the color row itself too so the user has a strong visual link
between the input they're hovering and the zone overlay on the page. */
#theme-tab-customize .color-row {
transition: background 0.15s, border-color 0.15s;
border-radius: 6px;
}
#theme-tab-customize .color-row:hover {
background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent);
}
/* "Auto-saved" pill that flashes after each color/font change. */
.theme-autosaved-pill {
position: sticky;
bottom: 8px;
margin: 8px auto 0;
width: fit-content;
display: flex;
align-items: center;
gap: 5px;
background: color-mix(in srgb, var(--color-success, #4caf50) 18%, var(--bg));
color: var(--color-success, #4caf50);
border: 1px solid color-mix(in srgb, var(--color-success, #4caf50) 40%, transparent);
border-radius: 12px;
padding: 4px 10px;
font-size: 11px;
font-weight: 500;
opacity: 0;
transform: translateY(4px);
transition: opacity 0.2s ease-out, transform 0.2s ease-out;
pointer-events: none;
z-index: 5;
}
.theme-autosaved-pill.visible {
opacity: 1;
transform: translateY(0);
}
/* ---- Doc fullscreen ---- */
.doc-editor-pane.doc-fullscreen {
flex: 1;
max-width: 100%;
width: 100% !important;
border-left: none;
}
/* Keep the hamburger reachable in fullscreen — version history can still
hide it (the panel covers that area). */
body:has(.doc-version-panel:not(.hidden)) .hamburger-btn {
display: none !important;
}
/* ---- Document run output ---- */
.doc-run-output {
border-top: 2px solid var(--hl-function, #61afef);
background: var(--bg);
padding: 6px 12px 8px;
max-height: 200px;
overflow: auto;
font-family: 'Fira Code', 'Courier New', monospace;
font-size: 0.85em;
line-height: 1.5;
position: relative;
}
.doc-run-output .code-runner-pre { background: none !important; border: none !important; margin: 0; padding: 0; }
.doc-run-output .code-runner-copy-inline {
right: 16px;
width: 28px;
height: 26px;
padding: 0;
align-items: center;
justify-content: center;
}
.doc-run-output .code-runner-error { color: var(--red); }
.doc-run-output .code-runner-loading { font-style: italic; color: var(--red); }
.doc-run-output .code-runner-empty { color: var(--fg-muted, var(--fg)); padding: 2px 0; }
.doc-run-output .code-runner-close { position: absolute; top: 4px; right: 4px; background: none; border: none; color: var(--fg); cursor: pointer; opacity: 0.5; font-size: 14px; padding: 2px 6px; }
.doc-run-output .code-runner-close:hover { opacity: 1; }
.doc-run-pre { color: var(--fg); white-space: pre-wrap; word-break: break-word; margin: 0; }
.doc-run-error { color: var(--red); white-space: pre-wrap; word-break: break-word; margin: 0; }
/* ---- Markdown preview ---- */
.doc-md-preview {
flex: 1;
overflow-y: auto;
padding: 16px 20px;
font-family: inherit;
font-size: 12px;
line-height: 1.6;
color: var(--fg);
background: var(--bg);
}
.doc-md-preview.doc-rich-preview {
background: #fff;
color: #111;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.6;
padding: 32px max(24px, calc((100% - 780px) / 2));
}
.doc-rich-preview > :not(.doc-preview-hover-edit) {
max-width: 780px;
margin-left: auto;
margin-right: auto;
}
.doc-rich-preview-empty {
position: relative;
left: -16px;
top: -8px;
color: #6b7280;
text-align: center;
}
.doc-preview-hover-edit {
position: sticky;
top: 0;
z-index: 4;
float: right;
display: inline-flex;
align-items: center;
gap: 5px;
margin: 0 0 8px 12px;
padding: 5px 8px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--panel);
color: var(--fg);
font: inherit;
font-size: 11px;
cursor: pointer;
opacity: 0;
pointer-events: none;
transform: translateY(-2px);
transition: opacity 0.12s, transform 0.12s, background 0.12s, border-color 0.12s;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
}
.doc-md-preview:hover .doc-preview-hover-edit,
.doc-preview-hover-edit:focus-visible {
opacity: 1;
pointer-events: auto;
transform: translateY(0);
}
.doc-preview-hover-edit:hover {
background: color-mix(in srgb, var(--accent, var(--red)) 12%, var(--panel));
border-color: color-mix(in srgb, var(--accent, var(--red)) 55%, var(--border));
}
/* DOCX visual preview — keep the Word document on a readable white sheet even
when the surrounding workspace uses a dark theme. */
.doc-docx-preview {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
padding: 28px 18px 42px;
background: #d9dce1;
color: #111827;
}
.doc-docx-paper {
width: min(816px, 100%);
min-height: 1056px;
box-sizing: border-box;
margin: 0 auto;
padding: 72px 76px;
background: #fff;
color: #111;
font-family: Arial, Helvetica, sans-serif;
font-size: 15px;
line-height: 1.55;
box-shadow: 0 4px 18px rgba(0,0,0,.22);
}
.doc-docx-paper h1, .doc-docx-paper h2, .doc-docx-paper h3,
.doc-docx-paper h4, .doc-docx-paper h5, .doc-docx-paper h6 {
color: #111;
line-height: 1.2;
margin: 1.2em 0 .55em;
}
.doc-docx-paper p { margin: 0 0 .8em; }
.doc-docx-paper img { max-width: 100%; height: auto; }
.doc-docx-paper table { width: 100%; border-collapse: collapse; margin: 1em 0; }
.doc-docx-paper td, .doc-docx-paper th { border: 1px solid #c7cbd1; padding: 6px 8px; vertical-align: top; }
.doc-docx-paper th { background: #f2f3f5; }
.doc-docx-paper a { color: #1757a6; }
.doc-docx-preview-loading, .doc-docx-preview-error {
max-width: 816px;
margin: 40px auto;
padding: 20px;
color: var(--fg);
text-align: center;
}
.doc-docx-preview-error { color: var(--color-error, #c2413d); }
@media (max-width: 700px) {
.doc-docx-preview { padding: 12px 8px 28px; }
.doc-docx-paper { min-height: 0; padding: 34px 24px; font-size: 14px; }
}
.doc-md-preview pre {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 6px;
padding: 10px 12px;
overflow-x: auto;
}
.doc-md-preview code {
font-size: 0.95em;
}
.doc-md-preview p { margin: 0.6em 0; }
.doc-md-preview h1, .doc-md-preview h2, .doc-md-preview h3 {
margin: 0.8em 0 0.4em;
color: var(--hl-string);
}
.doc-md-preview ul, .doc-md-preview ol {
margin-left: 20px;
margin-bottom: 0.6em;
}
.doc-md-preview blockquote {
border-left: 3px solid var(--border);
padding-left: 12px;
margin: 0.6em 0;
opacity: 0.8;
}
/* ---- Active state for doc action icon buttons ---- */
.doc-action-icon-btn.active {
opacity: 1;
color: var(--red);
background: color-mix(in srgb, var(--red) 12%, transparent);
}
#doc-run-btn:hover {
color: var(--hl-function, #61afef);
}
/* ===== ADMIN PANEL (inside settings modal) ===== */
/* Cards */
.admin-card {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
padding: 12px;
margin-bottom: 10px;
}
/* When the integrations editor opens, the inner admin-card should match the
listed integration cards (subtle tint, same border) instead of reverting
to the solid-panel admin-card surface used elsewhere. */
#unified-intg-form .admin-card,
#integrations-form .admin-card {
background: color-mix(in srgb, var(--fg) 3%, transparent);
}
.admin-card h2 {
font-size: 14px;
font-weight: 600;
letter-spacing: -0.03em;
margin-bottom: 8px;
padding-bottom: 6px;
border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent);
}
.admin-danger-card {
border-color: color-mix(in srgb, var(--color-error) 27%, transparent);
}
/* Toggle switch */
.admin-toggle-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.admin-toggle-label {
font-size: 13px;
font-weight: 500;
}
.admin-toggle-sub {
/* 65% mix keeps this helper text above WCAG AA 4.5:1 on the dark panel
(50% only reached ~3.9:1). */
color: color-mix(in srgb, var(--fg) 65%, transparent);
font-size: 11px;
margin-top: 2px;
}
/* Hide the native up/down spinners on the Max auto-skills number input. */
#skill-max-input {
-moz-appearance: textfield;
appearance: textfield;
}
#skill-max-input::-webkit-inner-spin-button,
#skill-max-input::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
.admin-switch {
position: relative;
width: 30px;
height: 16px;
flex-shrink: 0;
display: inline-block;
}
.admin-switch input {
opacity: 0;
width: 0;
height: 0;
}
.admin-slider {
position: absolute;
inset: 0;
background: color-mix(in srgb, var(--fg) 50%, transparent);
border-radius: 8px;
cursor: pointer;
transition: background 0.08s;
}