Files
Varaverk/Plugin/unraid/css/varaverk.css
T
Gmer4Lfe 1afeaf0e15 Give the token ledger its own card on the dashboard
The AI card answers whether the model is healthy now; what it has spent over a month was a
different question crowding it out one line at a time.
2026-08-09 11:00:00 -04:00

1238 lines
76 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.
/* Varaverk plugin styles — inherits unRAID theme, adds plugin-specific layout */
#varaverk-wrap { padding: 10px; font-family: inherit; }
/* Tab bar */
#vv-tabs { display: flex; gap: 4px; margin-bottom: 16px; border-bottom: 2px solid #444; align-items: flex-end; }
.vv-tab { padding: 6px 16px; text-decoration: none; color: #aaa; border-radius: 4px 4px 0 0; }
.vv-tab:hover { color: #fff; background: #333; }
.vv-tab.active { color: #fff; background: #555; border-bottom: 2px solid #fff; }
/* Expand toggle button */
#vv-expand-btn {
background: none; border: none; cursor: pointer;
color: #333; font-size: 15px; padding: 2px 8px 4px;
line-height: 1; border-radius: 3px; transition: color .15s;
margin-left: 6px; flex-shrink: 0;
}
#vv-expand-btn:hover { color: #888; }
#vv-expand-btn.active { color: #aaa; }
/* Fullscreen mode — hide Unraid chrome, reclaim the space */
body.vv-fullscreen #header { display: none !important; }
body.vv-fullscreen #menu { display: none !important; }
body.vv-fullscreen #displaybox { padding-left: 1rem !important; padding-top: .5rem !important; }
/* Cards / layout */
.vv-row { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.vv-card { flex: 1; min-width: 200px; background: #1e1e1e; border: 1px solid #444;
border-radius: 6px; padding: 12px; }
.vv-wide { flex: 100%; }
.vv-card h3 { margin: 0 0 10px; font-size: 13px; text-transform: uppercase;
color: #888; letter-spacing: 0.05em; white-space: normal;
overflow: hidden; min-width: 0;
display: flex; align-items: center; justify-content: space-between; }
/* Cog icon — links to related Unraid page. Hidden until card hovered. */
.vv-card-cog { color: #2a2a2a; font-size: 13px; line-height: 1; text-decoration: none;
padding: 1px 3px; border-radius: 3px; flex-shrink: 0;
transition: color 0.15s, background 0.15s;
font-style: normal; font-weight: normal; text-transform: none;
letter-spacing: 0; cursor: pointer; }
.vv-card:hover .vv-card-cog { color: #4a4a4a; }
.vv-card-cog:hover { color: #aaa !important; background: #333; }
/* Card header icon wrapper */
.vv-ico { display:inline-flex; align-items:center; opacity:0.38; flex-shrink:0; }
/* Status accent — left border colour for state-driven cards */
.vv-accent-ok { border-left-color: #2a5a2a !important; }
.vv-accent-warn { border-left-color: #5a4000 !important; }
.vv-accent-err { border-left-color: #5a1e1e !important; }
#vv-monitor .vv-card { transition: border-left-color 0.5s; }
/* Status banner — coloured strip at top of card body */
.vv-banner { border-radius: 4px; padding: 5px 10px; margin-bottom: 10px;
display: flex; align-items: center; justify-content: space-between;
font-size: 12px; font-weight: 600; }
.vv-banner-ok { background: #061306; border: 1px solid #1a401a; color: #4caf50; }
.vv-banner-warn { background: #130e00; border: 1px solid #3d2e00; color: #ff9800; }
.vv-banner-err { background: #140404; border: 1px solid #3d1010; color: #f44336; }
.vv-banner-off { background: #0d0d0d; border: 1px solid #252525; color: #555; }
/* System card — no h3, no top padding waste */
#vv-system { padding-top: 14px; }
/* System action buttons */
.vv-sys-btn { background: #2a2a2a; border: 1px solid #e65100; color: #ff9800; border-radius: 3px;
padding: 3px 0; font-size: 6px; cursor: pointer; line-height: 1;
width: 50px; min-width: 0; text-align: center; }
.vv-sys-btn:hover { background: #3a2000; color: #ffb74d; border-color: #ff9800; }
/* Fallback state badge */
.vv-state-badge { font-size: 20px; font-weight: bold; padding: 4px 0; margin-bottom: 2px; }
.vv-state-normal { color: #4caf50; }
.vv-state-failover { color: #f44336; }
.vv-state-no_internet { color: #ff9800; }
.vv-state-dark { color: #9e9e9e; }
.vv-state-unknown { color: #666; }
/* Docker table */
#vv-docker-table { width: 100%; border-collapse: collapse; font-size: 13px; }
#vv-docker-table th { text-align: left; padding: 4px 8px; color: #888;
border-bottom: 1px solid #444; }
#vv-docker-table td { padding: 4px 8px; border-bottom: 1px solid #2a2a2a; }
.vv-status-up { color: #4caf50; }
.vv-status-down { color: #f44336; }
/* Scheduler */
.vv-hint { color: #888; font-size: 13px; margin-bottom: 16px; }
.vv-sched-card { margin-bottom: 10px; }
.vv-script { background: #161616; border: 1px solid #333; border-radius: 4px;
padding: 6px 10px; margin: 4px 0; margin-left: 20px; }
.vv-job-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
transition: background 0.1s, box-shadow 0.1s; }
/* Orchestrator top row — clickable, show pointer + hover feedback */
.vv-orch-row { cursor: pointer; border-radius: 4px; }
.vv-orch-row:hover { background: rgba(255,255,255,0.04); }
.vv-job-label { flex: 1; font-size: 16px; font-weight: bold;
color: #6fcf97; min-width: 80px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vv-job-actions { display: flex; align-items: center; gap: 8px; padding-left: 44px; margin-top: 6px; }
.vv-job-desc { font-size: 14px; color: #777; margin: 3px 0 2px 0;
padding-left: 44px; box-sizing: border-box; width: 100%;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
cursor: default; }
.vv-cron { flex: 0 0 80px; width: 80px; background: #111; border: 1px solid #444; color: #ddd;
padding: 4px 6px; border-radius: 4px; font-family: monospace; font-size: 11px; }
.vv-event-badge { flex: 0 0 auto; padding: 3px 8px; border-radius: 4px; font-size: 12px;
background: #1a3a1a; border: 1px solid #2e6b2e; color: #6fcf6f;
white-space: nowrap; font-weight: 500; }
.vv-flag-badge { flex: 0 0 auto; padding: 2px 6px; border-radius: 3px; font-size: 11px;
background: #2e2200; border: 1px solid #6b4e00; color: #d4a017;
white-space: nowrap; font-family: monospace; }
.vv-log-label { display: flex; align-items: center; gap: 4px; font-size: 12px; color: #888;
cursor: pointer; white-space: nowrap; flex-shrink: 0; }
.vv-log-label input { cursor: pointer; accent-color: #4caf50; }
.vv-log-label:has(input:checked) { color: #4caf50; }
.vv-children { padding-top: 8px; border-top: 1px solid #333; margin-top: 8px; }
.vv-advanced-toggle { background: none; border: 1px solid #555; color: #aaa;
padding: 2px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; }
.vv-advanced-toggle:hover { border-color: #888; color: #fff; }
.vv-btn-sm { padding: 3px 10px; background: #2a2a2a; border: 1px solid #555; color: #ccc;
border-radius: 4px; cursor: pointer; font-size: 12px; white-space: nowrap; }
.vv-btn-sm:hover { border-color: #888; color: #fff; }
.vv-btn-sm.active { border-color: #4caf50; color: #4caf50; }
/* Save checkmark */
.vv-save-check { color: #4caf50; font-size: 13px; width: 14px; flex-shrink: 0;
opacity: 0; text-align: center; }
@keyframes vv-check-fade { 0%,60% { opacity: 1; } 100% { opacity: 0; } }
.vv-save-check.vv-check-show { animation: vv-check-fade 2s forwards; }
/* Running dot */
.vv-job-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
.vv-dot-running { background: #4caf50; animation: vv-pulse-dot 1s ease-in-out infinite; }
#vv-log-dot { animation: vv-pulse-dot 1s ease-in-out infinite; }
@keyframes vv-pulse-dot { 0%, 100% { opacity: 1; } 50% { opacity: 0.2; } }
/* Selected job row — editor-style left accent + blue tint */
.vv-row-selected { background: rgba(100,149,237,0.1); border-radius: 4px;
outline: 1px solid rgba(100,149,237,0.35);
box-shadow: inset 3px 0 0 #6495ed; }
/* Two-panel layout */
#vv-sched-layout { display: flex; gap: 16px; align-items: stretch; }
#vv-sched-left { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-self: flex-start; }
#vv-sched-cards { flex: 1; }
#vv-sched-right { display: none; flex: 1 1 0; min-width: 0; flex-direction: column; overflow: hidden; align-self: flex-start; }
#vv-sched-right.vv-panel-visible { display: flex; }
.vv-log-card { flex: 1; display: flex; flex-direction: column; padding-bottom: 0; }
.vv-log-right-pre { max-height: none; overflow-y: auto; }
/* Scheduler stacked layout (narrow viewport) */
@media (max-width: 900px) {
#vv-sched-layout { flex-direction: column; align-items: stretch; }
#vv-sched-left { flex: none; width: 100%; }
#vv-sched-right { flex-direction: column; width: 100%; overflow: visible; }
.vv-log-right-pre { min-height: 520px; max-height: 680px; }
/* Toolbar: stack title row above buttons row, let buttons wrap */
.vv-log-toolbar { flex-direction: column; align-items: flex-start; gap: 6px; }
.vv-log-toolbar > div { flex-wrap: wrap; gap: 6px !important; }
/* Array-event rows: badge already shows the event, cron input is redundant */
.vv-job-row:has(.vv-event-badge) .vv-cron { display: none; }
}
/* Plugin settings row (Advanced mode) */
.vv-nb-settings { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
padding: 5px 10px; background: #101010; border-bottom: 1px solid #1a1a1a; }
/* ── Monitor: locked row height + scrollable cards ────────────────────────── */
/* Cards on the monitor grid are flex columns — h3 pins, body scrolls */
#vv-monitor .vv-card {
display: flex;
flex-direction: column;
overflow: hidden;
}
#vv-monitor .vv-card h3 { flex-shrink: 0; }
#vv-monitor .vv-card > div {
flex: 1;
min-height: 0;
overflow-y: auto;
-ms-overflow-style: none;
scrollbar-width: none;
}
#vv-monitor .vv-card > div::-webkit-scrollbar { display: none; }
/* Hide scrollbars on any nested scrollable div inside monitor cards */
#vv-monitor .vv-card div::-webkit-scrollbar { display: none; }
/* Dynamic row heights capped per screen tier — rows size to content, never exceed the cap.
minmax(0, Xpx): track is content-driven but capped; align-items:stretch makes all cards
in a row fill the track, so short cards (Pools, Watchdog) match tall ones (Array).
Breakpoints are viewport height (after browser chrome), not screen height. */
/* ~720p (viewport ≤ 700px) */
@media (min-width: 481px) and (max-height: 700px) {
#vv-monitor { grid-auto-rows: minmax(0, calc((100vh - 160px) / 4)); }
}
/* ~1080p (viewport 7011100px) */
@media (min-width: 481px) and (min-height: 701px) and (max-height: 1100px) {
#vv-monitor { grid-auto-rows: minmax(0, calc((100vh - 240px) / 4)); }
}
/* ~1440p (viewport 11011450px) — calibrated on 15" 1440p display */
@media (min-width: 481px) and (min-height: 1101px) and (max-height: 1450px) {
#vv-monitor { grid-auto-rows: minmax(0, calc((100vh - 335px) / 4)); }
}
/* ~4K (viewport > 1450px) */
@media (min-width: 481px) and (min-height: 1451px) {
#vv-monitor { grid-auto-rows: minmax(0, calc((100vh - 500px) / 4)); }
}
/* Mobile: natural heights, let page scroll */
@media (max-width: 480px) {
#vv-monitor { grid-auto-rows: auto !important; }
#vv-monitor .vv-card { overflow: visible !important; }
#vv-monitor .vv-card > div { overflow-y: visible; min-height: auto; }
}
/* ── Monitor: AI row ──────────────────────────────────────────────────────────
Same eight columns and the same gap as #vv-monitor, and margin-top matches that grid's row
gap, so the two containers read as one board. What differs is height, which is the whole
reason it is a separate grid: no grid-auto-rows cap and no overflow:hidden on the cards, so
the Assistant's expand control gives the transcript real room instead of growing it inside a
box the row had already sized.
align-items:start, not the stretch the grid above uses, so a card opts in to matching the
Assistant rather than being dragged to its height. Conversations opts in below — a list next
to a transcript reads better level, and it has rows to fill the space with. AI stays at its
own height: its content is a handful of lines, and stretching it to 750px after an expand
would buy a tidy bottom edge with a mostly empty panel. */
#vv-monitor-ai {
display: grid;
grid-template-columns: repeat(8, 1fr);
gap: 12px;
margin-top: 12px;
width: 100%;
box-sizing: border-box;
align-items: start;
}
/* Explicit placement, two rows. The left column carries AI over Tokens; Conversations and the
Assistant span both rows, so the Assistant sets the height and the two stacked cards divide
what it sets. This has to be written as row+column rather than the spans that were inline on
the markup: a span says how wide, never which row, and auto-placement would drop Tokens into
the first gap it found rather than under AI. */
#vv-ai-stats-card { grid-column: 1 / span 1; grid-row: 1; }
#vv-ai-tokens-card { grid-column: 1 / span 1; grid-row: 2; }
#vv-ai-chats-card { grid-column: 2 / span 2; grid-row: 1 / span 2; }
#vv-ai-assistant-card { grid-column: 4 / span 5; grid-row: 1 / span 2; }
/* Conversations and Tokens take their height from the Assistant rather than setting their own.
align-self overrides the row's align-items:start for these two, so they fill the track the
Assistant defines — level at rest, and level again after expanding, since the track grows
with it.
They need their own scroll to do that, which Conversations did not need inside #vv-monitor:
there it inherited a cap from `.vv-card > div { flex:1; overflow-y:auto }`. Out here both are
content-driven, so forty saved chats — or a ledger with both hosts and a footer — would make
the card the tallest thing in its column and set the height it is supposed to be following.
The h3 pins and the body takes what is left, the same shape the monitor grid above uses. */
#vv-monitor-ai #vv-ai-chats-card,
#vv-monitor-ai #vv-ai-tokens-card {
align-self: stretch;
display: flex;
flex-direction: column;
overflow: hidden;
}
#vv-monitor-ai #vv-ai-chats-card h3,
#vv-monitor-ai #vv-ai-tokens-card h3 { flex-shrink: 0; }
/* height:0 with flex:1 is the part that makes these follow rather than lead. flex-basis is
already 0, so it changes nothing about the layout; what it changes is the height the body
reports upward. Without it the card can still hand the auto-sized grid track a max-content
height of its whole contents, and the track it was meant to fit inside grows to fit it
instead. It grows back to fill the track through flex, then scrolls. */
#vv-monitor-ai #vv-ai-chats-card > div,
#vv-monitor-ai #vv-ai-tokens-card > div {
flex: 1;
height: 0;
min-height: 0;
overflow-y: auto;
-ms-overflow-style: none;
scrollbar-width: none;
}
#vv-monitor-ai #vv-ai-chats-card > div::-webkit-scrollbar,
#vv-monitor-ai #vv-ai-tokens-card > div::-webkit-scrollbar { display: none; }
/* ── AI token ledger — shared by the AI tab and the Monitor card ──────────────
Moved out of pages/ai.php so both surfaces can draw the same rows. A tab's inline <style> is
only present while that tab is, and the Monitor renders this ledger too. The AI panel's own
chrome (.vv-ai-tok, the two-column split) stayed behind — it is that panel's frame, not part
of the ledger. */
.vv-ai-hostrow { display:flex; align-items:center; gap:8px; font-size:11px; padding:4px 6px;
border-radius:4px; cursor:pointer; border:1px solid transparent; }
.vv-ai-hostrow:hover { background:#141414; }
.vv-ai-hostrow.active { background:#14202c; border-color:#2d4a6a; }
.vv-ai-hostrow-n { color:#8a8a8a; }
.vv-ai-hostrow-h { color:#4a4a4a; font-family:monospace; font-size:10px; }
.vv-ai-hostrow-v { margin-left:auto; color:#c8c8c8; font-family:monospace; font-size:11px; flex-shrink:0; }
.vv-ai-hostrow-v.none { color:#3a3a3a; font-style:italic; font-family:inherit; font-size:10px; }
.vv-ai-tok-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:12px; }
.vv-ai-tok-l { font-size:9px; letter-spacing:.08em; text-transform:uppercase; color:#4a4a4a; }
.vv-ai-tok-v { font-size:17px; font-weight:bold; color:#c8c8c8; font-family:monospace; line-height:1.3; }
.vv-ai-tok-s { font-size:10px; color:#5a5a5a; }
.vv-ai-tok-foot { margin-top:9px; padding-top:7px; border-top:1px solid #1a1a1a; font-size:10px;
color:#4a4a4a; display:flex; gap:14px; flex-wrap:wrap; }
/* The ledger in a 1-of-8 column. Every rule here is the same content given a sixth of the width
it has on the AI tab: the tiles stop sitting side by side, the host id drops under its name
instead of competing with the total for one line, and the footer stacks. */
/* Name and total keep the first line, the host id takes the second on its own. Ordered rather
than reordered in the markup, because the AI tab wants all three on one line and this is the
same HTML. flex-basis:100% on the id is what forces the break; without the explicit order the
total would follow it onto a third line instead of staying beside the name. */
#vv-ai-tokens-card .vv-ai-hostrow { flex-wrap: wrap; gap: 1px 6px; padding: 4px 5px; }
#vv-ai-tokens-card .vv-ai-hostrow-v { order: 1; }
#vv-ai-tokens-card .vv-ai-hostrow-h { order: 2; flex-basis: 100%; }
#vv-ai-tokens-card .vv-ai-tok-grid { grid-template-columns: 1fr; gap: 9px; }
#vv-ai-tokens-card .vv-ai-tok-v { font-size: 15px; }
#vv-ai-tokens-card .vv-ai-tok-foot { flex-direction: column; gap: 3px; }
/* Section headings inside the card. .vv-ai-tok-l is already the 9px uppercase label this wants,
so it is reused rather than restated — with room above it to separate the two halves. */
#vv-ai-tokens-card .vv-ai-tok-head { margin: 11px 0 5px; }
#vv-ai-tokens-card .vv-ai-tok-head:first-child { margin-top: 0; }
/* Medium width — 4 columns, mirroring #vv-monitor's own breakpoint. The 1/2/5 spans do not
survive the narrower grid: 5 of 4 would silently overflow the track. */
@media (max-width: 1024px) {
#vv-monitor-ai { grid-template-columns: repeat(4, 1fr) !important; }
#vv-ai-stats-card { grid-column: 1 / span 1 !important; grid-row: 1 !important; }
#vv-ai-tokens-card { grid-column: 1 / span 1 !important; grid-row: 2 !important; }
#vv-ai-chats-card { grid-column: 2 / span 3 !important; grid-row: 1 / span 2 !important; }
/* The Assistant drops to its own full-width row here. Five columns of eight is a readable
transcript; three of four beside a list is not, and it is the card you sit and read. */
#vv-ai-assistant-card { grid-column: 1 / span 4 !important; grid-row: 3 !important; }
}
/* Phone — single column, natural heights, page scrolls. Matching heights is meaningless once
the cards are stacked, and the inner scroll has to go with it: on a page that already scrolls,
a scroll region inside it is a trap for a thumb. */
@media (max-width: 480px) {
#vv-monitor-ai { grid-template-columns: 1fr !important; }
/* grid-row has to be released along with grid-column. The explicit rows above are the whole
reason Tokens sits under AI, and left in place on a single-column grid they would stack
cards into the same cell on top of each other rather than in sequence. */
#vv-monitor-ai > .vv-card { grid-column: 1 / -1 !important; grid-row: auto !important; }
#vv-monitor-ai #vv-ai-chats-card,
#vv-monitor-ai #vv-ai-tokens-card { display: block; overflow: visible; }
/* height:0 is only inert while these are flex items — as plain blocks it would collapse the
body to nothing, so it goes back with the flex context that justified it. */
#vv-monitor-ai #vv-ai-chats-card > div,
#vv-monitor-ai #vv-ai-tokens-card > div { height: auto; min-height: auto; overflow-y: visible; }
}
/* Monitor responsive — 4-column grid at medium width */
@media (max-width: 1400px) {
/* CPU core bars — reduce gap/min-width at intermediate widths before cores get clipped */
.vv-cpu-cores { gap: 2px !important; }
.vv-cpu-core { min-width: 7px !important; }
}
@media (max-width: 1024px) {
#vv-monitor { grid-template-columns: repeat(4, 1fr) !important; }
#vv-docker { grid-column: span 4 !important; }
/* Reset explicit placements so cards reflow in the 4-col grid */
#vv-docker-folders { grid-column: span 4 !important; }
#vv-parity-card { grid-column: auto !important; }
#vv-storage-card { grid-column: auto !important; }
#vv-array-card { grid-column: auto !important; }
/* Streams header: hide right chip group entirely, keep server badges + media type */
.vv-stream-right { display: none; }
}
/* Containers+VMs: single column when viewport is narrow */
@media (max-width: 900px) {
.vv-df-cols { flex-direction: column; align-items: stretch; }
}
/* Phone layout — scheduler row/actions fixes + monitor single-column */
@media (max-width: 480px) {
/* Phone portrait — hint gone, cron stays inline but compact */
.vv-cron-hint { display: none !important; }
.vv-job-row .vv-cron { flex: 0 0 68px; width: 68px; font-size: 10px; margin-left: 30px; }
}
@media (max-width: 600px) {
/* Allow action buttons to wrap rather than overflow the card */
.vv-job-actions { flex-wrap: wrap; padding-left: 0; }
/* Kill the inline margin-left:auto that pushes Advanced off-screen */
.vv-advanced-toggle { margin-left: 0 !important; width: auto !important; }
/* Keep cron from growing — label owns remaining row space */
.vv-cron { flex: 0 0 75px; width: 75px; }
/* Slightly tighter label on narrow screens */
.vv-job-label { font-size: 14px; }
/* Footer buttons wrap instead of overflowing */
.vv-sched-footer { flex-wrap: wrap; }
/* Log toolbar search — narrow on small screens */
#vv-log-search { width: 80px; }
/* Snapshot footer — smaller on mobile */
.vv-snap-footer { gap: 10px !important; }
.vv-snap-item { gap: 4px; }
.vv-snap-label { font-size: 10px; }
.vv-snap-bar { width: 44px; height: 5px; }
.vv-snap-val { font-size: 11px; min-width: 26px; }
.vv-snap-div { font-size: 11px; }
.vv-snap-state { font-size: 11px; }
#vv-snap-partner { font-size: 11px; }
.vv-snap-media { font-size: 11px; }
/* CPU core bars — shrink gap and min-width so many cores don't overflow */
.vv-cpu-cores { gap: 2px !important; }
.vv-cpu-core { min-width: 6px !important; }
/* Monitor single-column — explicit placement cards need override too */
#vv-monitor { grid-template-columns: 1fr !important; }
#vv-monitor > .vv-card { grid-column: 1 / -1 !important; }
#vv-docker-folders { grid-column: 1 / -1 !important; }
}
/* Shared footer (Save Schedule left, info right) — same min-height so log card ends level with script cards */
.vv-sched-footer { display: flex; align-items: center; gap: 10px;
margin-top: 12px; padding: 10px 0; border-top: 1px solid #333;
flex-shrink: 0; min-height: 72px; box-sizing: border-box; }
.vv-sched-info { color: #666; font-size: 14px; display: flex; flex-direction: column; gap: 9px; }
.vv-save-btn { padding: 5px 18px; background: #4caf50; border: none; color: #fff;
border-radius: 4px; cursor: pointer; font-size: 13px; }
.vv-save-btn:hover { background: #388e3c; }
.vv-save-status { font-size: 12px; color: #aaa; }
.vv-run-btn { border-color: #2196f3 !important; color: #2196f3 !important; }
.vv-run-btn:hover { background: #0d47a1 !important; color: #fff !important;
border-color: #2196f3 !important; }
.vv-dry-btn { border-color: #ff9800 !important; color: #ff9800 !important; }
.vv-dry-btn:hover { background: #e65100 !important; color: #fff !important;
border-color: #ff9800 !important; }
.vv-log-btn { border-color: #555 !important; color: #666 !important; }
.vv-log-btn:hover { background: #333 !important; color: #aaa !important; border-color: #777 !important; }
.vv-log-btn.vv-has-log { border-color: #4caf50 !important; color: #4caf50 !important; }
.vv-log-btn.vv-has-log:hover { background: #1b5e20 !important; color: #fff !important;
border-color: #4caf50 !important; }
.vv-edit-btn { border-color: #7b1fa2 !important; color: #ce93d8 !important; }
.vv-edit-btn:hover { background: #4a148c !important; color: #fff !important; border-color: #7b1fa2 !important; }
.vv-add-script-btn { background: #1565c0 !important; border: none !important; color: #fff !important; margin-left: 8px; }
.vv-add-script-btn:hover { background: #0d47a1 !important; }
.vv-save-script-btn-style { background: #1565c0 !important; border: none !important; color: #fff !important; }
.vv-save-script-btn-style:hover { background: #0d47a1 !important; }
.vv-delete-btn { background: #b71c1c !important; border: none !important; color: #fff !important; margin-left: 4px; }
.vv-delete-btn:hover { background: #7f0000 !important; }
.vv-conf-btn { border-color: #00838f !important; color: #4dd0e1 !important; }
.vv-conf-btn:hover { background: #006064 !important; color: #fff !important; border-color: #00838f !important; }
/* Config form */
#vv-confform { padding: 2px 4px; }
.vv-cf-group { border-bottom: 1px solid #252525; padding-bottom: 14px; margin-bottom: 14px; }
.vv-cf-group:last-child { border-bottom: none; margin-bottom: 0; }
.vv-cf-group-header { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: bold;
color: #7a9eb5; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 10px; }
.vv-cf-file { font-size: 10px; color: #555; background: #1a1a1a; border: 1px solid #2e2e2e;
padding: 1px 6px; border-radius: 3px; font-weight: normal; text-transform: none;
letter-spacing: 0; }
/* One setting per striped row, numbered down the left. Previously these were separated only by
a 10px margin, which read as one continuous block once a script had more than a few. The
stripe is what actually does the separating; the number gives each one a handle to refer to. */
.vv-cf-field { display: flex; align-items: flex-start; gap: 10px;
padding: 8px 10px; border-radius: 3px; border-left: 2px solid transparent; }
.vv-cf-alt { background: #141414; }
.vv-cf-field:hover { border-left-color: #2d4a6a; }
.vv-cf-num { font-family: monospace; font-size: 10px; color: #3a3a3a; min-width: 20px;
text-align: right; padding-top: 2px; flex-shrink: 0; user-select: none; }
.vv-cf-field:hover .vv-cf-num { color: #6a6a6a; }
.vv-cf-body { flex: 1; min-width: 0; }
.vv-cf-key { font-family: monospace; font-size: 12px; color: #ccc; margin-bottom: 3px; }
.vv-cf-desc { font-size: 11px; color: #666; margin-bottom: 4px; font-style: italic; line-height: 1.4; }
.vv-cf-scalar { display: block; width: 100%; box-sizing: border-box; background: #111; border: 1px solid #333;
color: #ddd; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; }
.vv-cf-scalar:focus { border-color: #555; outline: none; }
/* Array fields: numbered gutter + striped rows. The wrap owns the border so the gutter and the
text read as one control. line-height is an exact 18px, not 1.6 — the stripes are a repeating
gradient and would drift out of register against the text on a fractional line box. */
.vv-cf-arraywrap { display: flex; border: 1px solid #333; border-radius: 4px; overflow: hidden;
background: #0d0d0d; }
.vv-cf-arraywrap:focus-within { border-color: #555; }
.vv-cf-lines { margin: 0; padding: 8px 6px 8px 0; width: 34px; min-width: 34px; flex-shrink: 0;
text-align: right; font-family: monospace; font-size: 11px; line-height: 18px;
color: #3a3a3a; background: #0a0a0a; border-right: 1px solid #1c1c1c;
overflow: hidden; user-select: none; white-space: pre; }
/* No soft wrap. A wrapped line takes two or three visual rows while the gutter prints one number
for it, so the numbers and the stripes drift further out of register with every long entry —
and the trailing comments in DAILY_MAINTENANCE_SCRIPTS are long. Scrolling sideways is the
same trade the main script editor already makes for the same reason. */
.vv-cf-array { display: block; width: 100%; box-sizing: border-box; background-color: transparent;
border: none; color: #ccc; padding: 8px; font-family: monospace; font-size: 11px;
line-height: 18px; resize: vertical; min-height: 60px;
white-space: pre; overflow-x: auto;
background-image: repeating-linear-gradient(rgba(255,255,255,0) 0 18px,
rgba(255,255,255,0.028) 18px 36px);
background-origin: content-box; background-attachment: local; }
.vv-cf-array:focus { outline: none; }
.vv-cf-empty { color: #555; font-size: 13px; font-style: italic; padding: 20px 4px; text-align: center; margin: 0; }
/* "No user adjustable settings" — a stated fact, not an empty region. Tagged rather than greyed
out, because the thing being communicated is that nothing is missing. */
.vv-cf-none { color: #9a8f6a; font-size: 12px; margin: 0; padding: 8px 10px;
background: #1c1a12; border: 1px solid #33301f; border-left: 3px solid #7a6a3a;
border-radius: 3px; }
.vv-custom-empty { color: #666; font-size: 13px; padding: 8px 4px; margin: 0; font-style: italic; }
.vv-custom-count { font-size: 12px; color: #666; margin-left: 8px; flex-shrink: 0; }
.vv-section-sep { font-size: 11px; font-weight: bold; color: #666; text-transform: uppercase;
letter-spacing: 0.08em; padding: 10px 4px 4px; border-top: 1px solid #222; margin-top: 8px; }
#vv-editor { flex-direction: column; gap: 0; }
.vv-editor-body { font-family: monospace; font-size: 12px; background: #0d0d0d; color: #ccc;
border: 1px solid #333; border-radius: 4px; padding: 10px 12px; resize: none;
line-height: 1.5; scroll-behavior: auto; tab-size: 2; width: 100%; box-sizing: border-box;
white-space: pre; overflow-x: auto;
appearance: none; -moz-appearance: none; }
/* Editor layout: gutter + inner area — unified bordered block */
#vv-editor-wrap { display: flex; border: 1px solid #2e2e2e; border-radius: 4px 4px 0 0;
overflow: hidden; background: #0d0d0d; }
#vv-ln-gutter { width: 46px; min-width: 46px; flex-shrink: 0;
background: #0a0a0a; border-right: 1px solid #1c1c1c;
font-family: monospace; font-size: 12px; line-height: 1.5; tab-size: 2;
color: #3a3a3a; text-align: right; padding: 10px 8px 10px 0;
overflow: hidden; user-select: none; white-space: pre; }
.vv-gln-cur { color: #ccc; font-weight: bold; }
#vv-editor-inner { position: relative; flex: 1; overflow: hidden; background: #0d0d0d; }
/* Current line highlight — sits behind overlay */
#vv-cur-line { position: absolute; left: 0; right: 0; height: 18px;
background: rgba(255,255,255,0.04);
border-top: 1px solid rgba(255,255,255,0.03);
border-bottom: 1px solid rgba(255,255,255,0.03);
pointer-events: none; display: none; }
/* Syntax-highlight overlay — transparent bg so cur-line shows through */
#vv-hl-overlay { display: none; position: absolute; top: 0; left: 0; right: 0; bottom: 0;
margin: 0; padding: 10px 12px; box-sizing: border-box;
font-family: monospace; font-size: 12px; line-height: 1.5; tab-size: 2;
white-space: pre; overflow: hidden;
pointer-events: none; user-select: none;
background: transparent; border: none; border-radius: 0; }
.vv-editor-hl #vv-hl-overlay { display: block; }
.vv-editor-hl #vv-editor-body { color: transparent; caret-color: #ddd; background: transparent;
border: none !important; border-radius: 0 !important; }
/* Visible selection — works even when text is transparent */
.vv-editor-hl #vv-editor-body::selection { background: rgba(100,149,237,0.35); color: transparent; }
.vv-editor-hl #vv-editor-body::-moz-selection{ background: rgba(100,149,237,0.35); color: transparent; }
/* Word match and search marks in the overlay */
.vv-word-mark { background: rgba(255,220,60,0.14); outline: 1px solid rgba(255,220,60,0.35); border-radius: 2px; }
.vv-search-mark { background: rgba(255,140,20,0.22); outline: 1px solid rgba(255,140,20,0.45); border-radius: 2px; }
.vv-search-mark-current{ background: rgba(255,90,0,0.40); outline: 2px solid rgba(255,100,0,0.7); border-radius: 2px; }
/* Slim scrollbar for editor */
#vv-editor-body::-webkit-scrollbar { width: 8px; height: 8px; }
#vv-editor-body::-webkit-scrollbar-track { background: #0a0a0a; }
#vv-editor-body::-webkit-scrollbar-thumb { background: #2a2a2a; border-radius: 4px; }
#vv-editor-body::-webkit-scrollbar-thumb:hover { background: #444; }
/* Editor find bar — two-row (find + optional replace) */
#vv-editor-find { display: none; flex-direction: column; gap: 0; padding: 5px 8px;
margin-bottom: 5px; background: #161616; border: 1px solid #333;
border-radius: 4px; font-size: 12px; }
#vv-editor-find.vv-find-open { display: flex; }
.vv-find-row { display: flex; align-items: center; gap: 6px; }
.vv-replace-row { display: none; align-items: center; gap: 6px;
margin-top: 4px; padding-top: 4px; border-top: 1px solid #222; }
.vv-replace-row.vv-repl-open { display: flex; }
.vv-find-expand { font-size: 10px; color: #555; cursor: pointer; flex-shrink: 0;
width: 14px; text-align: center; user-select: none;
transition: transform 0.15s; line-height: 1; }
.vv-find-expand:hover { color: #aaa; }
.vv-find-expand.open { transform: rotate(90deg); color: #888; }
#vv-find-input, #vv-replace-input {
flex: 1; min-width: 0; background: #0d0d0d; border: 1px solid #333;
color: #ddd; padding: 4px 8px; border-radius: 3px;
font-family: monospace; font-size: 12px; }
#vv-find-input:focus, #vv-replace-input:focus {
border-color: #6495ed; outline: none; box-shadow: 0 0 0 1px rgba(100,149,237,0.25); }
#vv-find-input.vv-find-no-match { border-color: #c62828; box-shadow: 0 0 0 1px rgba(198,40,40,0.3); }
.vv-find-count { font-size: 11px; color: #555; white-space: nowrap; flex-shrink: 0; min-width: 64px; text-align: right; }
.vv-find-nav-btn{ padding: 2px 8px !important; }
.vv-repl-btn { border-color: #444 !important; color: #777 !important; }
.vv-repl-btn:hover { border-color: #777 !important; color: #ddd !important; }
.vv-find-x { color: #555; cursor: pointer; font-size: 16px; line-height: 1;
padding: 0 2px; flex-shrink: 0; }
.vv-find-x:hover { color: #aaa; }
/* Editor status bar */
#vv-editor-status { display: none; align-items: center; gap: 10px; padding: 3px 10px;
background: #0e0e0e; border: 1px solid #2e2e2e; border-top: none;
border-radius: 0 0 4px 4px; font-family: monospace;
font-size: 10px; color: #444; user-select: none; flex-shrink: 0; }
#vv-editor-status.vv-ed-active { display: flex; }
.vv-es-pos { color: #666; }
.vv-es-sel { color: #555; }
.vv-es-lang { margin-left: auto; color: #4a6a7a; }
/* Status bar action buttons */
.vv-es-btn { background: none; border: none; cursor: pointer; color: #555;
font-size: 10px; padding: 1px 4px; border-radius: 2px; font-family: monospace; }
.vv-es-btn:hover { color: #aaa; background: rgba(255,255,255,0.05); }
.vv-es-btn.active { color: #6495ed; }
#vv-es-fontsize { color: #444; font-size: 10px; min-width: 26px; text-align: center; }
/* Word wrap mode — applied to #vv-editor-wrap when wrap is enabled */
#vv-editor-wrap.vv-ed-wrap .vv-editor-body { white-space: pre-wrap !important; overflow-x: hidden !important; }
#vv-editor-wrap.vv-ed-wrap #vv-hl-overlay { white-space: pre-wrap !important; word-break: break-word !important; }
#vv-editor-wrap.vv-ed-wrap #vv-ln-gutter { display: none; }
#vv-editor-wrap.vv-ed-wrap #vv-cur-line { display: none !important; }
/* Indent guides — 1px lines every 2 chars, starting at indent level 1 */
#vv-editor-inner {
position: relative; flex: 1; overflow: hidden;
background-color: #0d0d0d;
background-image: repeating-linear-gradient(
90deg,
rgba(255,255,255,0.045) 0 1px,
transparent 1px 2ch
);
background-position: calc(12px + 2ch) 0;
}
/* Go to line bar */
#vv-goto-bar { display: none; align-items: center; gap: 8px; padding: 5px 8px;
margin-bottom: 5px; background: #161616; border: 1px solid #333;
border-radius: 4px; font-size: 12px; }
#vv-goto-bar.vv-goto-open { display: flex; }
#vv-goto-input { width: 68px; background: #0d0d0d; border: 1px solid #333; color: #ddd;
padding: 4px 8px; border-radius: 3px; font-family: monospace; font-size: 12px; }
#vv-goto-input:focus { border-color: #6495ed; outline: none; }
.vv-goto-info { font-size: 11px; color: #555; white-space: nowrap; flex-shrink: 0; }
/* Undo / Redo buttons */
.vv-undo-redo-btn { border-color: #444 !important; color: #666 !important; }
.vv-undo-redo-btn:not([disabled]):hover { border-color: #777 !important; color: #ccc !important; background: #1e1e1e !important; }
.vv-undo-redo-btn[disabled] { opacity: 0.28 !important; cursor: default !important; pointer-events: none; }
.vv-undo-count { font-size: 10px; opacity: 0.7; margin-left: 2px; }
/* Suggestions panel — accordion */
#vv-suggestions { overflow-y: auto; }
.vv-sug-block { border-bottom: 1px solid #1e1e1e; }
.vv-sug-header { display: flex; align-items: center; gap: 8px; padding: 7px 4px;
cursor: pointer; user-select: none; flex-wrap: wrap; }
.vv-sug-header:hover { background: rgba(255,255,255,0.03); }
.vv-sug-chevron { color: #555; font-size: 11px; flex-shrink: 0; width: 10px; }
.vv-sug-title { color: #7a9eb5; font-weight: bold; font-size: 13px; flex: 1; min-width: 120px; }
.vv-sug-cron { color: #ddd; background: #111; border: 1px solid #333; padding: 1px 6px;
border-radius: 3px; font-size: 11px; font-family: monospace; white-space: nowrap; flex-shrink: 0; }
.vv-sug-label { color: #666; font-size: 12px; flex: 1; min-width: 0; white-space: nowrap;
overflow: hidden; text-overflow: ellipsis; }
.vv-sug-status { font-size: 11px; flex-shrink: 0; margin-left: auto; }
.vv-sug-on { color: #4caf50; }
.vv-sug-off { color: #777; }
.vv-sug-none { color: #444; }
.vv-sug-body { padding: 0 14px 10px; }
.vv-sug-desc { font-size: 12px; color: #888; white-space: pre-wrap; word-break: break-word;
background: none; border: none; margin: 4px 0 8px; padding: 0;
font-family: inherit; line-height: 1.6; }
.vv-sug-scripts { display: flex; flex-direction: column; gap: 4px; }
.vv-sug-script-row { display: flex; align-items: center; gap: 8px; font-size: 12px; }
.vv-sug-inline-cron { color: #aaa; background: #111; padding: 1px 5px; border-radius: 3px;
font-size: 11px; white-space: nowrap; }
.vv-sug-path { color: #888; font-family: monospace; }
.vv-sug-configured { color: #4caf50; font-size: 11px; }
/* Info sections inside Scheduler Information panel */
/* ── Assistant dock (scheduler right panel) ────────────────────────────────────────────────── */
#vv-ai-dock { border: 1px solid #262626; border-radius: 6px; background: #0e0e0e;
margin-top: 6px; flex-shrink: 0; }
#vv-ai-dock-bar { display: flex; align-items: center; gap: 7px; padding: 6px 8px; }
#vv-ai-dock-chip { white-space: nowrap; max-width: 240px; overflow: hidden;
text-overflow: ellipsis; }
/* The button around it is .vv-ai-chip and carries the colour, border and padding. */
.vv-ai-chip-flash { animation: vvAiChipFlash .9s ease-out 2; }
@keyframes vvAiChipFlash { 0%,100% { background:#12191d; color:#5a7a8a; }
50% { background:#2a2312; color:#d8b25a; } }
#vv-ai-dock-input { flex: 1; min-width: 0; background: #0a0a0a; border: 1px solid #262626;
border-radius: 4px; color: #c8c8c8; font-family: inherit; font-size: 12px;
padding: 5px 9px; }
#vv-ai-dock-input:focus { outline: none; border-color: #2d4a6a; }
#vv-ai-dock-body { border-bottom: 1px solid #1c1c1c; padding: 9px 10px; overflow-y: auto;
font-size: 12px; line-height: 1.55; }
/* Always present, unlike the collapse button next to it — the size of the conversation is worth
changing before there is one, when you already know the answer will be long. */
.vv-ai-dock-grow { flex-shrink: 0; min-width: 26px; }
.vv-ai-dock-grow-on { color: #8fb0c4; border-color: #2d4a6a; background: #12191d; }
.vv-ai-dock-q { color: #8fb0c4; margin: 0 0 5px; }
.vv-ai-dock-q::before { content: ' '; color: #3a5a6a; }
.vv-ai-dock-a { color: #b8b8b8; margin: 0 0 11px; white-space: normal; }
.vv-ai-dock-a code { background: #1a1a1a; border: 1px solid #333; border-radius: 2px;
padding: 0 4px; font-size: 11px; color: #9ab; }
.vv-ai-dock-a strong { color: #ddd; }
.vv-ai-dock-wait { color: #5a5a5a; font-style: italic; }
.vv-ai-dock-err { color: #e57; }
.vv-ai-dock-src { margin-top: 6px; padding-top: 5px; border-top: 1px solid #1c1c1c;
font-size: 10px; color: #4a4a4a; font-family: monospace; }
.vv-ai-dock-sep { margin: 4px 0 9px; font-size: 10px; color: #4a4a4a; text-align: center;
border-top: 1px solid #1c1c1c; padding-top: 6px; }
.vv-info-block .vv-sug-title { color: #9ab; }
/* Depth switch on the help header. Outlined rather than filled — it changes what you are
reading, it does not do anything to the machine, and every button on this page that does
is filled. */
.vv-more-btn { flex-shrink: 0; background: none; border: 1px solid #8a8a8a; color: #ccc;
font-size: 10px; letter-spacing: .04em; padding: 2px 9px; border-radius: 3px;
cursor: pointer; font-family: inherit; }
.vv-more-btn:hover { border-color: #fff; color: #fff; background: rgba(255,255,255,0.06); }
.vv-more-btn.vv-more-on { border-color: #2d4a6a; color: #7a9eb5; background: #14202c; }
.vv-info-body { padding: 2px 10px 10px; }
.vv-info-cols { margin: 0; padding-left: 16px; columns: 2; column-gap: 20px; column-fill: balance; }
.vv-info-cols li { font-size: 12px; color: #aaa; margin-bottom: 5px; break-inside: avoid; line-height: 1.5; }
.vv-info-cols .vv-info-sep { column-span: all; list-style: none; margin: 10px -4px 6px;
padding: 4px 8px; background: #161a1d; border-left: 2px solid #1e6fa5;
font-size: 10px; font-weight: bold; text-transform: uppercase;
letter-spacing: .08em; color: #4a8ab5; break-inside: avoid; }
.vv-info-cols li strong { color: #ccc; }
.vv-info-cols code { background: #1a1a1a; padding: 0 4px; border-radius: 2px;
font-size: 11px; color: #9ab; border: 1px solid #333; }
/* ── Rendered page docs (include/docs.php → pages/readme/*.md) ─────────────────────────────── */
/* Two columns so the reference tables and the task sections sit side by side rather than making
the panel a long scroll. Headings span both, which is what keeps a task and its steps together
instead of splitting across the gap. */
.vv-doc { columns: 320px 2; column-gap: 26px; font-size: 12px; color: #aaa; line-height: 1.55; }
.vv-doc > h1 { display: none; } /* the panel already has its own title */
.vv-doc h2 { column-span: all; margin: 12px -4px 7px; padding: 4px 8px; background: #161a1d;
border-left: 2px solid #1e6fa5; font-size: 10px; font-weight: bold;
text-transform: uppercase; letter-spacing: .08em; color: #4a8ab5; }
.vv-doc h2:first-child { margin-top: 2px; }
.vv-doc p { margin: 0 0 7px; break-inside: avoid; }
.vv-doc ul, .vv-doc ol { margin: 0 0 8px; padding-left: 16px; }
.vv-doc li { margin-bottom: 4px; break-inside: avoid; }
.vv-doc strong { color: #ccc; }
.vv-doc em { color: #9a9a9a; font-style: italic; }
.vv-doc hr { display: none; } /* h2 bars already separate the sections */
.vv-doc code { background: #1a1a1a; padding: 0 4px; border-radius: 2px; font-size: 11px;
color: #9ab; border: 1px solid #333; }
.vv-doc blockquote { margin: 0 0 8px; padding: 6px 9px; background: #16130d;
border-left: 2px solid #7a5a2a; color: #b39a72; break-inside: avoid; }
.vv-doc .vv-live-var { color: #8fc98f; border-color: #2e4a2e; }
.vv-doc .vv-unknown-var { color: #e57; border-color: #5a2a2a; }
.vv-doc-table { width: 100%; border-collapse: collapse; margin: 0 0 9px; break-inside: avoid; }
.vv-doc-table th { text-align: left; font-size: 9px; text-transform: uppercase; letter-spacing: .07em;
color: #4a4a4a; border-bottom: 1px solid #2a2a2a; padding: 3px 6px 3px 0; }
.vv-doc-table td { font-size: 11px; color: #999; border-bottom: 1px solid #1a1a1a;
padding: 4px 6px 4px 0; vertical-align: top; }
.vv-doc-code { background: #0d0d0d; border: 1px solid #222; border-radius: 3px; padding: 7px 9px;
font-size: 11px; color: #9ab; overflow-x: auto; break-inside: avoid; }
.vv-info-divider { font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: #444;
padding: 10px 12px 4px; border-top: 1px solid #2a2a2a; margin-top: 2px; }
/* How do I use this — pinned at top of suggestions panel */
#vv-how-to-use {
position: sticky;
top: 0;
z-index: 3;
background: #1e1e1e;
border-bottom: 1px solid #333;
}
/* Run-status dot colours (tree + activity list) */
.vv-stat-ok { color: #4caf50; }
.vv-stat-warn { color: #ff9800; }
.vv-stat-error{ color: #f44336; }
.vv-stat-skip { color: #607d8b; }
.vv-stat-none { color: #444; }
/* Cron humanizer hint — left of cron input; first to collapse under space pressure */
.vv-cron-hint { font-size: 10px; color: #505050; white-space: nowrap;
flex: 0 10 auto; min-width: 0; max-width: 130px;
overflow: hidden; text-overflow: ellipsis;
pointer-events: none; user-select: none; }
/* Recent Activity list */
.vv-activity-list { display: flex; flex-direction: column; }
.vv-activity-row { display: flex; align-items: center; gap: 6px; padding: 4px 0;
border-bottom: 1px solid #1a1a1a; font-size: 11px; cursor: pointer; }
.vv-activity-row:last-child { border-bottom: none; }
.vv-activity-row:hover .vv-activity-label { color: #fff; }
.vv-activity-dot { flex-shrink: 0; font-size: 9px; }
.vv-activity-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
white-space: nowrap; color: #ccc; }
.vv-activity-ago { color: #555; font-size: 10px; white-space: nowrap; flex-shrink: 0; }
.vv-activity-dur { color: #444; font-size: 10px; white-space: nowrap; flex-shrink: 0;
min-width: 32px; text-align: right; }
/* Hands a run straight to the assistant. Hidden until the row is hovered — every row carries one
and shown always they read as the point of the list, which is the run history, not the AI. */
.vv-activity-ask { flex-shrink: 0; visibility: hidden; font: inherit; font-size: 10px;
color: #5a7a8a; background: #12191d; border: 1px solid #24343d;
border-radius: 3px; padding: 1px 6px; cursor: pointer; }
.vv-activity-row:hover .vv-activity-ask { visibility: visible; }
.vv-activity-ask:hover { color: #8fb0c4; border-color: #2d4a6a; }
.vv-activity-ask.vv-ask-bad { color: #c88; border-color: #4a2a2a; background: #1d1212; }
.vv-activity-row:focus-within .vv-activity-ask { visibility: visible; }
/* Log search */
#vv-log-search { width: 120px; font-size: 11px; padding: 2px 6px;
background: #111; border: 1px solid #333; color: #ccc;
border-radius: 3px; font-family: monospace; }
#vv-log-search:focus { border-color: #555; outline: none; }
mark { background: #5d4037; color: #ffcc80; border-radius: 2px; }
.vv-log-dim { opacity: 0.2; }
/* Cron Calculator */
.vv-calc-wrap { display: flex; flex-direction: column; gap: 8px; }
.vv-calc-in { width: 100%; background: #111; border: 1px solid #333; color: #ccc;
padding: 5px 8px; border-radius: 3px; font-family: monospace; font-size: 12px;
box-sizing: border-box; }
.vv-calc-in:focus { border-color: #555; outline: none; }
.vv-calc-expr { font-family: monospace; font-size: 13px; color: #7cb8e8;
background: #0d1117; padding: 4px 8px; border-radius: 3px; }
.vv-calc-desc { font-size: 12px; color: #aaa; padding: 2px 2px 0; }
.vv-calc-hint { font-size: 11px; color: #555; font-style: italic; }
.vv-calc-runs-lbl { font-size: 10px; color: #555; text-transform: uppercase;
letter-spacing: .05em; margin-top: 4px; }
.vv-calc-runs { display: flex; flex-direction: column; gap: 2px; }
.vv-calc-run-row { display: flex; gap: 8px; font-size: 11px; }
.vv-calc-run-in { color: #4caf50; white-space: nowrap; min-width: 56px; }
.vv-calc-run-at { color: #666; }
.vv-calc-apply-btn { align-self: flex-start; margin-top: 2px; }
/* Board blocks — Next Runs, Errors, Locks, Partner, Disabled */
.vv-board-placeholder { color: #555; font-size: 11px; padding: 3px 0; }
.vv-hdr-badge { display: inline-block; padding: 1px 7px; border-radius: 10px;
font-size: 10px; font-weight: bold; flex-shrink: 0; }
.vv-hdr-badge-red { background: #7f0000; color: #ef9a9a; }
.vv-hdr-badge-orange { background: #5d2000; color: #ffcc80; }
.vv-hdr-badge-gray { background: #2a2a2a; color: #aaa; }
/* Next Runs */
.vv-nextrun-list { display: flex; flex-direction: column; }
.vv-nextrun-row { display: flex; align-items: center; gap: 8px; padding: 4px 0;
border-bottom: 1px solid #1a1a1a; font-size: 12px; }
.vv-nextrun-row:last-child { border-bottom: none; }
.vv-nr-label { color: #ccc; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vv-nr-cron { color: #555; font-family: monospace; font-size: 10px; flex-shrink: 0; }
.vv-nr-in { color: #4caf50; font-size: 11px; white-space: nowrap; flex-shrink: 0; }
.vv-nr-at { color: #666; font-size: 11px; white-space: nowrap; flex-shrink: 0; }
/* Errors */
.vv-errors-list { display: flex; flex-direction: column; gap: 1px; }
.vv-err-row { padding: 5px 0; border-bottom: 1px solid #1a1a1a; }
.vv-err-row:last-child { border-bottom: none; }
.vv-err-top { display: flex; align-items: baseline; gap: 8px; margin-bottom: 2px; }
.vv-err-script { color: #e07070; font-size: 11px; font-weight: bold; }
.vv-err-age { color: #555; font-size: 10px; flex: 1; padding-left: 6px; white-space: nowrap; }
.vv-ack-btn { margin-left: auto; font-size: 10px; padding: 1px 6px;
color: #555; border-color: #333; flex-shrink: 0; }
.vv-ack-btn:hover { color: #aaa; border-color: #555; }
.vv-err-line { color: #888; font-size: 11px; word-break: break-all; line-height: 1.4; }
/* The error text opens the log at that line. Underlined on hover only — a permanently decorated
line of log output is harder to read, and this block exists to be read first. */
.vv-err-line-open { cursor: pointer; }
.vv-err-line-open:hover { color: #ccc; text-decoration: underline; text-decoration-color: #4a4a4a; }
/* The line the operator was sent to. Left bar rather than a full highlight: the log is dense and
a filled background over one wrapped line reads as a block of colour, not a pointer. */
.vv-log-jump { background: #23303a; color: #dfe8ee; border-left: 3px solid #4a7ea0;
padding-left: 5px; margin-left: -8px; display: inline-block; width: calc(100% + 3px); }
/* Locks */
.vv-locks-list { display: flex; flex-direction: column; }
.vv-lock-row { display: flex; align-items: center; gap: 8px; padding: 4px 0;
border-bottom: 1px solid #1a1a1a; font-size: 12px; }
.vv-lock-row:last-child { border-bottom: none; }
.vv-lk-name { color: #e8a87c; flex: 1; }
.vv-lk-age { color: #777; font-size: 11px; white-space: nowrap; }
.vv-lock-clear { font-size: 11px !important; padding: 1px 7px !important;
border-color: #b71c1c !important; color: #ef9a9a !important; }
.vv-lock-clear:hover { background: #7f0000 !important; color: #fff !important; }
/* Partner */
.vv-partner-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 12px; }
.vv-partner-name { color: #ccc; }
.vv-partner-detail { color: #666; font-size: 11px; }
.vv-partner-down { color: #f44336 !important; }
/* Disabled scripts */
.vv-disabled-list { display: flex; flex-direction: column; gap: 1px; }
.vv-disabled-row { display: flex; align-items: center; gap: 8px; padding: 3px 0;
border-bottom: 1px solid #1a1a1a; font-size: 12px; }
.vv-disabled-row:last-child { border-bottom: none; }
.vv-disabled-name { color: #888; flex: 1; }
.vv-disabled-grp { color: #444; font-size: 10px; font-family: monospace; white-space: nowrap; }
/* Notification board */
.vv-nb-board { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
padding: 7px 10px; background: #141414; border-bottom: 1px solid #222;
font-size: 12px; min-height: 34px; }
.vv-nb-stat { color: #aaa; }
.vv-nb-sep { color: #444; }
.vv-nb-running { color: #f0a040; }
.vv-nb-conf-btns { display: flex; gap: 6px; margin-left: auto; }
.vv-nb-conf-btn { font-size: 11px; font-family: monospace; }
/* Advanced mode button */
.vv-adv-mode-btn { border: 1px solid #555; color: #999; transition: background 0.15s, color 0.15s, border-color 0.15s; }
.vv-adv-mode-btn:hover { border-color: #888; color: #fff; }
.vv-adv-mode-btn.vv-adv-mode-on { background: #1565c0; border-color: #1565c0; color: #fff; }
/* Script browser in suggestions panel */
.vv-sb-desc { font-size: 12px; color: #888; margin: 4px 0 6px; line-height: 1.5; }
.vv-sb-child-desc { margin-left: 16px; }
.vv-sb-hdr { font-family: monospace; font-size: 11px; color: #666; white-space: pre-wrap;
word-break: break-word; background: none; border: none; margin: 4px 0 8px;
padding: 0; line-height: 1.5; border-left: 2px solid #222; padding-left: 8px; }
.vv-sb-full { font-family: monospace; font-size: 11px; color: #888; white-space: pre-wrap;
word-break: break-word; background: #0a0a0a; border: 1px solid #222;
border-radius: 3px; margin: 6px 0 8px; padding: 8px 10px; line-height: 1.5;
max-height: 480px; overflow-y: auto; }
.vv-sb-child-block { border-top: 1px solid #1a1a1a; margin-top: 8px; padding-top: 8px; }
.vv-sb-child-name { display: flex; align-items: center; gap: 6px; margin-bottom: 3px; flex-wrap: wrap; }
/* README content display */
.vv-readme-body { font-family: monospace; font-size: 11px; color: #777; white-space: pre-wrap;
word-break: break-word; background: none; border: none; margin: 0;
padding: 0 4px; line-height: 1.6; }
/* Script browser tree rows */
#vv-sb-tree { padding: 0; }
.vv-sb-entry { }
.vv-sb-row { display: flex; align-items: center; gap: 6px; padding: 5px 6px;
cursor: pointer; border-radius: 3px; user-select: none;
transition: background 0.1s, box-shadow 0.1s; }
.vv-sb-row:hover { background: rgba(255,255,255,0.04); }
.vv-sb-selected { background: rgba(100,149,237,0.14) !important;
outline: 1px solid rgba(100,149,237,0.4);
box-shadow: inset 3px 0 0 #6495ed; }
.vv-sb-orch-row { border-bottom: 1px solid #1c1c1c; }
.vv-sb-child-row { padding-left: 2px; }
.vv-sb-expand { width: 14px; flex-shrink: 0; color: #555; font-size: 10px; text-align: center; }
.vv-sb-expand:hover { color: #aaa; }
.vv-sb-leaf { cursor: default; pointer-events: none; }
.vv-sb-name { flex: 1; font-size: 13px; color: #b0c4d0; min-width: 0;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vv-sb-orch-row .vv-sb-name { font-weight: bold; color: #9ab; }
.vv-sb-children { padding-left: 10px; }
.vv-sb-child-indent { width: 14px; flex-shrink: 0; color: #333; font-size: 11px;
text-align: center; pointer-events: none; }
.vv-sb-badge { flex-shrink: 0; font-size: 11px; }
.vv-sb-cron { color: #555; background: #111; border: 1px solid #222;
padding: 0 4px; border-radius: 2px; font-size: 10px;
font-family: monospace; flex-shrink: 0; }
.vv-sb-status { font-size: 10px; flex-shrink: 0; }
/* Script info content area */
.vv-si-hdr { font-family: monospace; font-size: 12px; color: #4d894d; white-space: pre-wrap;
word-break: break-word; background: none; border: none; margin: 0;
padding: 8px 12px; line-height: 1.6; }
.vv-si-src { font-family: monospace; font-size: 11px; white-space: pre-wrap;
word-break: break-word; background: none; border: none; margin: 0;
padding: 8px 12px; line-height: 1.5; }
/* Cog settings button next to script name */
.vv-cog-btn { cursor:pointer; color:#444; font-size:.85em; margin-right:4px;
line-height:1; vertical-align:middle; user-select:none; }
.vv-cog-btn:hover { color:#aaa; }
/* Enriched script info blocks (advanced mode: header + docs + config) */
.vv-sinfo-block { border-top: 1px solid #222; padding: 0; }
.vv-sinfo-block:first-child { border-top: none; }
.vv-sinfo-lbl { font-size: 10px; font-weight: bold; text-transform: uppercase;
letter-spacing: .06em; color: #555; padding: 6px 12px 2px; }
/* Syntax highlight tokens — dark theme (VSCode-inspired) */
.vv-hl-sep { color: #2d2d2d; }
.vv-hl-shebang { color: #4a4a4a; }
.vv-hl-hash { color: #4a7340; }
.vv-hl-comment { color: #6a9955; }
.vv-hl-section { color: #9cdcfe; font-weight: bold; letter-spacing: 0.04em; }
.vv-hl-key { color: #9cdcfe; }
.vv-hl-value { color: #ce9178; }
.vv-hl-cron { color: #d7ba7d; font-weight: bold; }
.vv-hl-text { color: #4d894d; }
.vv-hl-keyword { color: #569cd6; }
.vv-hl-builtin { color: #4ec9b0; }
.vv-hl-string { color: #ce9178; }
.vv-hl-var { color: #d7ba7d; }
.vv-hl-number { color: #b5cea8; }
.vv-hl-op { color: #808080; }
/* Log panel */
.vv-log-panel { margin-top: 10px; border-top: 1px solid #333; padding-top: 8px; }
.vv-log-toolbar { display: flex; justify-content: space-between; align-items: center;
flex-wrap: wrap; gap: 4px; margin-bottom: 4px; }
.vv-log-ts { font-size: 11px; color: #666; }
.vv-log-pre { background: #0d0d0d; border: 1px solid #333; border-radius: 4px;
padding: 10px 12px; margin: 0; font-family: monospace; font-size: 12px;
color: #ccc; white-space: pre-wrap; word-break: break-all;
max-height: 340px; overflow-y: auto; line-height: 1.5;
scroll-behavior: auto; }
/* Toggle switch */
.vv-toggle { position: relative; display: inline-block; width: 36px; height: 20px; flex-shrink: 0; }
.vv-toggle input { opacity: 0; width: 0; height: 0; }
.vv-slider { position: absolute; inset: 0; background: #444; border-radius: 20px; cursor: pointer;
transition: 0.2s; }
.vv-slider:before { content: ''; position: absolute; width: 14px; height: 14px; left: 3px; bottom: 3px;
background: #fff; border-radius: 50%; transition: 0.2s; }
.vv-toggle input:checked + .vv-slider { background: #4caf50; }
.vv-toggle input:checked + .vv-slider:before { transform: translateX(16px); }
/* Config editor */
#vv-conf-tabs { display: flex; gap: 4px; margin-bottom: 8px; }
.vv-conf-tab { padding: 4px 12px; text-decoration: none; color: #aaa;
border: 1px solid #444; border-radius: 4px; font-size: 13px; }
.vv-conf-tab.active { color: #fff; background: #333; border-color: #666; }
#vv-conf-editor { width: 100%; min-height: 500px; background: #111; color: #ddd;
border: 1px solid #444; padding: 12px; font-family: monospace;
font-size: 13px; line-height: 1.5; border-radius: 4px; box-sizing: border-box; resize: vertical; }
#vv-conf-actions { margin-top: 8px; display: flex; align-items: center; gap: 10px; }
#vv-conf-actions button { padding: 6px 18px; background: #4caf50; border: none;
color: #fff; border-radius: 4px; cursor: pointer; font-size: 14px; }
#vv-conf-actions button:hover { background: #388e3c; }
#vv-conf-status { font-size: 13px; color: #aaa; }
/* Docs */
#vv-docs { display: flex; gap: 16px; }
#vv-docs-sidebar { width: 220px; flex-shrink: 0; }
#vv-docs-sidebar h3 { font-size: 12px; text-transform: uppercase; color: #888; margin: 0 0 8px; }
#vv-docs-sidebar ul { list-style: none; padding: 0; margin: 0; }
#vv-docs-sidebar li { margin: 2px 0; }
#vv-docs-sidebar a { display: block; padding: 3px 8px; font-size: 12px; color: #aaa;
text-decoration: none; border-radius: 3px; }
#vv-docs-sidebar a:hover { background: #222; color: #fff; }
#vv-docs-sidebar a.active { background: #333; color: #fff; }
#vv-docs-content { flex: 1; min-width: 0; }
.vv-doc-body { background: #1a1a1a; border: 1px solid #444; border-radius: 6px;
padding: 20px; line-height: 1.7; }
.vv-doc-body h1, .vv-doc-body h2, .vv-doc-body h3 { color: #ddd; }
.vv-doc-body code { background: #111; padding: 1px 5px; border-radius: 3px; font-size: 12px; }
.vv-doc-body pre { background: #111; padding: 12px; border-radius: 4px; overflow-x: auto; }
.vv-doc-hint { font-size: 12px; color: #666; margin-top: 8px; }
/* Media stream sessions */
.vv-stream-servers { display: flex; justify-content: space-between; align-items: center;
flex-wrap: nowrap; gap: 8px; margin-bottom: 10px; }
/* Left side: server badges + media type */
.vv-stream-left { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; flex-shrink: 0; }
/* Right side: device / resolution / codec chip groups */
.vv-stream-right { display: flex; align-items: center; gap: 0; flex-wrap: nowrap; flex-shrink: 0; }
.vv-server-badge { background: #333; border: 1px solid #555; color: #aaa;
font-size: 10px; padding: 1px 7px; border-radius: 10px; }
.vv-server-badge-sm { font-size: 9px; padding: 1px 6px; flex-shrink: 0; }
/* Chip groups — server badge row */
.vv-device-sep { color: #2a2a2a; font-size: 12px; margin: 0 2px; user-select: none; }
/* shared chip geometry */
.vv-device-chip,
.vv-chip-device, .vv-chip-res, .vv-chip-codec, .vv-chip-mtype {
font-size: 10px; padding: 1px 7px; border-radius: 10px;
white-space: nowrap; border: 1px solid; }
/* media type — purple */
.vv-chip-mtype { color: #9a6abf; background: #1a0d2e; border-color: #3a1e55; }
/* device — blue */
.vv-chip-device { color: #5a9ec8; background: #0d1e2e; border-color: #1c3f5c; }
/* resolution — teal */
.vv-chip-res { color: #4aaa9a; background: #0b1e1c; border-color: #1a4540; }
/* codec — amber */
.vv-chip-codec { color: #b38820; background: #201500; border-color: #483300; }
/* legacy fallback */
.vv-device-chip { color: #666; background: #181818; border-color: #252525; }
/* chip group wrappers — inline so they flow with the badge row */
.vv-chip-group-device, .vv-chip-group-res, .vv-chip-group-codec { display: contents; }
.vv-stream-empty { color: #555; font-style: italic; font-size: 12px; margin: 4px 0; }
.vv-stream-empty span { font-size: 11px; color: #444; }
.vv-stream-row { margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #282828; }
.vv-stream-row:last-child { margin-bottom: 0; padding-bottom: 0; border-bottom: none; }
.vv-stream-top { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
.vv-stream-icon { font-size: 10px; color: #888; flex-shrink: 0; }
.vv-stream-title { flex: 1; font-size: 12px; color: #ddd; font-weight: 500;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vv-stream-meta { display: flex; gap: 10px; flex-wrap: wrap; font-size: 10px;
color: #666; margin-bottom: 5px; }
.vv-stream-user { color: #888; }
.vv-stream-client { color: #555; }
.vv-stream-method { font-weight: 500; }
.vv-stream-time { color: #555; margin-left: auto; }
.vv-stream-bar { height: 3px; background: #1a1a1a; border-radius: 2px; overflow: hidden; }
.vv-stream-bar div { height: 100%; border-radius: 2px; transition: width 0.4s; }
/* Live var substitution colours */
code.vv-live-var { color: #4caf50; background: #0d1f0d; }
code.vv-unknown-var { color: #ff9800; background: #1f130d; }
/* ── Arrange mode ─────────────────────────────────────────────────────────── */
.vv-drag-handle { cursor: grab; color: #555; font-size: 14px; padding: 0 5px 0 0; user-select: none; flex-shrink: 0; }
.vv-drag-handle:hover { color: #888; }
.vv-drag-handle:active { cursor: grabbing; color: #aaa; }
/* Dragged item: semi-transparent with a dashed ring so you still see what you grabbed */
.vv-drag-ghost { opacity: 0.4; outline: 1px dashed #555; border-radius: 4px; }
/* Drop insertion line: blue glow to match selection accent */
.vv-drop-line { height: 2px; background: #6495ed; border-radius: 2px; margin: 2px 0;
pointer-events: none; box-shadow: 0 0 6px rgba(100,149,237,0.7); }
.vv-arrange-active .vv-children { min-height: 28px; border: 1px dashed #2a2a2a; border-radius: 4px;
padding: 4px 2px; transition: border-color 0.12s, background 0.12s; }
/* Drop targets use blue to match selection theme */
.vv-arrange-active .vv-children.vv-drop-target { border-color: #6495ed; background: rgba(100,149,237,0.07); }
.vv-arrange-btn-active { background: #1a3a1e !important; color: #4caf50 !important; border-color: #2d5c33 !important; }
/* Grabbing cursor while dragging anything */
.vv-is-dragging, .vv-is-dragging * { cursor: grabbing !important; }
.vv-arrange-save-btn { background: #1a3a1e; border-color: #2d5c33; color: #4caf50; }
.vv-arrange-save-btn:hover { background: #22502a; }
#vv-arrange-btn { background: #7b1fa2; border-color: #7b1fa2; }
#vv-arrange-btn:hover { background: #4a148c; border-color: #4a148c; }
/* ── Arrange workspace panel ─────────────────────────────────────────────── */
.vv-arrange-ws-hdr { font-size: 11px; font-weight: bold; color: #888; text-transform: uppercase;
letter-spacing: 0.6px; margin-bottom: 8px; display: flex; align-items: center; gap: 8px; }
#vv-pending-badge { background: #ff9800; color: #000; font-size: 10px; padding: 1px 7px;
border-radius: 10px; font-weight: bold; }
.vv-arrange-pending-hdr { font-size: 10px; color: #555; text-transform: uppercase; letter-spacing: 0.5px;
margin-bottom: 5px; }
.vv-pending-row { display: flex; align-items: center; gap: 8px; padding: 3px 0;
font-size: 11px; border-bottom: 1px solid #1e1e1e; }
.vv-pending-script { color: #ddd; font-weight: 500; }
.vv-pending-arrow { color: #555; font-size: 10px; }
.vv-library-zone { border: 1px dashed #2a2a2a; border-radius: 4px; padding: 6px;
min-height: 60px; transition: border-color 0.12s, background 0.12s; }
.vv-library-zone.vv-drop-target { border-color: #ef5350; background: rgba(239,83,80,0.09);
box-shadow: inset 0 0 8px rgba(239,83,80,0.08); }
.vv-arrange-drop-hint { font-size: 10px; color: #444; text-align: center; padding: 2px 0 7px; }
.vv-lib-card { background: #1c1c1c; border: 1px solid #2e2e2e; border-radius: 3px;
padding: 4px 8px; margin-bottom: 4px; cursor: grab; display: flex;
align-items: center; justify-content: space-between; gap: 8px; }
.vv-lib-card:hover { border-color: #444; }
.vv-lib-card.vv-drag-ghost { opacity: 0.35; }
.vv-lib-card-name { font-size: 16px; color: #ccc; font-weight: bold; }
.vv-lib-card-path { font-size: 10px; color: #444; font-family: monospace; }
/* ── Custom script folders ────────────────────────────────────────────────── */
.vv-folder-group { margin-bottom: 1px; }
.vv-folder-row { display: flex; align-items: center; gap: 6px; padding: 3px 6px;
cursor: pointer; border-radius: 3px; color: #888; font-size: 12px; user-select: none; }
.vv-folder-row:hover { background: #1e1e1e; }
.vv-folder-chevron { font-size: 10px; color: #555; width: 10px; flex-shrink: 0; }
.vv-folder-name { flex: 1; font-weight: 500; color: #aaa; }
.vv-folder-count { font-size: 10px; color: #555; background: #1c1c1c;
padding: 0 5px; border-radius: 8px; border: 1px solid #2a2a2a; }
.vv-folder-children { padding-left: 12px; min-height: 4px; }
.vv-folder-children.vv-drop-target { background: rgba(100,149,237,0.07); border-radius: 4px;
outline: 1px dashed rgba(100,149,237,0.4); }
.vv-folder-new-row { display: flex; align-items: center; gap: 6px; padding: 4px 6px; }
.vv-new-folder-btn { background: #0277bd !important; border: none !important; color: #fff !important; }
.vv-new-folder-btn:hover { background: #01579b !important; }
/* ── Snapshot footer (right panel status bar) ────────────────────────────── */
.vv-snap-footer { display: flex !important; flex-direction: row !important;
align-items: center !important; justify-content: center; gap: 20px; flex-wrap: wrap; }
.vv-snap-item { display: flex; align-items: center; gap: 7px; }
.vv-snap-label { font-size: 12px; color: #555; text-transform: uppercase; letter-spacing: 0.05em; }
.vv-snap-bar { width: 90px; height: 8px; background: #222; border-radius: 4px; overflow: hidden; flex-shrink: 0; }
.vv-snap-bar span { display: block; height: 100%; border-radius: 4px; width: 0;
transition: width 0.5s, background-color 0.5s; }
.vv-snap-val { font-size: 15px; color: #aaa; font-family: monospace; min-width: 36px; }
.vv-snap-div { color: #333; font-size: 15px; }
.vv-snap-state { font-size: 15px; font-weight: bold; }
#vv-snap-partner { font-size: 15px; color: #666; }
.vv-snap-media { font-size: 15px; color: #666; }
/* ── Containers and VMs card ──────────────────────────────────────────────── */
.vv-df-section-hdr { font-size: 10px; font-weight: bold; color: #555; text-transform: uppercase;
letter-spacing: 0.08em; padding: 4px 2px 5px; border-bottom: 1px solid #222;
margin-bottom: 4px; }
.vv-df-empty { font-size: 12px; color: #555; font-style: italic; padding: 4px 2px 8px; }
.vv-df-vm-row { display: flex; align-items: center; gap: 8px; padding: 5px 2px;
border-bottom: 1px solid #1a1a1a; }
.vv-df-vm-row:last-of-type { border-bottom: none; }
.vv-df-vm-icon { font-size: 15px; line-height: 1; flex-shrink: 0; }
.vv-df-vm-meta { font-size: 10px; color: #555; }
.vv-df-cols { display: flex; gap: 10px; align-items: flex-start; }
.vv-df-col { flex: 1; min-width: 0; }
#vv-docker-folders-body { overflow-x: hidden; }
.vv-df-folder { border-bottom: 1px solid #1a1a1a; }
.vv-df-folder:last-child { border-bottom: none; }
.vv-df-folder-hdr { display: flex; align-items: center; gap: 6px; padding: 5px 4px;
cursor: pointer; user-select: none; border-radius: 3px; }
.vv-df-folder-hdr:hover { background: rgba(255,255,255,0.03); }
.vv-df-chevron { color: #555; font-size: 10px; width: 10px; flex-shrink: 0; }
.vv-df-fname { flex: 1; font-size: 12px; color: #aaa; font-weight: 500;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vv-df-folder-body { padding-left: 10px; padding-bottom: 3px; }
.vv-df-container { display: flex; align-items: center; gap: 7px; padding: 3px 6px;
cursor: pointer; border-radius: 3px; user-select: none; }
.vv-df-container:hover { background: rgba(255,255,255,0.04); }
.vv-df-active { background: rgba(100,149,237,0.1) !important;
outline: 1px solid rgba(100,149,237,0.35);
box-shadow: inset 3px 0 0 #6495ed; }
.vv-df-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.vv-df-cname { flex: 1; font-size: 12px; color: #ccc;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vv-df-status { font-size: 10px; color: #555; flex-shrink: 0; white-space: nowrap; }
.vv-df-actions { display: flex; gap: 6px; padding: 3px 6px 5px 24px; flex-wrap: wrap; }
/* ── Rsync standalone controls ────────────────────────────────────────────── */
.vv-rsync-location { width: 120px; flex-shrink: 1; min-width: 60px; font-size: 11px; font-family: monospace;
background: #111; border: 1px solid #333; color: #aaa;
padding: 2px 6px; border-radius: 3px; }
.vv-rsync-location:focus { border-color: #555; outline: none; }
.vv-rsync-location::placeholder { color: #444; }
.vv-script-args { flex: 1 1 0; min-width: 60px; max-width: 180px; font-size: 11px; font-family: monospace;
background: #111; border: 1px solid #2a2a2a; color: #aaa;
padding: 2px 6px; border-radius: 3px;
opacity: 0.3; transition: opacity 0.15s; }
.vv-script-args:hover, .vv-script-args:focus { opacity: 1; border-color: #555; outline: none; }
.vv-script-args::placeholder { color: #3a3a3a; }
.vv-rsync-save-btn { background: #1a2e1a; border-color: #2a4a2a; color: #6aaa6a; }
.vv-rsync-save-btn:hover { background: #22382a; }
/* ── AI profile picker ────────────────────────────────────────────────────────
Global rather than emitted with the chat widget, because both surfaces that show it are
styled differently in every other respect: the Monitor card renders a full transcript from
include/ai_chat.php, the Scheduler dock renders its own one-line bar and loads none of that
stylesheet. The picker is the one control they share, so it lives where .vv-ai-dock-* already
does instead of being emitted twice. */
/* ── Profile picker ─────────────────────────────────────────────────────── */
/* The chip borrows the Scheduler dock's language deliberately — same muted blue, same weight,
same 10px — so the two surfaces read as one control that happens to appear in two places. */
.vv-ai-profiles { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.vv-ai-picker { position:relative; flex-shrink:0; }
.vv-ai-chip { display:inline-flex; align-items:center; gap:5px; font-size:10px; color:#5a7a8a;
background:#12191d; border:1px solid #24343d; border-radius:3px; padding:3px 8px;
cursor:pointer; font-family:inherit; white-space:nowrap; }
.vv-ai-chip:hover { color:#8fb0c4; border-color:#2d4a6a; }
.vv-ai-chip-c { color:#3a5a6a; font-size:8px; }
.vv-ai-picker.open .vv-ai-chip { color:#8fb0c4; border-color:#2d4a6a; }
.vv-ai-picker.open .vv-ai-chip-c { transform:rotate(180deg); }
.vv-ai-menu { display:none; position:absolute; top:calc(100% + 4px); left:0; z-index:60;
min-width:250px; background:#0e0e0e; border:1px solid #2a2a2a; border-radius:5px;
box-shadow:0 6px 18px rgba(0,0,0,.55); overflow:hidden; }
.vv-ai-picker.open .vv-ai-menu { display:block; }
.vv-ai-opt { display:block; width:100%; text-align:left; background:none; border:none;
border-bottom:1px solid #1a1a1a; padding:7px 10px; cursor:pointer;
font-family:inherit; }
.vv-ai-opt:last-child { border-bottom:none; }
.vv-ai-opt:hover { background:#152238; }
.vv-ai-opt.active { background:#12191d; }
.vv-ai-opt.active .vv-ai-opt-l { color:#9bd; }
.vv-ai-opt-l { display:block; font-size:11px; color:#b8b8b8; }
.vv-ai-opt-h { display:block; font-size:10px; color:#4a4a4a; line-height:1.4; margin-top:2px; }
.vv-ai-prof-hint { font-size:10px; color:#4a4a4a; flex:1; min-width:150px; }