/* Cookbook (including What Fits?), deep research, memory, settings and admin.
*
* This is an eagerly loaded tail segment of the app stylesheet cascade. The
* former static/style.css now lives in the numbered 00-17 fragments; these
* three pre-existing panel files follow those fragments without changing
* their relative order. Nothing here is lazy-loaded.
*
* LOAD ORDER IS PART OF THE CONTRACT. static/index.html and the PRECACHE list
* in static/sw.js must agree:
* 1. static/css/00-tokens.css through static/css/17-research.css,
* in numeric order
* 2. static/css/documents-gallery-editor.css
* 3. static/css/email-calendar-notes-tasks.css
* 4. static/css/cookbook-research-memory-settings.css
*
* Rules in this file retain their existing cascade position. Shared rules
* formerly kept in static/style.css now live in the numbered fragments.
*/
/* Cookbook already shows its own running/served-status dot
(#cookbook-notif-dot, toggled with .cookbook-notif-active on the
button). Don't stack the tabbed-down pulse on top of it — the two
dots overlap. Suppress the minimized dot while the status dot is up. */
.list-item.rail-minimized.cookbook-notif-active::after { display: none; }
@keyframes research-done-pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.5); opacity: 0.7; }
}
#cookbook-gguf-delete-overlay {
background:rgba(0,0,0,0.5);
backdrop-filter:blur(4px);
pointer-events:auto !important;
z-index:99999 !important;
position:fixed !important;
inset:0 !important;
}
.cookbook-gguf-delete-actions {
flex-wrap:wrap;
}
/* ── Processing pulse animation (reused by session-star) ── */
@keyframes research-pulse {
0%, 100% { opacity: 0.3; transform: scale(0.8); }
50% { opacity: 1; transform: scale(1.2); }
}
@keyframes memory-synapse-pulse {
0%, 100% { opacity: 0.35; transform: scale(1); }
50% { opacity: 0.65; transform: scale(1.02); }
}
.memory-add-input::placeholder {
color: color-mix(in srgb, var(--fg) 40%, transparent);
}
.memory-toolbar-toggle:has(input:not(:checked)) > span {
text-decoration: line-through;
text-decoration-color: color-mix(in srgb, var(--fg) 30%, transparent);
}
.memory-sort-label { white-space: nowrap; }
.memory-sort-btn[aria-expanded="true"] .memory-sort-caret { transform: rotate(180deg); }
@keyframes memory-synapse-sweep {
/* Sweep traverses left → right in the first ~12% of the cycle (≈0.7s of
a 6.2s loop), then waits offscreen. */
0% { --sweep: -20%; }
12% { --sweep: 120%; }
13%, 100% { --sweep: 120%; }
}
.memory-item-edit-input:focus {
outline: none;
}
.memory-edit-cat-select:focus {
outline: none;
}
.memory-add-row .memory-edit-cat-select:focus {
border-color: var(--red);
}
.memory-item-source::before,
.memory-item-time::before {
content: '\00b7 ';
}
.adm-check-dot::after {
content: '';
position: absolute;
left: 50%;
top: 45%;
width: 5px;
height: 2.5px;
border-left: 1.5px solid #fff;
border-bottom: 1.5px solid #fff;
transform: translate(-50%, -50%) rotate(-45deg) scale(0);
transform-origin: center;
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.adm-model-row:active .adm-check-dot {
transform: scale(0.9);
}
.adm-cb-hidden:checked + .adm-check-dot::after {
transform: translate(-50%, -50%) rotate(-45deg) scale(1);
}
.adm-cb-hidden:checked + .adm-endpoint-select-dot::after {
display: none;
}
/* Most recently added endpoint — brief accent glow so the user can
spot the new row immediately after Adding / Find. Fades out cleanly. */
@keyframes adm-ep-just-added-glow {
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent, var(--red)) 55%, transparent); background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent); }
60% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent, var(--red)) 0%, transparent); background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent); }
100% { box-shadow: 0 0 0 0 transparent; background: transparent; }
}
.adm-section-toggle:focus-visible { outline: 1px solid var(--red); outline-offset: 1px; }
/* Collapsed: hide the form body and point the caret right. */
.adm-add-section.collapsed .admin-model-form { display: none; }
.adm-add-section.collapsed .adm-section-caret { transform: rotate(-90deg); }
.adm-quickstart-section {
margin-top: 7px;
}
.adm-quickstart-section:not(.collapsed) .adm-quickstart-toggle {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.adm-quickstart-section.collapsed .adm-quickstart-body { display: none; }
.adm-quickstart-section.collapsed .adm-section-caret { transform: rotate(-90deg); }
.adm-provider-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@keyframes admin-spin {
to { transform: rotate(360deg); }
}
.admin-rag-item .admin-btn-delete { font-size: 10px; padding: 2px 6px; }
/* Cookbook's cached-model list: NO inner-scroll cap. Two nested scroll
surfaces (this + the outer .admin-card) trapped the wheel so an expanded
serve panel couldn't be reached on tall content. Let the outer
.admin-card (overflow-y:auto) be the single scroll surface. */
.hwfit-cached-list {
max-height: none !important;
overflow-y: visible !important;
}
/* The Servers list reuses .memory-toolbar for layout but must grow with every
added server — the 120px cap above was clipping manually-added servers. */
.memory-toolbar.cookbook-servers-toolbar {
max-height: none;
overflow: visible;
}
#hwfit-cached-list {
position: relative;
top: -6px;
}
#hwfit-server-select,
#hwfit-search {
position: relative;
top: -2px !important;
}
#hwfit-search {
height: 30px;
min-height: 30px;
font-family: inherit;
font-size: 11px;
background-image: url("data:image/svg+xml;utf8,");
background-repeat: no-repeat;
background-position: 9px center;
padding-left: 28px;
}
.cookbook-body::-webkit-scrollbar {
width: 4px;
}
.cookbook-body::-webkit-scrollbar-thumb {
background: color-mix(in srgb, var(--fg) 15%, transparent);
border-radius: 4px;
}
.cookbook-body::-webkit-scrollbar-track {
background: transparent;
padding: 4px 0;
}
#hwfit-dl-server.cookbook-server-select-colored,
#hwfit-server-select.cookbook-server-select-colored,
#hwfit-cache-server.cookbook-server-select-colored,
#hwfit-deps-server.cookbook-server-select-colored {
color: var(--cookbook-server-color, var(--fg));
border-color: color-mix(in srgb, var(--cookbook-server-color) 55%, var(--border));
background-color: color-mix(in srgb, var(--cookbook-server-color) 12%, var(--bg));
}
.cookbook-output-wrap:hover .cookbook-output-kill { opacity: 0.7; }
.cookbook-tab-error-dot { --notif-glow: var(--color-error, #f44); }
/* Brief highlight on the matched task card when jumping from the
running pill, so the user can spot it among a long list. */
.cookbook-task-flash {
animation: cookbook-task-flash-anim 1.6s ease-out;
}
@keyframes cookbook-task-flash-anim {
0% { box-shadow: 0 0 0 2px var(--accent, var(--red)); }
100% { box-shadow: 0 0 0 2px transparent; }
}
/* Cookbook header "downloading" status label sits 2px too far left
against the rest of the cookbook chrome — nudge it right. */
#cookbook-bg-status {
left: 2px;
}
/* Keep both GPU action labels on one line — they wrapped to two rows on
mobile, which looked broken. */
.cookbook-gpu-probe, .cookbook-gpu-clear { white-space: nowrap; }
#cookbook-hf-latest-list .cookbook-hf-link:hover {
opacity: 1;
}
#cookbook-hf-latest-list {
min-height: 0;
max-height: min(52vh, 480px);
overflow-y: auto;
overflow-x: hidden;
overscroll-behavior: contain;
scrollbar-width: thin;
}
.cookbook-section-header .cookbook-section-title {
flex: 1;
}
.cookbook-section-header:hover .cookbook-section-chevron { opacity: 1; }
.cookbook-slot-wrap:hover .cookbook-slot-del { opacity: 1; }
/* Keep dependency pills visually centered against the package description.
The row itself stays stable; only the controls move down slightly. */
.cookbook-dep-row > .cookbook-dep-tag {
transform: translateY(2px);
}
/* Conditional line under the Download h2: only when the section is folded
(collapsed). When expanded, the body content provides separation; the
underline reads as clutter. */
#cookbook-dl-tab-fold { border-bottom: none !important; padding-bottom: 0 !important; }
#cookbook-dl-tab-fold.is-folded {
border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent) !important;
padding-bottom: 6px !important;
}
/* Smooth open/close for the Direct Download body — replaces the
instant display:none toggle with a max-height + opacity slide so
auto-fold and auto-expand don't feel jarring. 1200px is a safe
upper bound for the body height; the slide only fires up to the
actual content height because content stops there. */
#cookbook-dl-tab-fold-body {
overflow: hidden;
max-height: 1200px;
opacity: 1;
transition: max-height 0.28s ease, opacity 0.18s ease, margin 0.28s ease;
}
#cookbook-dl-tab-fold-body.is-folded {
max-height: 0;
opacity: 0;
margin-top: 0;
margin-bottom: 0;
pointer-events: none;
}
.cookbook-dep-installed-btn .cookbook-dep-installed-label { padding: 0 8px; }
.cookbook-dep-installed-btn:hover { filter: brightness(1.15); }
.hwfit-serve-topline .hwfit-serve-runtime-note {
flex: 1 1 auto;
min-width: 0;
}
/* Expanded serve panel — make sure it can be scrolled past when it
grows taller than the visible viewport. Caps panel height to viewport
minus chrome and gives it its own internal scroll, so the surrounding
cached-list scroll isn't the only escape route. */
.hwfit-cached-item .hwfit-serve-panel {
max-height: calc(100vh - 180px);
overflow-y: auto;
overscroll-behavior: contain;
}
@media (max-width: 768px) {
.hwfit-cached-item .hwfit-serve-panel {
max-height: calc(100svh - 120px);
}
.hwfit-serve-preset-row {
justify-content: flex-end;
margin: -2px 0 0;
}
.hwfit-serve-topline {
flex-wrap: wrap;
}
.hwfit-serve-topline .hwfit-serve-runtime-note {
flex-basis: 100%;
}
.hwfit-serve-topline .hwfit-serve-runtime-text {
white-space: normal;
}
}
.hwfit-serve-row label select,
.hwfit-serve-row label input {
display: block;
margin-top: 2px;
}
.hwfit-extra-env-label {
grid-column: 1 / -1;
}
.hwfit-serve-panel[data-backend-active="vllm"] .hwfit-extra-env-label {
grid-column: 2 / -1;
}
.hwfit-sf.hwfit-sf-full { grid-column: 1 / -1; }
.hwfit-spec-tokens-bare::-webkit-outer-spin-button,
.hwfit-spec-tokens-bare::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
display: none;
}
.hwfit-numstep .hwfit-spec-tokens::-webkit-outer-spin-button,
.hwfit-numstep .hwfit-spec-tokens::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.hwfit-numstep-btn:focus { outline: none; }
.hwfit-spec-group:has(input[type="checkbox"]:not(:checked)) .hwfit-spec-method,
.hwfit-spec-group:has(input[type="checkbox"]:not(:checked)) .hwfit-spec-tokens,
.hwfit-spec-group:has(input[type="checkbox"]:not(:checked)) .hwfit-numstep {
opacity: 0.45;
pointer-events: none;
}
.hwfit-serve-extra {
margin-bottom: 6px;
}
.hwfit-serve-cmd-details {
margin: 4px 0 0;
}
.hwfit-serve-cmd-summary::-webkit-details-marker {
display: none;
}
.hwfit-serve-cmd-summary::after {
content: '';
margin-left: auto;
width: 0;
height: 0;
border-left: 4px solid currentColor;
border-top: 3px solid transparent;
border-bottom: 3px solid transparent;
opacity: 0.6;
transform: rotate(0deg);
transition: transform 0.18s ease;
}
.hwfit-serve-cmd-details[open] > .hwfit-serve-cmd-summary::after {
transform: rotate(90deg);
}
.hwfit-serve-cmd-details[open] > .hwfit-serve-cmd-summary {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
/* Same z-index treatment as .cookbook-task-dropdown — cookbook modal's
auto-stack climbs past low values; popups append to body and need to
sit above the modal regardless. */
:where(.hwfit-cached-dropdown,
.cookbook-gpu-split-menu,
.cookbook-launch-actions-menu,
.cookbook-saved-menu,
.cookbook-dep-menu) {
z-index: 10000;
}
.hwfit-serve-cmd-wrap .hwfit-serve-cmd {
/* Just enough breathing room so a cursor at line-end doesn't actually
touch the Copy icon — text otherwise uses the full width of the box. */
padding-right: 32px;
}
.hwfit-serve-cmd-details[open] .hwfit-serve-cmd {
border-color: color-mix(in srgb, var(--accent, var(--red)) 48%, var(--border));
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent, var(--red)) 16%, transparent),
0 0 12px color-mix(in srgb, var(--accent, var(--red)) 13%, transparent);
}
.cookbook-gpu-split .cookbook-gpu-split-main {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.cookbook-gpu-split .cookbook-gpu-split-arrow {
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
border-left-width: 0 !important;
padding: 0 6px !important;
min-width: 24px;
}
.cookbook-task[data-type="download"][data-status="running"] .cookbook-task-type[data-type="download"] {
background: color-mix(in srgb, var(--color-accent, #00aaff) 18%, transparent);
color: var(--color-accent, #00aaff);
}
@keyframes cookbook-wave-pulse {
0%, 100% { opacity: 0.45; }
50% { opacity: 1; }
}
.cookbook-task[data-status="done"] .cookbook-task-check-ico { display: inline; }
.cookbook-task[data-status="done"] .cookbook-task-clear-ico { display: none; }
.cookbook-task-header {
cursor: pointer;
}
.cookbook-env-row > .cookbook-field-label {
flex: 1 1 140px;
min-width: 120px;
}
.cookbook-extra-label { grid-column: 1 / -1; }
.cookbook-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--fg);
border: 2px solid var(--panel);
box-shadow: 0 1px 4px rgba(0,0,0,0.3);
cursor: pointer;
transition: transform 0.1s, box-shadow 0.1s;
}
.cookbook-slider:hover::-webkit-slider-thumb {
transform: scale(1.15);
box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent, var(--red)) 20%, transparent);
}
.cookbook-slider:active::-webkit-slider-thumb {
transform: scale(1.25);
box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent, var(--red)) 25%, transparent);
}
.cookbook-slider::-moz-range-thumb {
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--fg);
border: 2px solid var(--panel);
box-shadow: 0 1px 4px rgba(0,0,0,0.3);
cursor: pointer;
}
.cookbook-slider::-moz-range-progress {
background: var(--accent, var(--red));
height: 6px;
border-radius: 4px;
}
.cookbook-slider::-webkit-slider-runnable-track {
height: 6px;
border-radius: 4px;
}
.cookbook-slider::-moz-range-track {
height: 6px;
border-radius: 4px;
background: color-mix(in srgb, var(--fg) 12%, transparent);
}
.cookbook-checkbox:checked::after {
transform: translateX(14px);
}
/* Sliders span full row for alignment */
.cookbook-field-slider {
grid-column: 1 / -1;
}
.cookbook-diag-body {
margin-top: 7px;
}
/* Add Server collapsible */
.cookbook-server-details {
margin: 4px 0 6px;
}
.cookbook-server-toggle::-webkit-details-marker { display: none; }
.cookbook-srv-status:hover { transform: scale(1.3); }
@keyframes cookbook-srv-pulse {
0%, 100% { opacity: 0.45; }
50% { opacity: 1; }
}
.cookbook-server-entry::after {
content: '';
position: absolute;
inset: 0;
z-index: 2;
border-radius: inherit;
padding: 1px;
pointer-events: none;
background: linear-gradient(
to right,
transparent 0%,
transparent calc(var(--sweep, -20%) - 8%),
color-mix(in srgb, var(--cookbook-server-color, var(--red)) 85%, transparent) var(--sweep, -20%),
transparent calc(var(--sweep, -20%) + 8%),
transparent 100%
);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
animation: memory-synapse-sweep 6.2s linear infinite;
animation-delay: 0.4s;
}
#cookbook-servers-list .cookbook-server-entry:nth-child(2n)::after { animation-duration: 7.4s; animation-delay: 1.6s; }
#cookbook-servers-list .cookbook-server-entry:nth-child(3n)::after { animation-duration: 8.8s; animation-delay: 3.2s; }
#cookbook-servers-list .cookbook-server-entry:nth-child(5n)::after { animation-duration: 9.3s; animation-delay: 4.7s; }
#cookbook-servers-list .cookbook-server-entry:nth-child(7n)::after { animation-duration: 5.5s; animation-delay: 2.3s; }
.cookbook-server-entry:hover::after { animation: none; opacity: 0; }
@media (prefers-reduced-motion: reduce) {
.cookbook-server-entry::after { animation: none; opacity: 0; }
}
/* Keep the row inside the card on narrow screens — the fixed-width host/path
inputs would otherwise overflow the card's background to the right. */
@media (max-width: 768px) {
.cookbook-server-row .cookbook-srv-host,
.cookbook-server-row .cookbook-srv-path { flex: 1 1 100% !important; width: auto !important; min-width: 0 !important; }
}
.cookbook-server-row .cookbook-srv-name { width: 60px; flex-shrink: 0; flex-grow: 0; }
.cookbook-server-row select.cookbook-srv-color {
display: none !important;
}
.cookbook-server-row .cookbook-srv-color-btn:focus-visible {
outline: 2px solid color-mix(in srgb, var(--cookbook-server-color, var(--accent, var(--red))) 70%, transparent);
outline-offset: 2px;
}
.cookbook-server-row .cookbook-srv-host { flex: 1; min-width: 100px; }
.cookbook-server-row .cookbook-srv-host[readonly] { opacity: 0.4; cursor: default; }
.cookbook-server-row .cookbook-srv-port { width: 40px; flex-shrink: 0; flex-grow: 0; }
.cookbook-server-row .cookbook-srv-env { width: 65px; flex-shrink: 0; flex-grow: 0; }
.cookbook-server-row .cookbook-srv-path { flex: 1; min-width: 80px; }
.cookbook-server-row .cookbook-srv-status { width: 8px; height: 8px; align-self: center; }
/* Brief highlight on the serve command box when a saved config is loaded, so
the click clearly registers (loading is otherwise silent). */
.cookbook-cmd-flash {
animation: cookbookCmdFlash 0.6s ease;
}
.hwfit-toolbar:has(.hwfit-hw-manual-btn) {
gap: 8px;
}
.hwfit-toolbar .hwfit-server-select { min-width: 70px; flex-shrink: 0; }
.hwfit-toolbar .hwfit-usecase { min-width: 70px; flex-shrink: 0; }
.hwfit-toolbar .hwfit-engine { min-width: 0; width: 86px; flex-shrink: 0; font-size: 10px; }
.hwfit-toolbar .hwfit-search { flex: 1; min-width: 80px; }
.hwfit-advanced-panel .hwfit-engine-menu {
min-width: 132px;
}
/* Lower-opacity "Search models..." placeholder so it reads as a hint, not
a label — matches the muted form-field feel of the inline filters. */
.hwfit-search::placeholder { opacity: 0.5; }
.hwfit-search::-webkit-input-placeholder { opacity: 0.5; }
.hwfit-search::-moz-placeholder { opacity: 0.5; }
.hwfit-quant-wrap .hwfit-quant,
.hwfit-engine-wrap .hwfit-engine {
/* Make room for the ? on the right edge, in addition to the native chevron. */
padding-right: 32px;
}
.hwfit-toolbar .hwfit-host { width: 110px; flex-shrink: 0; }
.hwfit-env-row .hwfit-envtype { width: auto; min-width: 70px; flex-shrink: 0; }
.hwfit-env-row .hwfit-envpath { flex: 1; min-width: 100px; }
.hwfit-env-row .hwfit-gpus { width: 90px; flex-shrink: 0; }
.hwfit-hw-chip-dismiss { cursor: pointer; }
.hwfit-hw-chip-row[data-hw-chip="gpu"] .hwfit-hw-chip-x {
transform: translateY(-3px);
}
.hwfit-manual-panel .hwfit-hw-manual-save,
.hwfit-manual-panel .hwfit-hw-manual-clear {
/* -3 (was -2) — 1px more up to optically align with the labeled
inputs to their left. */
transform: translateY(-3px);
}
.cookbook-model-list-fade {
animation: cookbook-model-list-fade-in 180ms ease-out both;
}
@keyframes cookbook-model-list-fade-in {
from { opacity: 0; transform: translateY(3px); }
to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
.cookbook-model-list-fade,
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:not(.hwfit-header) { animation: none; }
}
.hwfit-row:has(.hwfit-dl-dot):hover { opacity: 0.9; }
.hwfit-row:has(.hwfit-dl-dot) .hwfit-dl-dot { opacity: 1; }
.hwfit-header .hwfit-name { font-size: 9px; }
.hwfit-sortable { cursor: pointer; user-select: none; }
.hwfit-c-params { width: 42px; }
.hwfit-c-quant { width: 52px; }
.hwfit-c-vram { width: 42px; }
.hwfit-c-ctx { width: 32px; }
.hwfit-c-speed { width: 44px; }
.hwfit-c-mode { width: 72px; }
@keyframes hwfit-panel-in {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
.hwfit-cb { cursor: pointer; }
.settings-appearance-panel {
flex-direction: column;
}
@keyframes cookbook-modal-enter {
0% {
opacity: 0;
transform: translateY(12px) scale(0.94);
filter: saturate(0.85);
}
65% {
opacity: 1;
transform: translateY(-2px) scale(1.012);
filter: saturate(1.05);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
filter: none;
}
}
@keyframes cookbook-modal-enter-mobile {
0% { opacity: 0; transform: translateY(100%); }
100% { opacity: 1; transform: translateY(0); }
}
/* Cookbook Serve Advanced fold — wraps the rarely-touched tuning rows
(KV/Attention/Swap/Env for vLLM, llama.cpp batch/cache/split, VRAM
monitor, speculative, extra args). Matches the existing .hwfit-panel-
advanced look: muted-gray label, no caps, no letter-spacing, no
warning-y opacity. Content flows into the parent's existing scroll
surface (no inner max-height) and inner rows reset their margin so
stacking gaps don't double when the fold opens. */
/* Styled to match the Add Models page collapsible sections
(.adm-section-toggle) — same border/background/caret pattern, so the
two folds across the app read consistently. */
details.hwfit-serve-advanced {
margin-top: 8px;
overflow: visible;
}
details.hwfit-serve-advanced > summary.hwfit-serve-advanced-summary {
cursor: pointer;
user-select: none;
list-style: none;
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--fg);
opacity: 0.8;
border: 1px solid var(--border);
border-radius: 6px;
padding: 6px 9px;
background: color-mix(in srgb, var(--fg) 4%, transparent);
transition: border-color 0.12s, background 0.12s, opacity 0.12s, border-radius 0s;
}
details.hwfit-serve-advanced > summary.hwfit-serve-advanced-summary::-webkit-details-marker {
display: none;
}
details.hwfit-serve-advanced > summary.hwfit-serve-advanced-summary:hover {
opacity: 1;
border-color: var(--red);
background: color-mix(in srgb, var(--red) 8%, transparent);
}
/* Caret on the right, rotates open/closed. SVG-style rectangles via
borders keep this glyph-free + crisp at small sizes. */
details.hwfit-serve-advanced > summary.hwfit-serve-advanced-summary::after {
content: '';
margin-left: auto;
width: 0;
height: 0;
border-left: 4px solid currentColor;
border-top: 3px solid transparent;
border-bottom: 3px solid transparent;
opacity: 0.6;
transform: rotate(90deg);
transition: transform 0.18s ease;
}
details.hwfit-serve-advanced:not([open]) > summary.hwfit-serve-advanced-summary::after {
transform: rotate(0deg);
}
/* Body rows below the header — tight rhythm so the fold doesn't
feel airy. The cookbook modal's existing .cookbook-body is the
scroll surface; nothing inside the fold should add its own scroll. */
details.hwfit-serve-advanced[open] > summary.hwfit-serve-advanced-summary {
margin-bottom: 6px;
}
details.hwfit-serve-advanced > .hwfit-serve-row.hwfit-vram-monitor .hwfit-vram-readout {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
details.hwfit-serve-advanced > .hwfit-serve-row label select,
details.hwfit-serve-advanced > .hwfit-serve-row label input {
margin-top: 1px;
}
details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="vllm_attn_backend"]) {
position: relative;
left: -63px;
}
details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="vllm_block_size"]) {
position: relative;
left: -75px;
}
details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="swap"]) {
position: relative;
left: -73px;
}
details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="vllm_kv_cache_dtype"]) {
position: relative;
left: 2px;
}
@media (max-width: 768px) {
details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="vllm_block_size"]) {
left: 1px;
}
details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="swap"]) {
left: -3px;
}
details.hwfit-serve-advanced > .hwfit-serve-checks .hwfit-sf-cb {
flex: 1 1 100%;
}
}
/* Hide the native number-input spinner arrows (e.g. SMTP/IMAP Port) — they
render unstyled and ugly. Field still accepts only numbers. */
.settings-input[type="number"]::-webkit-outer-spin-button,
.settings-input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.settings-input[type="number"] {
-moz-appearance: textfield;
appearance: textfield;
}
.cookbook-output-wrap:hover .cookbook-output-copy {
opacity: 0.5;
}
.cookbook-output-wrap .cookbook-output-copy:hover {
opacity: 1;
}
#cookbook-dl-btn-search {
position: relative;
top: -4px;
padding-top: 0;
padding-bottom: 0;
height: 28px;
}
#cookbook-dl-btn {
position: relative;
top: -4px;
}
.research-synapse-compact .rs-stage { height: 288px; }
@keyframes rs-live {
0%, 100% { opacity: 0.45; }
50% { opacity: 1; }
}
.research-synapse .rs-guide ellipse {
fill: none;
stroke: color-mix(in srgb, var(--fg) 7%, transparent);
stroke-width: 1;
stroke-dasharray: 2 7;
}
.research-synapse .rs-edge-tone-1 { stroke: color-mix(in srgb, var(--gold, #d4a72c) 45%, var(--border)); }
.research-synapse .rs-edge-tone-2 { stroke: color-mix(in srgb, #5aa7c7 42%, var(--border)); }
.research-synapse .rs-edge.rs-edge-firing {
stroke: var(--accent, var(--red));
stroke-width: 2;
opacity: 1;
filter: drop-shadow(0 0 4px var(--accent, var(--red)));
animation: rs-fire 1.1s ease-out;
}
@keyframes rs-fire {
0% { stroke-dasharray: 4 200; stroke-dashoffset: 200; opacity: 0; }
20% { opacity: 1; }
100% { stroke-dasharray: 200 4; stroke-dashoffset: -200; opacity: 0.55; }
}
.research-synapse .rs-node-root {
fill: var(--accent, var(--red));
stroke: var(--accent, var(--red));
filter: drop-shadow(0 0 5px color-mix(in srgb, var(--accent, var(--red)) 65%, transparent));
}
.research-synapse .rs-root-halo {
fill: none;
stroke: color-mix(in srgb, var(--accent, var(--red)) 28%, transparent);
stroke-width: 1;
}
/* Sub & leaf nodes stay inside the accent palette so the whole graph
reads as one organism. Leaves are softer/lower-contrast dots that
float around their sub — not green-on-stem (which read as palm fronds). */
.research-synapse .rs-node-sub {
fill: color-mix(in srgb, var(--accent, var(--red)) 10%, var(--bg));
stroke: color-mix(in srgb, var(--accent, var(--red)) 70%, var(--fg));
}
.research-synapse .rs-node-sub.rs-node-tone-1 {
fill: color-mix(in srgb, var(--gold, #d4a72c) 11%, var(--bg));
stroke: color-mix(in srgb, var(--gold, #d4a72c) 76%, var(--fg));
}
.research-synapse .rs-node-sub.rs-node-tone-2 {
fill: color-mix(in srgb, #5aa7c7 11%, var(--bg));
stroke: color-mix(in srgb, #5aa7c7 72%, var(--fg));
}
.research-synapse .rs-node-leaf {
stroke: color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
fill: color-mix(in srgb, var(--accent, var(--red)) 42%, var(--bg));
}
.research-synapse .rs-node-leaf.rs-node-tone-1 { fill: color-mix(in srgb, var(--gold, #d4a72c) 48%, var(--bg)); }
.research-synapse .rs-node-leaf.rs-node-tone-2 { fill: color-mix(in srgb, #5aa7c7 48%, var(--bg)); }
.research-synapse .rs-source-node { cursor: help; }
@keyframes rs-hub-expand {
0% { filter: drop-shadow(0 0 0 transparent); }
35% { filter: drop-shadow(0 0 7px var(--accent, var(--red))); }
100% { filter: drop-shadow(0 0 0 transparent); }
}
@keyframes rs-pop {
0% { transform: scale(0); opacity: 0; }
60% { transform: scale(1.25); opacity: 1; }
100% { opacity: 1; }
}
@keyframes rs-pulse {
0% { transform: scale(1); opacity: 0.65; }
100% { transform: scale(5); opacity: 0; }
}
.research-synapse .rs-meta .rs-sep { display: none; }
.research-synapse.rs-complete .rs-pulse { animation: none; opacity: 0; }
.research-synapse.rs-complete .rs-node-root {
fill: var(--color-success, #4caf50);
stroke: var(--color-success, #4caf50);
}
@media (max-width: 480px) {
.research-synapse .rs-meta { grid-template-columns: 1fr auto auto; row-gap: 6px; }
.research-synapse .rs-meta .rs-status { display: none; }
}
.hwfit-cached-menu-btn svg { fill: currentColor; }
.settings-email-preferences-card > h2 {
display: none;
}
[data-settings-panel="email"] > .settings-email-preferences-card {
padding-top: 0;
}
.cookbook-task-menu-btn {
top: 0 !important;
transform: translateY(2px) scale(1.15) !important;
}
/* Settings "+ Add server" matches the model-dir "+ Add" path button (22px). */
#cookbook-server-add.cal-add-btn-text {
height: 28px;
min-width: 62px;
border-radius: 14px;
padding: 0 12px 0 8px;
position: relative;
top: 1px;
font-size: 12px;
}
@keyframes research-pane-enter {
from { opacity: 0; }
to { opacity: 1; }
}
.research-pane-header:active { cursor:grabbing; }
.research-pane-header h4 {
margin: 0;
font-size: 1rem;
font-weight: 600;
color: var(--red);
display: inline-flex;
align-items: center;
gap: 6px;
}
.research-tab-panel.hidden,
.research-tab-panel[hidden] { display: none; }
.research-tab-heading h2 {
margin: 0;
font-size: 14px;
font-weight: 600;
line-height: 1;
}
.research-tab-panel:not([data-research-panel="research"]) .research-jobs-list {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
}
/* Hide the in-body "Research" subtitle on mobile to save vertical space —
the toolbar/header carries enough context already. */
/* Match the .admin-card h2 styling used in Documents/Library so the
"Research" sub-title reads the same as those modals' titles. */
.research-new-job h2 {
font-size: 14px;
font-weight: 600;
letter-spacing: 0;
}
.research-new-job > .research-new-job-desc {
margin: 6px 0 0;
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
@media (max-width: 600px) {
/* Keep the "Research" title visible on mobile (matches the Cookbook tab
titles, which show on mobile). It used to be hidden here. */
.research-new-job h2 {
font-size: 14px;
}
}
.research-setting:has(#research-model) .research-setting-label {
position: relative;
top: 1px;
}
.research-setting:has(#research-rounds) select,
.research-setting:has(#research-category) select {
position: relative;
top: -4px;
}
.research-setting:has(#research-rounds) .research-setting-label,
.research-setting:has(#research-category) .research-setting-label {
position: relative;
top: 4px;
}
.research-setting select {
font-size: 11px; padding: 4px 6px;
background: var(--bg);
color: var(--fg);
border: 1px solid var(--border); border-radius: 4px;
}
.research-picker-btn:focus-visible,
.research-picker-option:focus-visible {
outline: 2px solid color-mix(in srgb, var(--accent, var(--red)) 65%, transparent);
outline-offset: 1px;
}
.research-picker-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.research-picker.open .research-picker-btn > svg { transform: rotate(180deg); }
.research-picker-menu[hidden] { display: none; }
.research-picker-option > span:nth-child(2):not(.research-picker-check),
.research-picker-option > span:first-child:not(.research-picker-icon) {
overflow: hidden; text-overflow: ellipsis;
}
.research-picker-option.active .research-picker-check { opacity: 1; }
.research-run-mode-popover.rrm-up { transform-origin: bottom right; }
.research-run-mode-popover .research-run-mode-row:hover {
background: color-mix(in srgb, var(--accent-primary, var(--red)) 14%, transparent);
}
.research-job-card.done { cursor:pointer; }
/* Past (library) research used to be dimmed to 0.65 which read as "greyed out".
They now look the same as the rest — folding them under "Past research"
handles the de-clutter instead. */
.research-job-card.done.from-library { opacity:1; }
.research-job-thumb-frame[role="button"] { cursor: pointer; }
.research-job-thumb-frame[role="button"]:hover {
outline: 1px solid color-mix(in srgb, var(--accent, var(--red)) 65%, transparent);
outline-offset: 1px;
}
.research-job-report-body h1, .research-job-report-body h2, .research-job-report-body h3 {
color: var(--cat-color, var(--accent, var(--fg)));
}
.research-hero::after {
content: ''; position: absolute; right: -40px; top: -40px;
width: 160px; height: 160px; border-radius: 50%;
background: radial-gradient(circle, color-mix(in srgb, var(--cat-color, var(--accent)) 15%, transparent) 0%, transparent 60%);
pointer-events: none;
}
.research-hero-icon svg { width: 100%; height: 100%; }
/* Product: callout-style bullets */
.research-body-product ul { list-style: none; padding-left: 0; }
.research-body-product ul li {
padding: 6px 10px 6px 28px; margin: 4px 0;
border-left: 2px solid color-mix(in srgb, #5b8abf 40%, transparent);
background: color-mix(in srgb, #5b8abf 4%, transparent);
border-radius: 0 4px 4px 0; position: relative;
}
.research-body-product ul li::before {
content: '▸'; position: absolute; left: 10px;
color: #5b8abf; font-weight: bold;
}
/* Comparison: styled table */
.research-body-comparison table {
width: 100%; border-collapse: collapse; margin: 12px 0;
border: 1px solid color-mix(in srgb, #e5a33a 25%, transparent);
border-radius: 6px; overflow: hidden;
}
.research-body-comparison th {
background: color-mix(in srgb, #e5a33a 18%, transparent);
color: #e5a33a; font-weight: 700;
padding: 8px 12px; text-align: left;
border-bottom: 2px solid color-mix(in srgb, #e5a33a 40%, transparent);
}
.research-body-comparison td {
padding: 8px 12px;
border-bottom: 1px solid color-mix(in srgb, #e5a33a 12%, transparent);
}
.research-body-comparison tr:nth-child(even) td {
background: color-mix(in srgb, #e5a33a 3%, transparent);
}
/* How-to: big numbered steps */
.research-body-howto ol { counter-reset: howto-step; list-style: none; padding-left: 0; }
.research-body-howto ol > li {
counter-increment: howto-step; position: relative;
padding: 10px 12px 10px 52px; margin: 8px 0;
background: color-mix(in srgb, #82c882 5%, transparent);
border-radius: 8px; border-left: 2px solid #82c882;
}
.research-body-howto ol > li::before {
content: counter(howto-step);
position: absolute; left: 10px; top: 14px;
width: 30px; height: 30px; border-radius: 50%;
background: #82c882; color: white;
font-weight: 700; font-size: 13px;
display: flex; align-items: center; justify-content: center;
box-shadow: 0 2px 6px color-mix(in srgb, #82c882 40%, transparent);
}
/* Landscape: section banners */
.research-body-landscape h3 {
padding: 8px 14px;
background: linear-gradient(90deg, color-mix(in srgb, #a07ae0 15%, transparent), transparent);
border-left: 3px solid #a07ae0;
border-radius: 0 6px 6px 0;
margin: 14px 0 8px;
}
/* Fact-check: verdict emphasis */
.research-body-factcheck blockquote {
border-left: 3px solid var(--red);
background: color-mix(in srgb, var(--red) 6%, transparent);
padding: 10px 14px; margin: 10px 0;
border-radius: 0 6px 6px 0;
}
.research-body-factcheck strong {
color: var(--red);
padding: 1px 6px; border-radius: 4px;
background: color-mix(in srgb, var(--red) 12%, transparent);
}
.research-job-cancel:hover,
.research-job-remove:hover,
.research-job-report-link:hover { opacity:1; }
.research-synapse-toggle:hover,
.research-synapse-toggle.active { opacity:1; }
.research-job-synapse-host.synapse-collapsed { display:none; }
.research-job-summary .research-job-thumb-frame {
transform: translateY(-4px);
}
.research-job-summary .research-job-failnote {
margin: 2px 0 0;
}
.research-job-thumb-frame::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
background: radial-gradient(ellipse at center,
transparent 42%,
color-mix(in srgb, var(--accent, var(--red)) 28%, transparent) 100%);
opacity: 0.75;
}
/* Push the first dim (dismiss/delete) button — and everything after it — to
the right edge of the actions row. `:first-of-type` would match the very
first