Files
odysseus/static/css/10-cookbook.css
T
Léo a4715b77a5 refactor(css): name the fragments by area instead of by number
style-part-01..08 were arbitrary 6,000-line cuts, so the names said nothing
and a reader had no way to guess which file held a rule.

The original stylesheet turns out to be roughly area-grouped already, so
cutting where the content changes rather than every 6,000 lines produces
boundaries worth naming. Classifying every top-level rule by selector prefix
and smoothing over a 40-rule window gives 17 stable runs: agent-chat, compare,
memory, documents, admin-settings, skills, gallery, cookbook, tasks,
image-editor, email, notes, calendar, research.

The numeric prefix stays because load order is load-bearing, and it also
disambiguates the areas that appear twice: the original interleaves, and
merging non-adjacent blocks of the same area would reorder the cascade.

The names describe where a file sits, not a claim that it holds every rule for
that area or only rules for it. The header in each file says so, because that
is the misreading this naming invites.

Reconstruction was checked against lab's style.css before rewriting: identical
after whitespace normalisation, all 47,528 lines accounted for. The computed
style baseline still reproduces exactly.
2026-09-29 17:50:04 +02:00

4325 lines
122 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.
/* 10-cookbook - part of the former static/style.css.
*
* The stylesheet is split by area, but the split is mechanical: rules were
* moved verbatim and NOT regrouped. The numeric prefix is the load order, and
* that order is what preserves the cascade. A rule sits in the file its
* original position fell into, so an area's rules are not all in its file and
* a file is not only that area's rules.
*
* Do not reorder the <link> tags in static/index.html, and do not move a rule
* between files: with 1,151 redeclarations and 1,886 !important in this
* cascade, either can change which declaration wins.
*
* static/index.html and the PRECACHE list in static/sw.js must agree on this
* order. tests/test_static_stylesheet_manifest.py and
* tests/test_css_computed_style_snapshot.py fail if that drifts.
*/
.scoreboard-table tbody tr:hover {
background: color-mix(in srgb, var(--fg) 5%, transparent);
}
#scoreboard-overlay svg {
color: var(--accent, var(--red));
}
/* ── Compare search results ── */
.compare-search-results {
display: flex;
flex-direction: column;
gap: 2px;
}
.compare-search-result {
padding: 6px 0;
border-bottom: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
}
.compare-search-result:last-child {
border-bottom: none;
}
.search-result-title {
color: var(--color-accent);
text-decoration: none;
font-weight: 500;
font-size: 0.9em;
display: block;
line-height: 1.3;
}
.search-result-title:hover {
color: var(--color-link-hover);
text-decoration: underline;
}
.search-result-snippet {
color: color-mix(in srgb, var(--fg) 70%, transparent);
font-size: 0.82em;
line-height: 1.4;
margin-top: 2px;
}
.search-result-url {
color: color-mix(in srgb, var(--fg) 35%, transparent);
font-size: 0.75em;
margin-top: 3px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* ── Cookbook Tool ── */
/* ── Cookbook ── */
.cookbook-serve-preset {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
border: 1px solid var(--border);
border-radius: 6px;
background: color-mix(in srgb, var(--fg) 3%, transparent);
margin-bottom: 3px;
cursor: pointer;
transition: background 0.15s;
}
.cookbook-serve-preset:hover { background: color-mix(in srgb, var(--fg) 8%, transparent); }
.cookbook-serve-preset-name {
font-size: 11px;
font-weight: 500;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.cookbook-serve-preset-meta {
font-size: 9px;
font-family: 'Fira Code', monospace;
color: var(--fg-muted);
opacity: 0.5;
}
.cookbook-serve-preset-launch {
background: none;
border: none;
color: #50fa7b;
cursor: pointer;
font-size: 10px;
opacity: 0.6;
padding: 0 2px;
position: relative;
top: -4px;
}
.cookbook-serve-preset-launch:hover { opacity: 1; }
.cookbook-serve-preset-rm {
background: none;
border: none;
color: var(--fg-muted);
cursor: pointer;
font-size: 10px;
opacity: 0.3;
padding: 0 2px;
position: relative;
top: -4px;
}
.cookbook-serve-preset-rm:hover { opacity: 0.8; color: var(--red); }
.cookbook-body {
display: flex;
flex-direction: column;
gap: 10px;
overflow-y: auto;
flex: 1;
min-height: 0;
scrollbar-width: thin;
}
#cookbook-modal .modal-content {
display: flex;
flex-direction: column;
overflow: hidden;
}
#cookbook-modal .modal-header {
flex: 0 0 auto;
}
#cookbook-modal .cookbook-body {
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
}
#cookbook-modal .cookbook-group {
min-height: 0;
}
#cookbook-modal .cookbook-group > .admin-card {
min-height: 0;
/* Let .cookbook-body be the SINGLE scroll surface. Nesting another
overflow:auto here trapped the wheel inside the cached-list when a
serve panel expanded — the page couldn't scroll past the panel's
bottom (Launch button got hidden). */
overflow: visible !important;
}
#cookbook-modal .cookbook-section-body {
min-height: 0;
}
.cookbook-group {
display: flex;
flex-direction: column;
gap: 8px;
flex: 1;
min-height: 0;
}
/* Cards — match admin-card */
.cookbook-card {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 12px;
display: flex;
flex-direction: column;
gap: 6px;
}
.cookbook-card-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.cookbook-card-header-actions { display: flex; gap: 4px; }
.cookbook-card-title { font-size: 13px; font-weight: 600; }
.cookbook-card-desc {
font-size: 11px;
color: color-mix(in srgb, var(--fg) 50%, transparent);
}
/* Fields grid */
.cookbook-fields {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: 6px;
margin-top: 4px;
}
.cookbook-field-label {
display: flex;
flex-direction: column;
font-size: 11px;
color: color-mix(in srgb, var(--fg) 60%, transparent);
gap: 3px;
}
/* Inputs — match admin-add-form input */
.cookbook-field-input {
padding: 5px 8px;
font-size: 12px;
font-family: inherit;
background: var(--bg);
color: var(--fg);
border: 1px solid var(--border);
border-radius: 6px;
outline: none;
}
.cookbook-field-input:focus { border-color: var(--red); }
#hwfit-dl-server, #hwfit-usecase, #hwfit-server-select, #hwfit-cache-server, #serve-sort { height: 28px; width: 88px; }
/* Serve tab — match Documents sizing, but leave room for the active "Cancel"
label and center it vertically so the descenders don't clip. */
#hwfit-cache-select {
min-width: 58px;
height: 28px;
position: relative;
top: -3px;
display: inline-flex;
align-items: center;
justify-content: center;
line-height: 1;
}
/* Command preview */
.cookbook-cmd-preview {
margin: 4px 0 0;
padding: 8px 10px;
background: color-mix(in srgb, var(--fg) 4%, transparent);
border: 1px solid var(--border);
border-radius: 6px;
font-family: 'Fira Code', monospace;
font-size: 11px;
white-space: pre-wrap;
word-break: break-all;
color: var(--fg);
}
/* Buttons — match admin-btn-sm */
.cookbook-actions { display: flex; gap: 6px; margin-top: 2px; }
.cookbook-btn {
padding: 4px 10px;
min-width: 54px;
text-align: center;
font-size: 11px;
font-family: inherit;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg);
color: var(--fg);
cursor: pointer;
transition: all 0.15s;
}
.cookbook-btn:hover {
background: var(--border);
border-color: var(--accent);
}
.cookbook-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.cookbook-run-btn {
background: var(--accent);
color: var(--panel);
border-color: var(--accent);
font-weight: 600;
}
.cookbook-run-btn:hover { opacity: 0.85; }
.cookbook-stop-btn {
background: var(--color-error);
border-color: var(--color-error);
}
/* Output */
.cookbook-output-wrap {
position: relative;
margin: 0;
}
.cookbook-output-kill {
position: absolute;
top: 4px;
right: 34px;
width: 22px;
height: 22px;
padding: 0;
background: none;
border: 1px solid transparent;
border-radius: 4px;
color: var(--fg-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
opacity: 0;
transition: opacity .15s, color .15s;
display: flex;
align-items: center;
justify-content: center;
}
.cookbook-output-kill:hover { color: var(--color-error, var(--warn)); border-color: var(--border); }
.cookbook-output-wrap .copy-code {
position: absolute;
top: 6px;
right: 6px;
}
.cookbook-output-wrap:hover .copy-code { opacity: 0.7; }
.cookbook-output-pre {
margin: 0;
padding: 8px 10px;
background: color-mix(in srgb, var(--fg) 4%, transparent);
border: 1px solid var(--border);
border-radius: 6px;
font-family: 'Fira Code', monospace;
font-size: 11px;
white-space: pre-wrap;
word-break: break-all;
max-height: 180px;
overflow-y: auto;
}
.cookbook-output-error { color: var(--color-error); }
/* Downloaded/cached models */
.hwfit-cached-item {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
padding: 6px 8px;
margin: 2px 0;
border-radius: 6px;
border: 1px solid var(--border);
background: color-mix(in srgb, var(--fg) 3%, transparent);
cursor: default;
font-size: 12px;
}
.hwfit-cached-item .hwfit-serve-panel {
flex-basis: 100%;
margin-top: 4px;
}
.hwfit-cached-item:hover {
background: color-mix(in srgb, var(--fg) 6%, transparent);
}
.hwfit-cached-header {
font-size: 10px;
opacity: 0.4;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
border: none !important;
background: none !important;
padding: 2px 8px;
cursor: default !important;
}
.hwfit-cached-header:hover { background: none !important; }
.hwfit-cached-name {
font-weight: 600;
flex-shrink: 0;
}
.hwfit-cached-repo {
flex: 1;
color: var(--fg-muted);
font-size: 11px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.hwfit-cached-size,
.hwfit-cached-files {
font-size: 10px;
color: var(--fg-muted);
white-space: nowrap;
}
.hwfit-cached-serve,
.hwfit-cached-delete {
flex-shrink: 0;
}
.hwfit-cached-delete {
opacity: 0.4;
font-size: 10px;
}
.hwfit-cached-delete:hover {
opacity: 1;
color: var(--color-error, var(--warn));
}
.hwfit-cached-badge {
font-size: 9px;
padding: 1px 6px;
border-radius: 3px;
font-weight: 600;
white-space: nowrap;
}
.hwfit-cached-ready {
background: color-mix(in srgb, var(--color-success, #4caf50) 20%, transparent);
color: var(--color-success, #4caf50);
}
.hwfit-cached-dl {
background: color-mix(in srgb, var(--color-warning, #f0ad4e) 20%, transparent);
color: var(--color-warning, #f0ad4e);
}
/* Cookbook notification dot */
#tool-cookbook-btn {
position: relative;
}
#rail-cookbook {
position: relative;
}
.cookbook-open-loading {
display: inline-flex;
align-items: center;
justify-content: center;
width: 14px;
height: 14px;
flex-shrink: 0;
margin-left: 2px;
color: var(--accent, var(--red));
opacity: 0.9;
}
.cookbook-open-loading .ai-spinner,
.cookbook-open-loading .ai-spinner-whirlpool {
display: inline-flex !important;
width: 14px;
height: 14px;
margin: 0 !important;
gap: 0 !important;
}
.cookbook-open-loading canvas {
display: block;
}
.cookbook-section-loading-wp,
.cookbook-section-loading-wp canvas {
width: 22px !important;
height: 22px !important;
display: block !important;
}
#rail-cookbook .cookbook-open-loading {
position: absolute;
right: 2px;
top: -2px;
width: 12px;
height: 12px;
margin-left: 0;
}
/* The serve-list 'downloading' / 'download stalled' status text sits a hair
low against the rest of the meta line on mobile — nudge it up 2px. */
@media (max-width: 768px) {
.cookbook-dl-status {
position: relative;
top: -2px;
}
}
.cookbook-notif-dot {
width: 6px;
height: 6px;
flex-shrink: 0;
border-radius: 50%;
background: var(--color-success, #4caf50);
/* Drives the breathing glow in the keyframes (matches email's dot). */
--notif-glow: var(--color-success, #4caf50);
animation: cookbook-notif-pulse 2s ease-in-out infinite;
}
.cookbook-notif-dot.cookbook-notif-error {
background: var(--color-error, #f44);
--notif-glow: var(--color-error, #f44);
position: relative;
left: -2px;
top: -1px;
}
.rail-notify-error { color: var(--color-error, #f44) !important; }
.cookbook-tab-error-dot {
display: inline-block; width: 5px; height: 5px; border-radius: 50%;
background: var(--color-error, #f44); margin-left: 4px; vertical-align: middle;
animation: cookbook-notif-pulse 2s ease-in-out infinite;
}
@keyframes cookbook-notif-pulse {
0%, 100% {
opacity: 1;
box-shadow: 0 0 0 0 color-mix(in srgb, var(--notif-glow, var(--accent, var(--red))) 60%, transparent);
}
50% {
opacity: 0.85;
box-shadow: 0 0 6px 2px color-mix(in srgb, var(--notif-glow, var(--accent, var(--red))) 55%, transparent);
}
}
.cookbook-clear-btn {
font-size: 10px !important;
padding: 0 8px !important;
height: 22px !important;
border-radius: 6px !important;
border: 1px solid var(--border) !important;
color: color-mix(in srgb, var(--fg) 40%, transparent) !important;
opacity: 1 !important;
background: none !important;
}
.cookbook-clear-btn:hover {
color: var(--red) !important;
border-color: var(--red) !important;
background: color-mix(in srgb, var(--red) 8%, transparent) !important;
}
.cookbook-bulk-bar {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
margin-bottom: 4px;
background: color-mix(in srgb, var(--red) 6%, transparent);
border: 1px solid color-mix(in srgb, var(--red) 20%, transparent);
border-radius: 6px;
font-size: 11px;
}
.cookbook-bulk-bar.hidden { display: none; }
.serve-select-cb {
width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; cursor: pointer;
background: var(--border); transition: background 0.15s;
align-self: center;
position: relative;
top: -2px;
}
#serve-bulk-bar {
position: relative;
top: -6px;
}
#serve-bulk-bar #serve-bulk-cancel {
top: -2px !important;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 28px;
height: 24px;
line-height: 1;
}
#serve-bulk-bar #serve-bulk-cancel svg {
top: 0;
}
.serve-select-cb.selected { background: var(--red); }
.serve-select-cb:hover { background: color-mix(in srgb, var(--red) 50%, transparent); }
#hwfit-cache-select.active {
background: var(--red);
color: #fff;
border-color: var(--red);
}
.cookbook-serve-dirs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
}
.cookbook-serve-dir-pill {
font-size: 10px;
font-family: 'Fira Code', monospace;
padding: 2px 8px;
border-radius: 4px;
background: color-mix(in srgb, var(--fg) 6%, transparent);
border: 1px solid color-mix(in srgb, var(--fg) 10%, transparent);
color: var(--fg-muted);
letter-spacing: 0.2px;
}
/* Status pills shown inline in a Serve-tab card title (next to the model
name) when the model has a live serve / download task. Shared base
class so "running" and "downloading" sit on the same row with the
same chrome; only color varies. */
.cookbook-serve-running-pill,
.cookbook-serve-downloading-pill {
display: inline-block;
margin-left: 6px;
padding: 1px 7px;
border-radius: 10px;
font-size: 9px;
font-weight: 500;
text-transform: lowercase;
letter-spacing: 0.3px;
vertical-align: 2px;
position: relative;
top: 1px; /* nudged down 2px from the old -1px so it sits
flush with the cap-height of the title text */
}
.cookbook-serve-running-pill {
color: var(--accent, var(--red));
background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
}
.cookbook-serve-downloading-pill {
color: var(--accent, var(--red));
background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
opacity: 0.85;
}
.cookbook-serve-downloading-pill.is-stalled {
/* Stalled downloads stay visible but read as warning, not progress. */
color: var(--fg-muted, #888);
background: color-mix(in srgb, var(--fg-muted, #888) 10%, transparent);
border-color: color-mix(in srgb, var(--fg-muted, #888) 30%, transparent);
opacity: 1;
}
.cookbook-serve-running-pill.is-clickable {
cursor: pointer;
transition: background 0.12s, border-color 0.12s;
}
.cookbook-serve-running-pill.is-clickable:hover {
background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
border-color: color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
}
.cookbook-serve-dir-edit {
font-size: 9px;
color: var(--fg-muted);
opacity: 0.4;
cursor: pointer;
text-decoration: underline;
text-underline-offset: 2px;
}
.cookbook-serve-dir-edit:hover {
opacity: 0.7;
color: var(--accent, var(--red));
}
.cookbook-gpu-group {
display: flex;
gap: 3px;
margin-top: -4px;
}
.cookbook-gpu-btn {
width: 30px; height: 30px;
font-size: 12px;
font-family: 'Fira Code', monospace;
border: 1px solid var(--border);
border-radius: 4px;
background: none;
color: var(--fg-muted);
cursor: pointer;
opacity: 0.4;
transition: all 0.15s;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
}
.cookbook-gpu-btn:hover {
opacity: 0.7;
border-color: var(--fg-muted);
}
.cookbook-gpu-btn.active {
opacity: 1;
background: color-mix(in srgb, var(--fg) 10%, transparent);
border-color: color-mix(in srgb, var(--fg) 55%, transparent);
color: var(--fg);
font-weight: 600;
}
/* Probe annotation states (set by "Free?" button) */
.cookbook-gpu-btn.gpu-free {
opacity: 1;
border-color: #4ade80;
color: #4ade80;
}
.cookbook-gpu-btn.gpu-busy {
opacity: 0.85;
border-color: color-mix(in srgb, var(--red) 70%, transparent);
color: var(--red);
background: color-mix(in srgb, var(--red) 8%, transparent);
}
.cookbook-gpu-btn.gpu-missing {
opacity: 0.2;
text-decoration: line-through;
cursor: not-allowed;
}
/* "Clear Server" lives in the actions row next to "Probe GPUs".
Inherits .cookbook-btn sizing; this rule just adds the destructive
red tint on hover so the "this kills processes" cue stays. */
.cookbook-gpu-clear:hover:not(:disabled) {
color: var(--red);
border-color: color-mix(in srgb, var(--red) 60%, var(--border));
background: color-mix(in srgb, var(--red) 8%, transparent);
}
.cookbook-gpu-clear:disabled { opacity: 0.4; cursor: wait; }
.cookbook-gpu-clear:disabled { opacity: 0.4; cursor: wait; }
/* GPU probe popup — per-GPU process list with kill buttons */
.cookbook-gpu-popup {
/* Fixed positioning (relative to viewport) so we never get pulled into
a scrolling/transform stacking context from an ancestor. Z-index has
to clear the cookbook modal (260) and the rest of the high-z UI
layers (themed-confirm and various overlays sit around 9000-10000). */
position: fixed;
z-index: 10010;
min-width: 280px;
max-width: 420px;
background: var(--panel, #1a1a1a);
border: 1px solid var(--border);
border-radius: 6px;
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
font-family: 'Fira Code', monospace;
font-size: 11px;
color: var(--fg);
}
.cookbook-gpu-popup-head {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 10px;
border-bottom: 1px solid var(--border);
font-weight: 600;
}
.cookbook-gpu-popup-stats {
flex: 1;
font-weight: 400;
color: var(--fg-muted);
font-size: 10px;
}
.cookbook-gpu-popup-close {
background: none;
border: none;
color: var(--fg-muted);
cursor: pointer;
font-size: 14px;
padding: 0 4px;
}
.cookbook-gpu-popup-close:hover { color: var(--fg); }
.cookbook-gpu-popup-body {
padding: 4px 0;
max-height: 320px;
overflow-y: auto;
}
.cookbook-gpu-popup-empty {
padding: 10px;
color: var(--fg-muted);
font-style: italic;
}
.cookbook-gpu-proc {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
}
.cookbook-gpu-proc:hover { background: color-mix(in srgb, var(--fg) 5%, transparent); }
.cookbook-gpu-proc-info {
flex: 1;
display: flex;
gap: 8px;
align-items: center;
min-width: 0;
}
.cookbook-gpu-proc-pid {
color: var(--fg-muted);
width: 56px;
flex-shrink: 0;
}
.cookbook-gpu-proc-name {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
}
.cookbook-gpu-proc-mem {
color: var(--fg-muted);
flex-shrink: 0;
}
.cookbook-gpu-proc-actions {
display: flex;
gap: 4px;
flex-shrink: 0;
}
.cookbook-gpu-kill {
border: 1px solid color-mix(in srgb, var(--red) 60%, transparent);
background: none;
color: var(--red);
border-radius: 3px;
padding: 2px 8px;
cursor: pointer;
font-family: inherit;
font-size: 10px;
}
.cookbook-gpu-kill[data-sig="KILL"] {
background: color-mix(in srgb, var(--red) 12%, transparent);
}
.cookbook-gpu-kill:hover:not(:disabled) {
background: color-mix(in srgb, var(--red) 20%, transparent);
}
.cookbook-gpu-kill:disabled { opacity: 0.4; cursor: wait; }
.cookbook-serve-title {
display: flex;
align-items: center;
gap: 4px;
min-width: 0;
}
.cookbook-serve-title-name {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.cookbook-hf-link {
font-size: 9px;
text-decoration: none;
color: var(--fg-muted);
opacity: 0.5;
padding: 1px 5px;
border: 1px solid color-mix(in srgb, var(--fg) 12%, transparent);
border-radius: 3px;
background: color-mix(in srgb, var(--fg) 4%, transparent);
vertical-align: 1px;
letter-spacing: 0.3px;
font-weight: 600;
flex-shrink: 0;
}
.cookbook-hf-link:hover {
opacity: 0.8;
border-color: var(--accent, var(--red));
color: var(--accent, var(--red));
}
/* In the Trending models list the HF pill is the row's main affordance —
tint it accent so it stands out next to the model name. */
#cookbook-hf-latest-list .cookbook-hf-link {
color: var(--accent, var(--red));
opacity: 0.85;
border-color: color-mix(in srgb, var(--accent, var(--red)) 30%, transparent);
}
/* Running tab sections */
.cookbook-saved-section,
.cookbook-serve-section,
.cookbook-dl-section {
border: 1px solid var(--border);
border-radius: 6px;
margin-bottom: 6px;
overflow: hidden;
}
/* Divider between server-group sections in the Active tab so the
transition between e.g. local + remote-host server blocks reads
visually instead of running into each other. */
.cookbook-group[data-backend-group="Running"] > [class*="cookbook-server-section-"] + [class*="cookbook-server-section-"] {
margin-top: 14px;
padding-top: 14px;
border-top: 1px solid color-mix(in srgb, var(--border) 65%, transparent);
}
.cookbook-section-header {
display: flex;
align-items: center;
gap: 6px;
padding: 7px 10px;
cursor: pointer;
user-select: none;
/* Persistent surface + border so it reads as a clickable bar instead of
blending into the panel background. */
background: color-mix(in srgb, var(--fg) 5%, transparent);
border: 1px solid var(--border);
border-radius: 6px;
transition: background 0.15s, border-color 0.15s;
}
.cookbook-section-header:hover {
background: color-mix(in srgb, var(--fg) 9%, transparent);
border-color: color-mix(in srgb, var(--fg) 22%, transparent);
}
.cookbook-section-header.has-server-color {
color: var(--fg);
border-color: color-mix(in srgb, var(--cookbook-server-accent, var(--cookbook-server-color)) 45%, var(--border));
background: color-mix(in srgb, var(--cookbook-server-accent, var(--cookbook-server-color)) 4%, var(--bg));
box-shadow: inset 6px 0 0 var(--cookbook-server-accent, var(--cookbook-server-color));
}
.cookbook-section-header.has-server-color:hover {
background: color-mix(in srgb, var(--cookbook-server-accent, var(--cookbook-server-color)) 7%, var(--bg));
border-color: color-mix(in srgb, var(--cookbook-server-accent, var(--cookbook-server-color)) 60%, var(--border));
}
.cookbook-section-header.has-server-color .cookbook-section-title,
.cookbook-section-header.has-server-color .cookbook-section-chevron {
color: var(--cookbook-server-accent, var(--cookbook-server-color));
}
.cookbook-section-header.has-server-color .cookbook-srv-status.ok {
background: var(--cookbook-server-accent, var(--cookbook-server-color));
box-shadow: 0 0 0 2px color-mix(in srgb, var(--cookbook-server-accent, var(--cookbook-server-color)) 18%, transparent),
0 0 8px color-mix(in srgb, var(--cookbook-server-accent, var(--cookbook-server-color)) 55%, transparent);
}
.cookbook-section-header .cookbook-clear-btn {
margin-left: 0;
position: relative;
top: -3px;
}
@media (max-width: 768px) {
.cookbook-section-header .cookbook-clear-btn {
top: -3px;
}
.cookbook-task-check {
top: 4px;
}
}
/* "Stop all" sits just left of "Clear finished"; it carries the auto margin so
the pair is pushed together to the right of the section title. */
.cookbook-section-header .cookbook-stop-all-btn {
margin-left: auto;
margin-right: 6px;
position: relative;
top: -3px;
}
.cookbook-stop-all-btn {
font-size: 10px !important;
padding: 0 8px !important;
height: 22px !important;
border-radius: 6px !important;
border: 1px solid var(--border) !important;
color: color-mix(in srgb, var(--fg) 40%, transparent) !important;
opacity: 1 !important;
background: none !important;
}
.cookbook-stop-all-btn:hover {
color: var(--warn, #f0ad4e) !important;
border-color: var(--warn, #f0ad4e) !important;
background: color-mix(in srgb, var(--warn, #f0ad4e) 8%, transparent) !important;
}
.cookbook-section-chevron {
flex-shrink: 0;
opacity: 0.6;
transition: transform 0.15s ease, opacity 0.15s ease;
}
.cookbook-section-body {
padding: 0;
}
.cookbook-saved-toggle-header {
font-size: 10px;
font-weight: 600;
color: var(--fg-muted);
text-transform: uppercase;
letter-spacing: 0.3px;
margin-bottom: 4px;
}
.cookbook-saved-toggle:hover { color: var(--fg); }
.cookbook-saved-item {
margin: 2px 0;
border-radius: 4px;
font-size: 11px;
}
.cookbook-saved-header {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 6px;
border-radius: 4px;
transition: background 0.08s;
}
.cookbook-saved-header:hover {
background: color-mix(in srgb, var(--fg) 5%, transparent);
}
.cookbook-saved-item .hwfit-serve-panel {
margin: 4px 0 4px 6px;
}
.cookbook-saved-host {
font-size: 10px;
color: var(--fg-muted);
font-family: 'Fira Code', monospace;
}
.cookbook-saved-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.cookbook-saved-launch {
font-size: 10px;
padding: 2px 8px;
}
.cookbook-saved-del {
background: none;
border: none;
color: var(--fg-muted);
opacity: 0.3;
cursor: pointer;
font-size: 11px;
padding: 0 2px;
}
.cookbook-saved-del:hover {
opacity: 1;
color: var(--color-error, var(--warn));
}
/* Serve config panel */
.hwfit-serve-panel {
margin-top: 6px;
padding: 10px;
border: 1px solid var(--border);
border-radius: 6px;
background: color-mix(in srgb, var(--fg) 3%, transparent);
flex: 1;
display: flex;
flex-direction: column;
position: relative;
}
.cookbook-serve-slots {
display: flex;
gap: 3px;
justify-content: flex-end;
margin-bottom: 4px;
}
/* When the Save split sits inside Row 1 (next to GPUs), align it with the
input baseline (the row's grid cells stretch top-down; without this the
Save buttons sit above the GPU button group). */
.hwfit-serve-row .cookbook-serve-slots {
align-self: end;
margin-bottom: 4px;
}
.cookbook-slot-btn {
min-width: 22px; height: 28px;
padding: 0 6px;
font-size: 10px; font-weight: 600;
border: 1px solid var(--border);
border-radius: 4px;
background: none;
white-space: nowrap;
max-width: 80px;
overflow: hidden;
text-overflow: ellipsis;
color: var(--fg-muted);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.5;
transition: all 0.15s;
}
.cookbook-slot-btn:hover { opacity: 0.9; border-color: var(--fg-muted); }
/* Saved-configs split button: "Save" + dropdown-arrow joined into one control,
but each segment keeps its own style — Save uses the filled accent
"Done"-button look; the arrow stays a subtle outlined menu trigger. */
.cookbook-saved-split { gap: 0; }
.cookbook-saved-save,
.cookbook-saved-arrow { max-width: none; }
.cookbook-saved-save {
padding: 0 10px;
gap: 4px;
background: var(--bg);
color: var(--fg);
border-color: var(--border);
font-weight: 600;
opacity: 1;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.cookbook-saved-save:hover {
background: var(--border);
border-color: var(--accent);
opacity: 1;
}
.cookbook-saved-arrow {
padding: 0 6px;
background: var(--bg);
color: var(--fg);
border-color: var(--border);
opacity: 1;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-left: none;
}
.cookbook-saved-menu .cookbook-saved-favorite {
border-left: 3px solid var(--red);
background: color-mix(in srgb, var(--red) 4%, transparent);
}
.cookbook-saved-fav-btn {
width: 20px;
height: 20px;
padding: 0;
border: 1px solid transparent;
border-radius: 6px;
background: none;
color: var(--fg-muted);
opacity: 0.55;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.cookbook-saved-fav-btn:hover,
.cookbook-saved-fav-btn.active {
color: var(--red);
opacity: 1;
background: color-mix(in srgb, var(--red) 10%, transparent);
}
.cookbook-saved-fav-badge {
flex-shrink: 0;
background: transparent;
padding: 0;
}
.cookbook-serve-favorite-model {
border-left-color: var(--red) !important;
background: color-mix(in srgb, var(--red) 4%, transparent);
}
.cookbook-serve-fav-badge {
display: inline-flex;
align-items: center;
vertical-align: 1px;
margin-left: 5px;
margin-right: 2px;
background: transparent;
padding: 0;
}
.cookbook-slot-saved { background: color-mix(in srgb, var(--accent) 10%, transparent); border-color: color-mix(in srgb, var(--accent) 30%, transparent); color: var(--accent); }
.cookbook-slot-saved:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
.cookbook-slot-btn.active { opacity: 1; background: var(--accent); color: #fff; border-color: var(--accent);
}
.cookbook-slot-wrap {
position: relative;
display: inline-flex;
}
.cookbook-slot-del {
position: absolute;
top: -5px;
right: -5px;
width: 14px;
height: 14px;
padding: 0;
border: 1px solid var(--border);
border-radius: 50%;
background: var(--bg, #1a1a1a);
color: var(--fg-muted);
font-size: 12px;
line-height: 1;
cursor: pointer;
opacity: 0;
transition: opacity 0.15s, color 0.15s, border-color 0.15s;
display: flex;
align-items: center;
justify-content: center;
}
.cookbook-slot-del:hover {
color: #f44;
border-color: #f44;
}
.cookbook-card-backend { display: none; }
/* Dependencies row — aligned tags */
.cookbook-dep-row {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 10px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--panel);
}
/* Let the deps list fill the available window height instead of being
capped at the default .doclib-grid 400-px box. */
[data-backend-group="Dependencies"] #cookbook-deps-list {
flex: 1;
max-height: none;
}
/* Settings tab: stack the HF token + Servers cards as distinct blocks
(matches the Download tab's block-per-section layout). */
.cookbook-settings-stack {
display: flex;
flex-direction: column;
gap: 10px;
flex: 1;
/* Scroll the whole settings panel so the Servers card can grow to hold every
server (it used to be a cramped internal scroll box that clipped them). */
overflow-y: auto;
min-height: 0;
}
.cookbook-settings-stack.hidden { display: none; }
.cookbook-dep-row.cookbook-dep-blocked { opacity: 0.4; }
.cookbook-dep-info { flex: 1; min-width: 0; }
.cookbook-dep-section {
display: flex;
align-items: baseline;
gap: 8px;
margin: 12px 2px 4px;
}
.cookbook-dep-section-title {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.03em;
}
.cookbook-dep-section-note {
font-size: 10px;
color: color-mix(in srgb, var(--fg) 50%, transparent);
}
.cookbook-dep-subgroup {
margin: 6px 0 8px 12px;
padding: 7px 8px 8px;
border-left: 2px solid color-mix(in srgb, var(--accent, var(--red)) 45%, transparent);
background: color-mix(in srgb, var(--fg) 3%, transparent);
border-radius: 0 6px 6px 0;
display: flex;
flex-direction: column;
gap: 6px;
}
.cookbook-dep-subgroup-title {
display: flex;
align-items: baseline;
gap: 7px;
margin: 0 1px 1px;
font-size: 10.5px;
font-weight: 700;
color: color-mix(in srgb, var(--fg) 78%, transparent);
}
.cookbook-dep-subgroup-title em {
font-size: 9.5px;
font-style: normal;
font-weight: 500;
color: color-mix(in srgb, var(--fg) 48%, transparent);
}
.cookbook-dep-tag {
font-size: 9px;
padding: 0 8px;
border-radius: 4px;
white-space: nowrap;
text-align: center;
min-width: 62px;
box-sizing: border-box;
line-height: 1;
/* Explicit height so the <button> tags (Install / Installed) are the EXACT
same height as the sibling <span> tags — Firefox gives buttons a taller
native box otherwise. Mirrors the server-row tag rule (height:24px). */
height: 24px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.cookbook-dep-target {
border: 1px solid var(--border);
color: color-mix(in srgb, var(--fg) 50%, transparent);
}
.cookbook-dep-cat {
background: color-mix(in srgb, var(--fg) 10%, transparent);
color: color-mix(in srgb, var(--fg) 60%, transparent);
}
/* Rebuild tag — same look as the LLM category tag, sits to its left. */
.cookbook-dep-rebuild {
background: color-mix(in srgb, var(--fg) 10%, transparent);
color: color-mix(in srgb, var(--fg) 75%, transparent);
border: 1px solid color-mix(in srgb, var(--fg) 20%, transparent);
cursor: pointer;
font-family: inherit;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
position: relative;
top: -2px;
}
.cookbook-dep-reinstall {
top: -3px;
}
.cookbook-dep-rebuild:hover {
background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
color: var(--accent, var(--red));
border-color: color-mix(in srgb, var(--accent, var(--red)) 45%, transparent);
}
.cookbook-dep-installed {
background: color-mix(in srgb, var(--green, #50fa7b) 18%, transparent);
color: var(--green, #50fa7b);
border: 1px solid color-mix(in srgb, var(--green, #50fa7b) 35%, transparent);
/* Match Install + Installed ▾ so all three variants align in mixed rows. */
width: 87.7px;
min-width: 87.7px;
padding: 0 10px;
box-sizing: border-box;
}
.cookbook-dep-na {
background: color-mix(in srgb, var(--fg) 8%, transparent);
color: color-mix(in srgb, var(--fg) 60%, transparent);
border: 1px solid color-mix(in srgb, var(--fg) 16%, transparent);
cursor: help;
/* Match other dep tag widths so N/A rows line up with Install / Installed. */
min-width: 75.85px;
padding: 0 10px;
box-sizing: border-box;
}
.cookbook-dep-install {
background: var(--accent, var(--red));
color: #fff;
border: none;
cursor: pointer;
font-family: inherit;
font-weight: 500;
position: relative;
top: -3px;
width: 87.7px;
min-width: 87.7px;
padding: 0 10px;
/* Strip the native button box so it's the same height as the sibling tags
(Firefox renders <button> taller otherwise); height comes from .cookbook-dep-tag. */
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
}
.cookbook-dep-build-deps {
width: auto;
min-width: 87.7px;
max-width: 100%;
white-space: normal;
overflow-wrap: anywhere;
line-height: 1.15;
text-align: center;
height: auto;
min-height: 24px;
}
/* The install state is the terminal control in a dependency row. Keep it on
the far edge even when optional rebuild/category controls are present. */
.cookbook-dep-row > .cookbook-dep-installed,
.cookbook-dep-row > .cookbook-dep-na,
.cookbook-dep-row > .cookbook-dep-install:not(.cookbook-dep-build-deps) {
margin-left: auto;
}
/* Center the "?" glyph inside the help chip. Without text-align it sits 0.5px
left of true center because of the character's natural baseline offset. */
.hwfit-help-chip {
text-align: center;
padding-left: 0.5px;
}
.cookbook-dep-install:hover { opacity: 0.85; }
/* Installed split button: "Installed" label + separator + ▾ caret; clicking it
opens the actions menu (Update). Replaces the old ⋮ button. */
.cookbook-dep-installed-btn {
padding: 0;
width: 87.7px;
min-width: 87.7px;
cursor: pointer;
font-family: inherit;
overflow: hidden;
position: relative;
top: -3px;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
}
.cookbook-dep-installed-btn .cookbook-dep-caret {
display: inline-flex;
align-items: center;
align-self: stretch;
padding: 0 5px;
font-size: 14px;
line-height: 1;
position: relative;
top: 1px;
left: 4px;
opacity: 0.85;
border-left: 1px solid color-mix(in srgb, var(--green, #50fa7b) 35%, transparent);
}
.hwfit-serve-row {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
gap: 6px;
margin-bottom: 6px;
}
/* Tighter min on the core-knobs row only so Dtype / TP / GPU Mem / Max
Seqs fit on one line without crushing Row 1's GPU button strip or
the Backend picker. */
.hwfit-serve-row-core {
grid-template-columns: repeat(auto-fill, minmax(78px, 1fr));
}
/* Save / saved-config split aligned to the right end of Row 1 and
nudged up 4px so it sits flush with the rest of the row. */
.hwfit-serve-row .cookbook-serve-slots {
justify-self: end;
position: relative;
top: -4px;
}
/* The Settings label wraps the Save split — right-align its title text
so the word "Settings" sits above the right-aligned Save button
instead of floating off to the left of its grid cell. 8px right
padding now (16px → 8px) per follow-up tweak, brings the title back
over the actual Save button instead of overshooting it. */
.hwfit-serve-row label:has(> .cookbook-serve-slots) {
grid-column: -2 / -1;
justify-self: end;
text-align: right;
padding-right: 0;
}
.hwfit-serve-row label:has(> .cookbook-serve-slots) > span {
display: inline-block;
position: relative;
left: -33px;
}
.hwfit-serve-topline {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 8px;
margin: 0 0 6px;
}
.hwfit-serve-topline .hwfit-serve-runtime-text {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.hwfit-serve-preset-row {
display: flex;
justify-content: flex-end;
margin: 0;
flex: 0 0 auto;
position: relative;
top: -6px;
}
.hwfit-serve-row label {
font-size: 10px;
color: var(--fg-muted);
white-space: nowrap;
letter-spacing: 0.3px;
}
.hwfit-serve-row-core .hwfit-context-label {
grid-column: span 2;
width: auto;
min-width: 0;
max-width: none;
justify-self: start;
}
.hwfit-context-control {
display: block;
position: relative;
align-items: center;
margin-top: 2px;
width: 100px;
}
.hwfit-context-control .hwfit-sf[data-field="ctx"] {
min-width: 0;
width: 100%;
padding-right: 40px;
}
.hwfit-serve-row-core label:has(.hwfit-sf[data-field="max_seqs"]) {
position: relative;
left: -66px;
}
.hwfit-serve-row-core label:has(.hwfit-sf[data-field="gpu_mem"]) {
position: relative;
left: -64px;
}
.hwfit-serve-row-core .hwfit-gpus-label {
position: relative;
left: -70px;
}
.hwfit-context-calc-btn {
position: absolute;
right: 3px;
top: -4px;
width: 34px;
height: 28px;
min-width: 34px;
padding: 0;
font-size: 10px;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
text-align: center;
overflow: hidden;
white-space: nowrap;
}
.hwfit-context-calc-btn .spinner-whirlpool {
width: 12px !important;
height: 12px !important;
margin: 0 auto !important;
}
.hwfit-context-calc-btn .ai-spinner-whirlpool {
width: 12px !important;
height: 12px !important;
}
@media (min-width: 769px) {
[data-llama-mode-toggle].mode-toggle.mode-toggle-three .mode-toggle-btn > span {
top: -6px !important;
}
.mode-toggle.mode-toggle-three [data-llama-mode="unified"] > span {
font-size: 0;
}
.mode-toggle.mode-toggle-three [data-llama-mode="unified"] > span::after {
content: 'Unif';
font-size: 11px;
}
}
@media (max-width: 768px) {
.hwfit-serve-row-core .hwfit-context-label {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.hwfit-serve-row-core label:has(.hwfit-sf[data-field="max_seqs"]),
.hwfit-serve-row-core label:has(.hwfit-sf[data-field="gpu_mem"]) {
left: 0;
}
details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="vllm_env_preset"]),
details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="extra_env"]) {
grid-column: 1 / -1 !important;
}
.hwfit-context-control {
display: inline-flex;
align-items: center;
gap: 4px;
width: auto;
}
.hwfit-context-control .hwfit-sf[data-field="ctx"] {
width: 100px;
padding-right: 6px;
}
.hwfit-context-calc-btn {
position: relative;
top: -2px;
left: 2px;
right: auto;
flex: 0 0 34px;
width: 34px;
min-width: 34px;
}
}
.hwfit-auto-ctx-note {
display: block;
margin-top: 3px;
font-size: 9px;
opacity: 0.6;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Ctx slider — ported from origin/main. Sits in the Scan/Download toolbar
next to the quant dropdown. Drives _ctxValue() in cookbook-hwfit.js. */
.hwfit-ctx-control {
height: 28px;
min-width: 134px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
gap: 5px;
padding: 0 7px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--bg);
font-size: 11px; /* slightly smaller than .cookbook-field-input so Context reads quieter next to Engine/Quant */
}
.hwfit-ctx-control span {
/* Match Quant/Engine select label style: no uppercase, no letter-spacing. */
text-transform: none;
letter-spacing: 0;
opacity: 0.9;
}
.hwfit-ctx-control > span:first-child {
font-size: 9.5px;
}
/* Editor-style slider (same look as the gallery editor sliders): thin pill
rail that fattens on interaction, circular red thumb that grows on hover. */
.hwfit-ctx-control input[type="range"] {
width: 64px;
min-width: 64px;
height: 4px;
padding: 0;
border: 0;
-webkit-appearance: none;
appearance: none;
/* Hard-coded grey so the rail is GUARANTEED visible regardless of theme —
every theme-derived color we tried (--fg-muted, --border, accent-bg mix)
kept blending into the panel background on at least one theme. */
background: rgba(150, 150, 150, 0.65);
border-radius: 999px;
accent-color: var(--red);
cursor: pointer;
transition: height 0.15s ease, background 0.15s ease;
}
.hwfit-ctx-control input[type="range"]:hover,
.hwfit-ctx-control input[type="range"]:focus,
.hwfit-ctx-control input[type="range"]:active {
background: var(--fg);
}
.hwfit-ctx-control input[type="range"]:hover,
.hwfit-ctx-control input[type="range"]:focus,
.hwfit-ctx-control input[type="range"]:active {
height: 10px;
}
.hwfit-ctx-control input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--red);
border: none;
cursor: pointer;
transition: width 0.12s ease, height 0.12s ease;
}
.hwfit-ctx-control input[type="range"]::-moz-range-thumb {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--red);
border: none;
cursor: pointer;
transition: width 0.12s ease, height 0.12s ease;
}
.hwfit-ctx-control input[type="range"]:hover::-webkit-slider-thumb,
.hwfit-ctx-control input[type="range"]:focus::-webkit-slider-thumb,
.hwfit-ctx-control input[type="range"]:active::-webkit-slider-thumb {
width: 18px;
height: 18px;
}
.hwfit-ctx-control input[type="range"]:hover::-moz-range-thumb,
.hwfit-ctx-control input[type="range"]:focus::-moz-range-thumb,
.hwfit-ctx-control input[type="range"]:active::-moz-range-thumb {
width: 18px;
height: 18px;
}
.hwfit-ctx-control output {
min-width: 28px;
text-align: right;
color: var(--fg);
font-weight: 600;
}
.hwfit-sf {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--fg);
font-family: inherit;
font-size: 12px;
padding: 0 6px;
height: 28px;
box-sizing: border-box;
}
.hwfit-sf[data-field="backend"],
.hwfit-sf[data-field="dtype"],
.hwfit-sf[data-field="tp"] {
height: 32px;
box-sizing: border-box;
width: 100%;
}
.hwfit-sf:focus {
border-color: var(--accent, var(--red));
outline: none;
}
.hwfit-sf[type="text"] { width: 100%; }
.hwfit-sf.hwfit-sf-wide { width: 100%; }
.hwfit-serve-checks {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 6px;
}
.hwfit-sf-cb {
display: flex;
flex-direction: row;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--fg-muted);
cursor: pointer;
padding: 3px 0;
}
.hwfit-sf-cb:hover { color: var(--fg); }
/* Speculative method + tokens controls — render inline beside the checkbox */
.hwfit-spec-group .hwfit-spec-method,
.hwfit-spec-group .hwfit-spec-tokens {
height: 20px;
padding: 0 4px;
font-size: 11px;
/* Match the generic .hwfit-sf serve controls: inherit font + 4px radius
so the Speculative method/token widgets read as the same control
family as the rest of the panel (they were 'Fira Code'/3px before). */
font-family: inherit;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--bg);
color: var(--fg);
}
.hwfit-spec-group .hwfit-spec-method { min-width: 110px; }
.hwfit-spec-group .hwfit-spec-tokens { width: 44px; text-align: center; }
/* The vLLM Speculative input was a bare <input type="number"> with the
native browser up/down spinner showing. Hide the spinner so it
matches the cleaner MTP-spec input next to it. */
.hwfit-spec-tokens-bare {
-moz-appearance: textfield;
appearance: textfield;
/* Tighten + center now that the spinner column is gone — without this
the input still reserves the old 44px and the number floats with a
dead gutter on the right. !important beats the inline width=44px
that ships in the markup. */
width: 30px !important;
padding-right: 4px !important;
text-align: center;
}
/* Themed step buttons replacing the native number-input spinner. */
.hwfit-numstep {
display: inline-flex;
align-items: stretch;
height: 20px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--bg);
overflow: hidden;
}
.hwfit-numstep:focus-within { border-color: var(--accent, var(--red)); }
.hwfit-numstep .hwfit-spec-tokens {
border: none !important;
border-radius: 0 !important;
width: 38px;
height: 100%;
background: transparent;
-moz-appearance: textfield;
appearance: textfield;
margin: 0;
}
.hwfit-numstep-btn {
width: 16px;
background: color-mix(in srgb, var(--fg) 8%, transparent);
border: none;
color: var(--accent, var(--red));
font-family: inherit;
font-size: 15px;
font-weight: 700;
line-height: 1;
cursor: pointer;
padding: 0;
opacity: 1;
transition: background 0.12s, opacity 0.12s, color 0.12s;
}
.hwfit-numstep-btn:hover {
opacity: 1;
background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
color: var(--accent, var(--red));
}
.hwfit-numstep-btn:active {
background: color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
}
.hwfit-numstep-btn[data-step="-1"] { border-right: 1px solid var(--border); }
.hwfit-numstep-btn[data-step="1"] { border-left: 1px solid var(--border); }
/* Custom toggle switch */
.hwfit-sf-cb input[type="checkbox"] {
-webkit-appearance: none;
appearance: none;
width: 28px;
height: 14px;
border-radius: 7px;
background: color-mix(in srgb, var(--fg) 15%, transparent);
position: relative;
cursor: pointer;
margin: 0;
flex-shrink: 0;
transition: background 0.2s;
}
.hwfit-sf-cb input[type="checkbox"]::after {
content: '';
position: absolute;
top: 1px;
left: 2px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #000;
transition: transform 0.2s, background 0.2s;
}
.hwfit-sf-cb input[type="checkbox"]:checked {
background: var(--accent, var(--red));
}
.hwfit-sf-cb input[type="checkbox"]:checked::after {
transform: translateX(14px);
background: #fff;
}
.hwfit-serve-extra label {
display: flex;
flex-direction: column;
gap: 2px;
font-size: 10px;
color: var(--fg-muted);
letter-spacing: 0.3px;
}
.hwfit-serve-extra .hwfit-sf {
width: 100%;
}
.hwfit-serve-cmd-summary {
display: flex;
align-items: center;
gap: 6px;
list-style: none;
cursor: pointer;
user-select: none;
padding: 6px 10px;
border: 1px solid var(--border);
border-radius: 5px;
background: color-mix(in srgb, var(--fg) 4%, transparent);
color: var(--fg-muted);
font-size: 10px;
letter-spacing: 0.3px;
}
.hwfit-serve-cmd {
margin: 0;
padding: 8px 10px;
background: var(--panel);
border: 1px solid var(--border);
border-top: none;
border-radius: 4px;
border-top-left-radius: 0;
border-top-right-radius: 0;
font-family: 'Berkeley Mono', 'SF Mono', 'Fira Code', monospace;
font-size: 10px;
white-space: pre;
word-break: normal;
width: 100%;
box-sizing: border-box;
resize: none;
color: var(--fg);
line-height: 1.5;
min-height: 112px;
overflow: auto;
}
.hwfit-serve-actions {
display: flex;
gap: 6px;
margin-top: 4px;
align-items: center;
}
.hwfit-serve-actions .cookbook-btn {
padding: 5px 14px;
font-size: 11px;
}
.hwfit-serve-actions-spacer { flex: 1 1 auto; }
.hwfit-serve-actions .cookbook-serve-slots {
margin: 0;
align-self: stretch;
}
.hwfit-serve-launch {
background: var(--accent-primary, var(--red));
color: #fff;
border: 1px solid var(--accent-primary, var(--red));
border-radius: 4px;
font-weight: 700;
}
.hwfit-serve-launch:hover {
opacity: 0.9;
}
/* Task header ⋮ menu button */
.cookbook-task-save-btn {
background: none;
border: none;
color: var(--fg-muted);
cursor: pointer;
opacity: 0.3;
padding: 2px;
flex-shrink: 0;
transition: opacity 0.15s;
position: relative;
top: -4px;
transform: scale(1.15);
}
.cookbook-task-save-btn:hover { opacity: 0.8; }
.cookbook-task-edit-btn {
background: none;
border: none;
color: var(--fg-muted);
cursor: pointer;
opacity: 0.3;
padding: 2px;
flex-shrink: 0;
transition: opacity 0.15s;
position: relative;
top: -4px;
transform: scale(1.15);
}
.cookbook-task-edit-btn:hover { opacity: 0.8; }
.cookbook-task-menu-btn {
background: none;
border: 1px solid transparent;
color: var(--fg-muted);
font-size: 16px;
width: 22px;
height: 22px;
cursor: pointer;
opacity: 0;
border-radius: 4px;
position: relative;
top: -3px;
display: inline-flex;
align-items: center;
justify-content: center;
transition: opacity 0.15s;
flex-shrink: 0;
}
.cookbook-task:hover .cookbook-task-menu-btn { opacity: 0.6; }
.cookbook-task-menu-btn:hover {
opacity: 1 !important;
background: color-mix(in srgb, var(--fg) 7%, transparent);
border-color: var(--border);
color: var(--fg);
}
.cookbook-task-menu-btn.cookbook-menu-active {
opacity: 1 !important;
background: color-mix(in srgb, var(--fg) 9%, transparent);
border-color: var(--border);
color: var(--fg);
}
@media (max-width: 768px) {
.cookbook-task .cookbook-task-menu-btn {
opacity: 0.72;
width: 32px;
height: 32px;
min-width: 32px;
top: -3px;
}
.cookbook-task .cookbook-task-menu-btn:active {
opacity: 1;
background: color-mix(in srgb, var(--fg) 9%, transparent);
border-color: var(--border);
}
}
/* Launch-command textarea wrapper — Copy pill floats at the top-right
corner of the field (chat run-output pattern). */
.hwfit-serve-cmd-wrap {
position: relative;
}
.hwfit-serve-cmd-title {
margin: 2px 0 3px;
font-size: 10px;
color: var(--fg-muted);
letter-spacing: 0.3px;
}
.hwfit-serve-cmd-details[open] > .hwfit-serve-cmd-summary {
color: var(--accent, var(--red));
border-color: color-mix(in srgb, var(--accent, var(--red)) 48%, var(--border));
box-shadow: 0 0 12px color-mix(in srgb, var(--accent, var(--red)) 10%, transparent);
}
.hwfit-serve-copy-inline {
position: absolute;
top: 4px; right: 4px;
z-index: 2;
width: 26px !important;
height: 26px !important;
min-width: 26px !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center;
justify-content: center;
opacity: 0.7;
transition: opacity 0.12s, color 0.12s;
}
.hwfit-serve-copy-inline:hover { opacity: 1; color: var(--accent-primary, var(--red)); }
.hwfit-serve-copy-inline.copied { opacity: 1; color: var(--color-save-green, #4caf50); }
.hwfit-serve-copy-inline svg { display: block; }
/* Split button: Clear Server (main) + ^ arrow (more actions). The two halves
are visually joined — left button square on its right edge, arrow square
on its left edge — so they read as one widget. */
.cookbook-gpu-split { display: inline-flex; flex-shrink: 0; }
.cookbook-gpu-split .cookbook-gpu-split-arrow svg { display: block; }
.cookbook-task-dropdown {
/* Must sit above the cookbook modal (whose auto-stack z-index starts at
300 and climbs whenever the modal is brought to the front). 1000 was
not enough — the dropdown rendered behind the modal on mobile. */
z-index: 10000;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 6px;
padding: 4px;
box-shadow: 0 4px 16px rgba(0,0,0,0.3);
min-width: auto;
width: max-content;
}
.cookbook-task-dropdown .dropdown-item-compact {
padding: 6px 10px;
font-size: 12px;
cursor: pointer;
border-radius: 4px;
white-space: nowrap;
}
.cookbook-task-dropdown .dropdown-item-compact:hover {
background: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent);
}
.cookbook-dropdown-danger:hover {
color: var(--red) !important;
}
.cookbook-task-retry:hover { color: var(--color-warning, #f0ad4e); }
.cookbook-task-kill:hover { color: var(--color-error, var(--warn)); }
/* Serve "Edit command" modal */
.cookbook-edit-overlay {
position: fixed; inset: 0; z-index: 10000;
background: rgba(0,0,0,0.5);
display: flex; align-items: center; justify-content: center;
}
.cookbook-edit-modal {
width: min(720px, 92vw);
background: var(--panel, var(--bg));
border: 1px solid var(--border);
border-radius: 8px;
padding: 16px;
box-shadow: 0 10px 30px rgba(0,0,0,0.35);
display: flex; flex-direction: column; gap: 10px;
}
.cookbook-edit-title {
font-size: 13px; font-weight: 600; color: var(--fg);
}
.cookbook-edit-textarea {
width: 100%;
min-height: 140px;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--fg);
font-family: ui-monospace, Menlo, Consolas, monospace;
font-size: 12px;
padding: 8px;
outline: none;
resize: vertical;
white-space: pre;
overflow: auto;
}
.cookbook-edit-textarea:focus { border-color: var(--accent, var(--fg)); }
.cookbook-edit-actions {
display: flex; gap: 8px; justify-content: flex-end;
}
.cookbook-edit-save {
background: color-mix(in srgb, var(--accent) 20%, transparent) !important;
border-color: var(--accent) !important;
}
/* Running tasks */
/* Each running task is a real card with a left accent stripe coloured by
status, instead of an invisible divider against bg. */
.cookbook-task {
margin: 0 0 8px;
border: 1px solid var(--border);
border-left: 3px solid color-mix(in srgb, var(--fg) 30%, transparent);
border-radius: 8px;
overflow: hidden;
max-width: 100%;
min-width: 0;
background: color-mix(in srgb, var(--fg) 3%, var(--bg));
transition: border-color 0.15s, background 0.15s;
}
.cookbook-task:hover {
border-color: color-mix(in srgb, var(--fg) 25%, var(--border));
background: color-mix(in srgb, var(--fg) 5%, var(--bg));
}
/* Status-driven left stripe via :has() — graceful fallback to neutral. */
.cookbook-task:has(.cookbook-task-running) { border-left-color: var(--green, #50fa7b); }
.cookbook-task:has(.cookbook-task-downloading) { border-left-color: var(--color-accent, #00aaff); }
.cookbook-task:has(.cookbook-task-done) { border-left-color: var(--green, #50fa7b); }
.cookbook-task:has(.cookbook-task-error) { border-left-color: var(--color-error, var(--warn, #f87171)); }
.cookbook-task:has(.cookbook-task-queued) { border-left-color: var(--color-warning, #f0ad4e); }
/* Serve crashed / unreachable — full red frame so a dead server in the
Running tab is obvious at a glance (mirrors the endpoint health dot). */
.cookbook-task.cookbook-task-unreachable,
.cookbook-task[data-type="serve"][data-status="error"],
.cookbook-task[data-type="serve"][data-status="crashed"] {
border-color: var(--color-error, #f44);
border-left-color: var(--color-error, #f44);
background: color-mix(in srgb, var(--color-error, #f44) 8%, var(--bg));
}
.cookbook-task-header {
display: flex;
align-items: center;
gap: 8px;
padding: 7px 10px;
background: color-mix(in srgb, var(--fg) 7%, transparent);
font-size: 12px;
border-bottom: 1px solid color-mix(in srgb, var(--fg) 6%, transparent);
/* Pin the row so flex parents + Firefox mobile can't squeeze its height to 0,
which hides the type pill + model name and leaves only the sub-line +
output visible. */
flex-shrink: 0;
min-height: 32px;
}
.cookbook-task-type {
text-transform: uppercase;
font-size: 9px;
font-weight: 600;
letter-spacing: 0.5px;
padding: 1px 5px;
border-radius: 3px;
flex-shrink: 0;
width: 67.3px;
text-align: center;
}
.cookbook-task-type[data-type="serve"] {
background: color-mix(in srgb, var(--green, #50fa7b) 16%, transparent);
color: var(--green, #50fa7b);
}
.cookbook-dl-add-server {
position: relative;
top: -3px;
}
.cookbook-task-type[data-type="download"] {
background: color-mix(in srgb, var(--fg) 10%, transparent);
color: var(--fg-muted);
}
/* Finished state — overrides the per-type colors so a completed download or
serve task shows the same green FINISHED chip. */
.cookbook-task-type.cookbook-task-type-done {
background: color-mix(in srgb, var(--green, #50fa7b) 16%, transparent);
color: var(--green, #50fa7b);
}
.cookbook-task-backend {
width: 67.3px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
opacity: 0.5;
}
.cookbook-task-name {
flex: 1;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.cookbook-task-wave {
font-size: 11px;
letter-spacing: -1px;
color: #fff;
/* Gentle pulse on top of the cycling bars so a running task reads as clearly
alive — especially when the card is collapsed and the log isn't visible. */
animation: cookbook-wave-pulse 1.3s ease-in-out infinite;
}
.cookbook-task-indicator {
min-width: 24px;
text-align: center;
flex-shrink: 0;
display: inline-flex;
justify-content: center;
align-items: center;
/* The done/clear pill inside is wider than 24px and would otherwise
overflow into the edit/save buttons that sit immediately before it on
a serve task. Push it right so it stops crashing into them on desktop. */
margin-left: 8px;
}
.cookbook-task-check {
display: inline-flex;
align-items: center;
gap: 3px;
position: relative;
top: 0;
cursor: pointer;
padding: 1px 6px 1px 4px;
border-radius: 9px;
color: var(--red, #ff5555);
transition: background 0.15s;
}
.cookbook-task-check svg { flex-shrink: 0; }
.cookbook-task-check:hover { background: color-mix(in srgb, var(--red, #ff5555) 18%, transparent); }
/* Terminal task clear pill. */
.cookbook-task-done-label,
.cookbook-task-clear-label {
font-size: 9px;
line-height: 1;
text-transform: lowercase;
}
.cookbook-task-done-label { color: var(--red, #ff5555); }
.cookbook-task-clear-label { display: none; }
.cookbook-task-check-ico { display: none; }
.cookbook-task-clear-ico { display: inline; }
.cookbook-task[data-status="done"] .cookbook-task-check {
color: var(--green, #50fa7b);
}
.cookbook-task[data-status="done"] .cookbook-task-check:hover {
background: color-mix(in srgb, var(--green, #50fa7b) 16%, transparent);
}
.cookbook-task[data-status="done"] .cookbook-task-done-label {
color: var(--green, #50fa7b);
}
@media (max-width: 820px) {
.cookbook-task-check {
top: 2px;
}
}
.cookbook-task-start-now {
display: inline-flex;
align-items: center;
gap: 3px;
position: relative;
top: -4px;
cursor: pointer;
/* Tightened vertical padding so the hover-background isn't disproportionately
tall vs the icon+label. */
padding: 0 6px 0 4px;
height: 14px;
border: 0;
border-radius: 7px;
background: transparent;
color: var(--fg);
font-family: inherit;
font-size: 9px;
line-height: 1;
text-transform: lowercase;
white-space: nowrap;
transition: background 0.15s;
}
.cookbook-task-start-now svg {
flex-shrink: 0;
position: relative;
top: 0;
}
.cookbook-task-start-now:hover {
background: color-mix(in srgb, var(--fg) 12%, transparent);
}
/* "Serve" button on a finished download — green pill matching the "running" /
finished badge (it sits next to the green FINISHED chip + check). */
.cookbook-task-serve-btn {
display: inline-flex;
align-items: center;
gap: 3px;
padding: 1px 6px 1px 4px;
border: 0;
border-radius: 9px;
line-height: 1;
flex-shrink: 0;
cursor: pointer;
font-family: inherit;
font-size: 9px;
text-transform: lowercase;
background: transparent;
color: var(--green, #50fa7b);
position: relative;
top: -2px;
margin-right: 2px;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
transition: background 0.15s;
}
.cookbook-task-serve-btn svg { flex-shrink: 0; }
.cookbook-task-serve-btn:hover { background: color-mix(in srgb, var(--green, #50fa7b) 16%, transparent); }
.cookbook-task-sub {
padding: 1px 10px 4px;
line-height: 1;
display: flex;
align-items: center;
gap: 10px;
}
.cookbook-task-session {
font-size: 9px;
color: var(--fg-muted);
font-family: 'Fira Code', monospace;
opacity: 0.35;
letter-spacing: 0.3px;
}
.cookbook-task-server {
font-size: 9px;
color: var(--fg-muted);
font-family: 'Fira Code', monospace;
opacity: 0.4;
}
.cookbook-task-uptime {
font-size: 9px;
color: var(--fg-muted);
font-family: 'Fira Code', monospace;
opacity: 0.4;
}
.cookbook-task-status {
font-size: 9px;
padding: 1px 6px;
border-radius: 3px;
font-weight: 600;
text-align: left;
flex-shrink: 0;
line-height: 16px;
}
.cookbook-task-running { background: color-mix(in srgb, var(--green, #50fa7b) 20%, transparent); color: var(--green, #50fa7b); }
.cookbook-task-downloading { background: color-mix(in srgb, var(--color-accent, #00aaff) 20%, transparent); color: var(--color-accent, #00aaff); }
/* Stopping: same pill treatment as "running" but orange. */
.cookbook-task-stopping { background: color-mix(in srgb, var(--orange, #ffb86c) 22%, transparent); color: var(--orange, #ffb86c); }
.cookbook-task-done { background: color-mix(in srgb, var(--green) 15%, transparent); color: var(--green); }
.cookbook-task-error { background: color-mix(in srgb, var(--color-error, var(--warn)) 20%, transparent); color: var(--color-error, var(--warn)); }
/* Crashed: same filled-pill treatment as "running" but red, with a red border. */
.cookbook-task-crashed { background: color-mix(in srgb, var(--red, #ff5555) 16%, transparent); color: var(--red, #ff5555); border: 1px solid var(--red, #ff5555); padding: 0 5px; }
.cookbook-task-stopped { background: color-mix(in srgb, var(--color-warning, #f0ad4e) 22%, transparent); color: var(--color-warning, #f0ad4e); }
.cookbook-task-queued { background: color-mix(in srgb, var(--color-warning, #f0ad4e) 15%, transparent); color: var(--color-warning, #f0ad4e); }
/* Stopped / crashed servers get an orange surround instead of the heavier
red — communicates "this isn't running, here's the last command you used,
relaunch when ready" without screaming "error". Color stays normal so the
command text remains easy to copy. */
.cookbook-task[data-status="stopped"],
.cookbook-task[data-status="error"],
.cookbook-task[data-status="crashed"] {
border-color: color-mix(in srgb, var(--color-warning, #f0ad4e) 55%, var(--border));
background: color-mix(in srgb, var(--color-warning, #f0ad4e) 6%, var(--bg));
box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-warning, #f0ad4e) 18%, transparent);
}
.cookbook-task[data-status="stopped"] .cookbook-output-pre,
.cookbook-task[data-status="error"] .cookbook-output-pre,
.cookbook-task[data-status="crashed"] .cookbook-output-pre {
border-top-color: color-mix(in srgb, var(--color-warning, #f0ad4e) 35%, var(--border));
}
.cookbook-task-retry,
.cookbook-task .cookbook-output-wrap { margin: 0; }
.cookbook-task .cookbook-output-pre {
border-radius: 0;
border-top: 1px solid color-mix(in srgb, var(--fg) 8%, transparent);
max-height: 150px;
max-width: 100%;
overflow-x: hidden;
box-sizing: border-box;
background: color-mix(in srgb, var(--fg) 5%, var(--bg));
}
.cookbook-task .cookbook-output-pre:empty {
display: none;
}
.cookbook-task-collapsed {
display: none !important;
}
.cookbook-task[data-type="serve"] .cookbook-task-header {
margin: 4px 4px 0;
border-radius: 6px;
}
.cookbook-task[data-type="serve"] .cookbook-output-wrap {
margin: 0 4px 4px;
}
.cookbook-task[data-type="serve"] .cookbook-output-pre {
border-radius: 6px;
}
/* Env bar — match admin-card */
.cookbook-env-bar {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 12px;
}
.cookbook-env-row {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.cookbook-env-row .cookbook-field-input {
width: 100%;
box-sizing: border-box;
}
/* Tabs — match library tabs */
.cookbook-tabs {
display: flex;
gap: 0;
border-bottom: 1px solid var(--border);
margin-bottom: 8px;
}
/* Mobile: every modal tab strip should swipe horizontally instead of wrapping
or getting cut off. Hide the scrollbar (still pannable via touch / drag). */
@media (max-width: 768px) {
/* Server settings actions need a less fiddly touch target on phones. */
#cookbook-server-add.cal-add-btn-text {
height: 37px;
border-radius: 18.5px;
}
.cookbook-server-check-btn,
.cookbook-server-key-btn,
.cookbook-server-cancel-btn,
.cookbook-server-rm-btn,
.cookbook-server-save-btn {
height: 31px !important;
min-height: 31px;
border-radius: 7px;
}
.cookbook-official-filter {
flex: 0 0 auto;
}
.cookbook-dep-row {
align-items: flex-start;
flex-wrap: wrap;
}
.cookbook-dep-row > .cookbook-dep-info {
flex: 1 1 100%;
}
.cookbook-dep-build-deps {
flex: 0 1 auto;
max-width: 100%;
margin-left: auto;
order: 20;
}
.cookbook-dep-row > .cookbook-dep-installed,
.cookbook-dep-row > .cookbook-dep-installed-btn,
.cookbook-dep-row > .cookbook-dep-na,
.cookbook-dep-row > .cookbook-dep-install:not(.cookbook-dep-build-deps) {
margin-left: auto;
}
.cookbook-tabs,
.memory-tabs,
.admin-tabs,
.lib-tabs,
.gallery-tabs,
.preset-tabs {
flex-wrap: nowrap !important;
overflow-x: auto !important;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
overscroll-behavior-x: contain;
scrollbar-width: none;
}
.cookbook-tabs::-webkit-scrollbar,
.memory-tabs::-webkit-scrollbar,
.admin-tabs::-webkit-scrollbar,
.lib-tabs::-webkit-scrollbar,
.gallery-tabs::-webkit-scrollbar,
.preset-tabs::-webkit-scrollbar {
display: none;
}
.cookbook-tabs > *,
.memory-tabs > *,
.admin-tabs > *,
.lib-tabs > *,
.gallery-tabs > *,
.preset-tabs > * {
flex-shrink: 0;
}
}
.cookbook-tab {
/* `background: none` isn't enough on Windows — Chrome/Edge fall back to the
OS native button background (dark gray / black under dark mode) when
no explicit color + appearance reset is applied. Force transparent +
appearance:none so the tab inherits the modal's panel color. */
appearance: none;
-webkit-appearance: none;
background: transparent;
background-color: transparent;
padding: 6px 14px;
font-size: 12px;
font-family: inherit;
color: var(--fg-muted);
border: none;
border-bottom: 2px solid transparent;
cursor: pointer;
transition: color 0.1s, border-color 0.1s;
white-space: nowrap;
display: inline-flex;
align-items: center;
gap: 4px;
}
.cookbook-tab:hover {
color: var(--fg);
}
.cookbook-tab.active {
color: var(--accent, var(--red));
border-bottom-color: var(--accent, var(--red));
}
/* Mobile: hide tab icons so labels don't wrap to a new row */
@media (max-width: 640px) {
.cookbook-tab svg {
display: none;
}
.cookbook-tab {
padding: 6px 10px;
font-size: 13px;
}
/* Mobile cookbook text: match the calendar/library modal scale. iOS keeps
its no-zoom safeguard below; Android does not need inflated controls. */
.cookbook-body {
font-size: 13px;
}
.cookbook-body h2 { font-size: 14px; }
.cookbook-body .memory-desc,
.cookbook-body .doclib-desc { font-size: 12px; }
.cookbook-body .memory-item-title { font-size: 13px; }
.cookbook-body .memory-item-meta { font-size: 11px !important; }
.cookbook-body .hwfit-sf,
.cookbook-body .cookbook-field-input,
.cookbook-body .cookbook-dl-repo { font-size: 13px; }
.cookbook-body .memory-toolbar-btn { font-size: 12px; }
}
/* Mobile cookbook sizing — kept in line with calendar/library modals. */
@media (max-width: 768px) {
.hwfit-serve-row-core .hwfit-context-label {
grid-column: 1 / -1;
}
/* The Speculative control (checkbox + method dropdown + token stepper)
is too wide for a phone — the stepper ran off the right edge of the
modal. Let the group wrap onto its own line, take full width, and
shrink the method dropdown so the +/− stepper stays on-screen. */
.hwfit-spec-group {
flex-wrap: wrap;
flex-basis: 100%;
column-gap: 4px;
row-gap: 4px;
}
.hwfit-spec-group .hwfit-spec-method {
min-width: 0;
flex: 0 1 82px;
max-width: 82px;
}
.hwfit-numstep { flex: 0 0 auto; }
.hwfit-spec-group .hwfit-help-chip-inline {
flex: 0 0 auto;
margin-left: 2px !important;
}
.cookbook-card-title { font-size: 13px; }
.cookbook-card-desc { font-size: 12px; }
.cookbook-field-label { font-size: 12px; }
/* All cookbook controls share one compact field scale. */
.cookbook-field-input { font-size: 13px; padding: 6px 8px; }
select.cookbook-field-input { font-size: 13px !important; padding: 5px 8px; }
/* Repo input + model search read oversized at 16px; bring their text and
placeholder hints down to match the rest of the cookbook. */
.cookbook-dl-repo, .hwfit-search { font-size: 13px !important; }
.cookbook-cmd-preview { font-size: 12px; padding: 8px 10px; }
.cookbook-btn { font-size: 12px; padding: 6px 12px; }
.cookbook-tab { font-size: 13px; }
.cookbook-task-header { font-size: 13px; padding: 6px 10px; }
.cookbook-task-name { font-size: 13px; }
.cookbook-task-sub { font-size: 11px; }
.cookbook-task-status {
font-size: 10px;
/* Vertical alignment of the phase/loading badge against the task title. */
position: relative;
top: 2px;
}
/* Keep the ascii wave aligned with the badge (both 2px down). */
.cookbook-task-wave {
position: relative;
top: 2px;
}
/* Session id (serve-…) + uptime sub-line down 1px. */
.cookbook-task-session,
.cookbook-task-uptime {
position: relative;
top: 1px;
}
/* Rest of the header row down 2px to match the status badge + wave: the
model title, the serve/download type tag, and the edit/save/menu icons. */
.cookbook-task-type,
.cookbook-task-name,
.cookbook-task-edit-btn,
.cookbook-task-save-btn {
position: relative;
top: 2px;
}
/* The ⋮ menu sits higher than the rest of the row. */
.cookbook-task-menu-btn {
position: relative;
top: -2px;
/* Mobile has no hover — the base rule's opacity:0 left the ⋮ button
invisible AND effectively unclickable. Always show + give a proper
touch target. */
opacity: 0.7 !important;
width: 32px !important;
height: 32px !important;
font-size: 18px !important;
}
/* The copied-confirmation checkmark sits 2px higher than the copy icon. */
.cookbook-output-copy.copied svg {
position: relative;
top: 2px;
}
.cookbook-section-title { font-size: 12px; }
.cookbook-settings-label { font-size: 12px; }
.cookbook-settings-hint { font-size: 11px; }
.cookbook-settings-input { font-size: 13px; }
/* Settings stack is flex:1 + overflow:hidden with no inner scroll, so on a
short mobile viewport its lower half gets clipped. Let it scroll. */
.cookbook-settings-stack { overflow-y: auto !important; -webkit-overflow-scrolling: touch; }
/* The Servers card is flex:1 + its own overflow-y:auto — a nested scroll that
collapses and crops its list inside the now-scrolling stack. Let both cards
take natural height and scroll the whole stack as one instead. */
.cookbook-settings-stack > .admin-card {
flex: 0 0 auto !important;
overflow: visible !important;
}
.cookbook-dl-repo { font-size: 13px !important; }
.cookbook-dl-btn { font-size: 14px; }
.cookbook-serve-preset-name { font-size: 14px; }
.cookbook-serve-preset-meta { font-size: 12px; }
.cookbook-saved-name { font-size: 14px; }
.cookbook-saved-host { font-size: 12px; }
.cookbook-slot-btn { font-size: 13px; }
/* The serve-panel "Save" split button reads larger than the surrounding
controls (it's also bold) — knock it down so it matches the row. */
.cookbook-saved-save,
.cookbook-saved-arrow { font-size: 11px; }
.cookbook-output-pre { font-size: 12px; }
.cookbook-dep-tag { font-size: 12px; }
.cookbook-checkbox-label { font-size: 13px; }
.cookbook-gpu-btn { font-size: 13px; }
}
/* Slider — range + text value */
.cookbook-slider-wrap {
display: flex;
align-items: center;
gap: 8px;
}
.cookbook-slider {
flex: 1;
min-width: 0;
height: 6px;
margin: 8px 0;
padding: 0;
-webkit-appearance: none;
appearance: none;
background: linear-gradient(to right, var(--accent, var(--red)) 0%, var(--accent, var(--red)) 50%, color-mix(in srgb, var(--fg) 12%, transparent) 50%);
border-radius: 4px;
outline: none;
cursor: pointer;
transition: opacity 0.1s;
}
.cookbook-slider-value {
width: 64px !important;
flex-shrink: 0;
text-align: center;
font-size: 11px;
}
/* Toggle switches — match admin-switch */
.cookbook-checkbox-row {
grid-column: 1 / -1;
display: flex;
flex-wrap: wrap;
gap: 6px 14px;
padding: 4px 0;
}
.cookbook-checkbox-label {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: color-mix(in srgb, var(--fg) 60%, transparent);
cursor: pointer;
user-select: none;
}
.cookbook-checkbox-label:hover { color: var(--fg); }
.cookbook-checkbox {
appearance: none;
-webkit-appearance: none;
width: 30px;
height: 16px;
background: color-mix(in srgb, var(--fg) 15%, transparent);
border-radius: 8px;
position: relative;
cursor: pointer;
transition: background 0.08s;
flex-shrink: 0;
}
.cookbook-checkbox::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--panel);
box-shadow: 0 1px 2px rgba(0,0,0,0.25);
transition: transform 0.08s;
}
.cookbook-checkbox:checked {
background: var(--red);
}
/* GPU toggle grid */
.cookbook-gpu-row {
display: flex;
align-items: center;
gap: 4px;
margin-top: 8px;
}
.cookbook-gpu-label {
font-size: 11px;
color: color-mix(in srgb, var(--fg) 60%, transparent);
margin-right: 4px;
white-space: nowrap;
}
.cookbook-gpu-btn {
width: 30px;
height: 30px;
padding: 0;
font-size: 11px;
font-family: inherit;
font-weight: 600;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg);
color: color-mix(in srgb, var(--fg) 40%, transparent);
cursor: pointer;
transition: all 0.15s;
}
.cookbook-gpu-btn:hover {
color: var(--fg);
border-color: var(--red);
background: color-mix(in srgb, var(--fg) 4%, transparent);
}
.cookbook-gpu-btn.active {
background: var(--red);
color: var(--panel);
border-color: var(--red);
}
/* Instance / Save buttons */
.cookbook-add-instance-btn,
.cookbook-save-btn {
background: transparent;
border: 1px dashed color-mix(in srgb, var(--accent) 50%, transparent);
color: color-mix(in srgb, var(--fg) 70%, transparent);
font-size: 11px;
}
.cookbook-add-instance-btn:hover,
.cookbook-save-btn:hover {
border-color: var(--accent);
color: var(--fg);
}
.cookbook-remove-instance-btn {
background: transparent;
border: none;
color: color-mix(in srgb, var(--fg) 40%, transparent);
font-size: 12px;
cursor: pointer;
padding: 0 4px;
line-height: 1;
}
.cookbook-remove-instance-btn:hover { color: var(--red); }
/* Preset chips */
/* Saved card styling */
.cookbook-saved-card .cookbook-card-desc {
font-size: 11px;
color: color-mix(in srgb, var(--fg) 50%, transparent);
}
.cookbook-delete-preset-btn {
color: color-mix(in srgb, var(--fg) 50%, transparent);
font-size: 12px;
}
.cookbook-delete-preset-btn:hover {
color: var(--red);
}
/* Kill button */
.cookbook-kill-btn {
border-color: var(--color-error);
color: var(--color-error);
background: transparent;
}
.cookbook-kill-btn:hover {
background: var(--color-error);
color: var(--panel);
border-color: var(--color-error);
}
/* Error diagnosis banner */
.cookbook-diagnosis {
margin-top: 6px;
padding: 10px 12px;
background: color-mix(in srgb, var(--color-error) 8%, transparent);
border: 1px solid color-mix(in srgb, var(--color-error) 30%, transparent);
border-radius: 6px;
/* The diagnosis body can carry traceback fragments and long unbroken
paths (e.g. /home/.../snapshots/<sha>/<file>.gguf). Without these,
a single long token pushes the card wider than the cookbook modal,
scrolling the row right and clipping the action buttons. */
min-width: 0;
max-width: 100%;
overflow-wrap: anywhere;
word-break: break-word;
}
.cookbook-diagnosis pre,
.cookbook-diagnosis code {
white-space: pre-wrap;
word-break: break-word;
overflow-wrap: anywhere;
max-width: 100%;
}
.cookbook-diag-header {
display: flex;
align-items: center;
gap: 7px;
position: relative;
top: -4px;
margin-bottom: -4px;
}
.cookbook-diag-fold {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 0;
min-height: 0;
border: 0;
background: transparent;
color: var(--color-error);
font: inherit;
font-size: 11px;
font-weight: 700;
cursor: pointer;
margin-right: auto;
}
.cookbook-diag-fold:hover {
background: transparent;
color: var(--color-error);
opacity: 0.85;
}
.cookbook-diag-chevron {
display: inline-block;
width: 10px;
font-size: 10px;
}
.cookbook-diag-copy {
border: 0;
background: transparent;
color: var(--fg-muted);
padding: 0 2px;
width: 18px;
height: 18px;
min-height: 18px;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
}
.cookbook-diag-copy:hover {
background: transparent;
color: var(--fg);
}
.cookbook-diag-copy.copied {
color: var(--green, #50fa7b);
}
.cookbook-diag-copy svg {
display: block;
}
.cookbook-diag-dismiss {
border: 0;
background: transparent;
color: var(--fg-muted);
padding: 0;
width: 16px;
height: 18px;
min-height: 18px;
line-height: 16px;
font-size: 13px;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
position: relative;
top: -2px;
}
.cookbook-diag-dismiss:hover {
background: transparent;
color: var(--color-error);
}
.cookbook-diag-message {
font-size: 12px;
font-weight: 600;
color: var(--color-error);
margin-bottom: 4px;
margin-left: 2px;
user-select: text;
}
.cookbook-diag-suggestion {
font-size: 11px;
line-height: 1.35;
color: var(--fg-muted);
margin-bottom: 8px;
margin-left: 2px;
user-select: text;
}
.cookbook-diag-fixes {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.cookbook-diag-actions {
position: relative;
display: inline-flex;
}
.cookbook-diag-action-trigger {
font-size: 11px;
padding: 4px 10px;
min-height: 24px;
background: var(--panel);
border: 1px solid color-mix(in srgb, var(--color-error) 40%, transparent);
color: var(--fg);
}
.cookbook-diag-action-trigger:hover {
border-color: var(--color-error);
background: color-mix(in srgb, var(--color-error) 12%, transparent);
}
.cookbook-diag-menu {
position: absolute;
left: 0;
top: calc(100% + 4px);
min-width: 180px;
z-index: 80;
}
.cookbook-diag-menu button {
width: 100%;
justify-content: flex-start;
text-align: left;
white-space: nowrap;
}
.cookbook-diag-btn {
font-size: 11px;
padding: 4px 10px;
background: var(--panel);
border: 1px solid color-mix(in srgb, var(--color-error) 40%, transparent);
color: var(--fg);
}
.cookbook-diag-btn:hover {
border-color: var(--color-error);
background: color-mix(in srgb, var(--color-error) 12%, transparent);
}
/* Icons on the left of diagnosis action buttons (Retry / Copy / Edit / etc.). */
.cookbook-diag-btn,
.cookbook-diag-menu button {
display: inline-flex;
align-items: center;
gap: 5px;
}
.cookbook-diag-btn-ico {
flex-shrink: 0;
opacity: 0.9;
}
/* ── What Fits? (hardware model fitting tab in cookbook) ── */
.cookbook-group.hidden { display: none !important; }
/* Section titles */
.cookbook-section-title {
font-size: 12px;
font-weight: 600;
color: var(--fg);
opacity: 0.7;
margin: 10px 0 4px;
}
.cookbook-section-title:first-child { margin-top: 0; }
/* Download input */
.cookbook-dl-input {
display: flex;
gap: 6px;
margin-bottom: 4px;
align-items: stretch;
}
.cookbook-dl-repo {
flex: 1;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--fg);
font-family: 'Fira Code', monospace;
font-size: 12px;
padding: 5px 8px;
}
.cookbook-dl-repo:focus {
border-color: var(--accent, var(--red));
outline: none;
}
.cookbook-dl-repo::placeholder {
color: var(--fg-muted);
opacity: 0.5;
font-family: inherit;
}
/* Brief border+glow flash when an Ollama row in the hwfit list autofills the
Download input — helps the user see what landed when the input is offscreen
or above a tall list. */
.cookbook-dl-repo.cookbook-dl-flash {
border-color: var(--red) !important;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--red) 25%, transparent) !important;
transition: border-color 0.2s, box-shadow 0.2s;
}
.cookbook-dl-btn {
background: var(--accent, var(--red));
color: #fff;
border: none;
border-radius: 4px;
padding: 0 14px;
height: 28px;
font-size: 11px;
font-family: inherit;
cursor: pointer;
flex-shrink: 0;
display: flex;
align-items: center;
margin-top: -4px;
}
.cookbook-dl-btn:hover {
opacity: 0.9;
}
.cookbook-dl-gguf-row {
margin-top: -1px;
gap: 5px;
align-items: center;
justify-content: flex-end;
font-size: 11px;
position: relative;
top: -2px;
}
.cookbook-dl-gguf-label {
opacity: 0.65;
flex-shrink: 0;
}
#cookbook-dl-gguf-quant {
height: 28px;
min-width: 118px;
flex: 0 0 auto;
}
#cookbook-dl-gguf-note {
opacity: 0.55;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 240px;
}
/* HF link in search panel */
.hwfit-panel-hf-link {
font-size: 10px;
color: var(--red);
text-decoration: none;
margin-left: auto;
padding: 2px 6px;
border: 1px solid color-mix(in srgb, var(--red) 40%, transparent);
border-radius: 3px;
transition: all 0.15s;
}
.hwfit-panel-hf-link:hover {
color: #fff;
background: var(--red);
border-color: var(--red);
}
.cookbook-server-toggle {
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
user-select: none;
list-style: none;
padding: 4px 6px;
border-radius: 4px;
font-size: 11px;
color: var(--fg-muted);
transition: background 0.1s, color 0.1s;
}
.cookbook-server-toggle:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 5%, transparent); }
.cookbook-server-toggle svg {
opacity: 0.5;
transition: transform 0.2s;
}
.cookbook-server-details[open] .cookbook-server-toggle svg {
transform: rotate(45deg);
}
.cookbook-settings-content {
padding: 6px 0;
}
.cookbook-settings-row {
display: flex;
flex-direction: column;
gap: 2px;
margin-bottom: 8px;
}
.cookbook-settings-label {
font-size: 10px;
color: var(--fg-muted);
}
.cookbook-settings-hint {
opacity: 0.5;
font-weight: 400;
}
.cookbook-settings-input {
width: 100%;
font-size: 11px;
padding: 4px 6px;
}
.cookbook-settings-subtitle {
font-size: 10px;
font-weight: 600;
color: var(--fg-muted);
text-transform: uppercase;
letter-spacing: 0.3px;
margin: 8px 0 4px;
display: flex;
align-items: center;
gap: 6px;
}
.cookbook-server-add {
background: none;
border: 1px solid var(--border);
border-radius: 3px;
color: var(--fg-muted);
font-size: 13px;
width: 16px;
height: 16px;
position: relative;
top: -3px;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
line-height: 0;
padding-bottom: 3px;
line-height: 16px;
text-align: center;
transition: color 0.1s, border-color 0.1s;
}
.cookbook-server-add:hover {
color: var(--fg);
border-color: var(--fg);
}
.cookbook-server-row {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
}
.cookbook-srv-status {
flex: 0 0 auto;
width: 8px;
height: 8px;
border-radius: 50%;
background: #777;
transition: background 0.2s, box-shadow 0.2s, transform 0.15s;
cursor: pointer;
}
.cookbook-srv-status.testing {
background: var(--color-warning, #f0ad4e);
animation: cookbook-srv-pulse 0.9s ease-in-out infinite;
}
.cookbook-srv-status.ok {
background: var(--cookbook-server-color, var(--color-success, #50fa7b));
/* Soft outer ring + breathing halo so active servers glow like LEDs. */
box-shadow:
0 0 0 2px color-mix(in srgb, var(--cookbook-server-color, var(--color-success, #50fa7b)) 25%, transparent),
0 0 10px 2px color-mix(in srgb, var(--cookbook-server-color, var(--color-success, #50fa7b)) 55%, transparent);
animation: cookbook-srv-glow-ok 2.4s ease-in-out infinite;
}
.cookbook-srv-status.fail {
background: var(--red, #e06c75);
box-shadow:
0 0 0 2px color-mix(in srgb, var(--red, #e06c75) 25%, transparent),
0 0 10px 2px color-mix(in srgb, var(--red, #e06c75) 55%, transparent);
}
@keyframes cookbook-srv-glow-ok {
0%, 100% {
box-shadow:
0 0 0 2px color-mix(in srgb, var(--cookbook-server-color, var(--color-success, #50fa7b)) 22%, transparent),
0 0 8px 1px color-mix(in srgb, var(--cookbook-server-color, var(--color-success, #50fa7b)) 45%, transparent);
}
50% {
box-shadow:
0 0 0 3px color-mix(in srgb, var(--cookbook-server-color, var(--color-success, #50fa7b)) 38%, transparent),
0 0 14px 3px color-mix(in srgb, var(--cookbook-server-color, var(--color-success, #50fa7b)) 75%, transparent);
}
}
.cookbook-server-row input.hwfit-sf,
.cookbook-server-row select.hwfit-sf {
font-size: 11px;
padding: 0 6px;
margin: 0;
height: 24px;
min-height: 24px;
max-height: 24px;
line-height: 24px;
box-sizing: border-box;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--bg);
color: var(--fg);
font-family: inherit;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.cookbook-server-row select.hwfit-sf {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5'%3E%3Cpath d='M0 0l4 5 4-5z' fill='%23999'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 6px center;
padding-right: 18px;
}
/* Each server is its own card block (mirrors the Running tab's task cards):
full border + accent left-rail + rounded corners + subtle fill, spaced. */
.cookbook-server-entry {
position: relative;
margin-bottom: 8px;
padding: 8px 10px;
border: 1px solid var(--border);
border-left: 3px solid var(--cookbook-server-color, color-mix(in srgb, var(--fg) 30%, transparent));
border-radius: 8px;
background: color-mix(in srgb, var(--fg) 3%, var(--bg));
box-sizing: border-box;
}
.cookbook-server-entry:last-child { margin-bottom: 0; }
.cookbook-server-row .cookbook-srv-color-wrap {
width: 28px;
flex-shrink: 0;
flex-grow: 0;
display: inline-flex;
align-items: center;
position: relative;
color: var(--cookbook-server-color, var(--fg-muted));
}
.cookbook-server-row .cookbook-srv-color-dot {
position: static;
transform: none;
pointer-events: none;
width: 12px;
height: 12px;
border-radius: 50%;
background: currentColor;
opacity: 0.55;
z-index: 1;
}
.cookbook-server-row .cookbook-srv-color-wrap.has-color .cookbook-srv-color-dot {
opacity: 1;
}
.cookbook-server-row .cookbook-srv-color-btn {
width: 26px;
height: 26px;
padding: 0 !important;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 0;
background: transparent;
box-shadow: none;
border-radius: 50%;
top: -2px;
}
.cookbook-server-row .cookbook-srv-color-btn:hover {
background: color-mix(in srgb, var(--cookbook-server-color, var(--fg)) 14%, transparent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--cookbook-server-color, var(--fg)) 18%, transparent);
}
.cookbook-server-row .cookbook-srv-color-caret {
position: absolute;
right: 6px;
top: 50%;
transform: translateY(-50%);
opacity: 0.65;
}
.cookbook-server-row .cookbook-srv-color-wrap.has-color .cookbook-srv-color-btn {
color: var(--fg);
border-color: transparent;
background-color: transparent;
box-shadow: none;
}
.cookbook-server-row .cookbook-srv-color-wrap.has-color .cookbook-srv-color-dot {
background: var(--cookbook-server-color, currentColor);
}
.cookbook-server-row .cookbook-srv-color-wrap.has-color .cookbook-srv-color-caret {
color: var(--fg-muted);
}
.cookbook-srv-color-menu {
position: absolute;
z-index: 5000;
top: calc(100% + 4px);
left: 0;
width: 138px;
padding: 4px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg);
box-shadow: 0 8px 24px color-mix(in srgb, #000 35%, transparent);
}
.cookbook-srv-color-menu.hidden {
display: none;
}
.cookbook-srv-color-item {
width: 100%;
height: 24px;
border: 1px solid transparent;
border-radius: 4px;
background: color-mix(in srgb, var(--swatch-color, var(--fg)) 10%, transparent);
color: var(--fg);
display: flex;
align-items: center;
gap: 7px;
padding: 0 7px;
font: inherit;
font-size: 11px;
cursor: pointer;
text-align: left;
}
.cookbook-srv-color-item:hover,
.cookbook-srv-color-item.active {
border-color: color-mix(in srgb, var(--swatch-color, var(--accent, var(--red))) 55%, var(--border));
background: color-mix(in srgb, var(--swatch-color, var(--accent, var(--red))) 18%, var(--bg));
}
.cookbook-srv-color-item-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--swatch-color, var(--fg-muted));
border: 1px solid color-mix(in srgb, var(--fg) 18%, transparent);
flex-shrink: 0;
}
/* Normalize every control on a server row to the same 24-px height — with
many servers the small per-control height drift was very visible. */
.cookbook-server-row > * {
height: 24px;
box-sizing: border-box;
flex-shrink: 0;
}
.cookbook-server-row > input.hwfit-sf,
.cookbook-server-row > select.hwfit-sf,
.cookbook-server-row > button,
.cookbook-server-row .cookbook-srv-actions > button {
height: 24px;
line-height: 22px;
padding: 0 8px;
font-size: 11px;
box-sizing: border-box;
}
.cookbook-server-row .cookbook-srv-actions > .close-btn { width: 24px; padding: 0; line-height: 22px; }
.cookbook-server-row .cookbook-dep-tag { display: inline-flex; align-items: center; height: 24px; padding: 0 6px; line-height: 1; }
.cookbook-server-rm {
width: 22px;
height: 22px;
font-size: 10px;
opacity: 0.55;
border: 1px solid var(--border);
border-radius: 4px;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: opacity 0.12s, background 0.12s, color 0.12s, border-color 0.12s;
}
.cookbook-server-rm:hover {
opacity: 1;
background: var(--red);
color: #fff;
border-color: var(--red);
}
/* Labelled "Delete server" variant (lives in the Model Directory row) — override
the 22x22 icon box with a normal text button. */
.cookbook-server-rm-btn {
width: auto;
height: auto;
padding: 2px 8px;
font-size: 10px;
font-family: inherit;
background: none;
color: var(--red);
border-color: color-mix(in srgb, var(--red) 40%, var(--border));
white-space: nowrap;
position: relative;
top: -3px;
}
/* The "+" glyph in the Servers-header "+ Add" button, nudged up 1px (scoped so
the shared calendar +New pill is unaffected). */
#cookbook-server-add .cal-add-plus { position: relative; top: -1px; }
/* Save button on a new server entry — same shape as Delete, accent-colored;
turns green once saved. */
.cookbook-server-save-btn {
width: auto;
padding: 2px 8px;
font-size: 10px;
font-family: inherit;
cursor: pointer;
background: none;
color: var(--accent, var(--red));
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 45%, var(--border));
border-radius: 4px;
white-space: nowrap;
}
.cookbook-server-save-btn:hover { background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent); }
/* Connection controls use the same compact typography as the server's
Delete and Save actions, despite living in the title row. */
.cookbook-server-check-btn,
.cookbook-server-key-btn {
padding: 2px 8px;
font-size: 10px;
font-family: inherit;
font-weight: 400;
line-height: 1;
white-space: nowrap;
}
/* Cancel (discard a new server) — same shape/size as Save, muted. */
.cookbook-server-cancel-btn {
width: auto;
padding: 2px 8px;
font-size: 10px;
font-family: inherit;
cursor: pointer;
background: none;
color: var(--fg-muted, var(--fg));
border: 1px solid var(--border);
border-radius: 4px;
white-space: nowrap;
}
.cookbook-server-cancel-btn:hover { color: var(--fg); border-color: var(--fg); }
.cookbook-server-save-btn.saved {
color: var(--green, #50fa7b);
border-color: color-mix(in srgb, var(--green, #50fa7b) 45%, var(--border));
cursor: default;
}
.cookbook-path-row {
display: flex;
gap: 4px;
margin-bottom: 3px;
align-items: center;
}
.cookbook-path-input {
flex: 1;
}
.cookbook-path-add {
background: none;
border: 1px solid var(--border);
border-radius: 3px;
color: var(--fg-muted);
font-size: 12px;
width: 18px;
height: 18px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
line-height: 1;
transition: color 0.1s, border-color 0.1s;
}
.cookbook-path-add:hover {
color: var(--fg);
border-color: var(--fg);
}
/* Server selector in search toolbar */
.hwfit-server-select {
min-width: 70px;
}
/* GPU toggle buttons */
.hwfit-gpu-toggles {
display: flex;
gap: 8px;
align-items: center;
position: relative;
top: -3px;
}
.hwfit-gpu-btn {
background: none;
border: 1px solid var(--border);
border-radius: 4px;
color: var(--fg-muted);
font-size: 10px;
padding: 0 5px;
cursor: pointer;
min-width: 20px;
height: 28px;
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
transition: background 0.1s, color 0.1s, border-color 0.1s;
font-family: inherit;
}
.hwfit-gpu-btn:hover {
color: var(--fg);
border-color: var(--fg);
}
.hwfit-gpu-btn.active {
background: var(--accent, var(--red));
color: #fff;
border-color: var(--accent, var(--red));
}
/* Pool selector for heterogeneous GPU boxes — sits left of the RAM/GPU buttons */
.hwfit-gpu-group {
background: var(--bg-elev, var(--bg));
border: 1px solid var(--border);
border-radius: 4px;
color: var(--fg);
font-size: 10px;
height: 28px;
padding: 0 4px;
cursor: pointer;
box-sizing: border-box;
font-family: inherit;
max-width: 190px;
}
.hwfit-gpu-group:hover { border-color: var(--fg); }
@keyframes cookbookCmdFlash {
0% { box-shadow: 0 0 0 0 var(--accent, var(--red)); border-color: var(--accent, var(--red)); }
30% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, var(--red)) 40%, transparent); border-color: var(--accent, var(--red)); }
100% { box-shadow: 0 0 0 0 transparent; }
}
/* "Confirmed working" tick on a saved serve config (auto-saved once its endpoint registered) */
.cookbook-saved-confirmed {
flex-shrink: 0;
display: inline-flex;
align-items: center;
line-height: 0;
}
.hwfit-container { display: flex; flex-direction: column; gap: 8px; }
.hwfit-toolbar {
display: flex; gap: 4px; align-items: center; flex-wrap: wrap;
}
.hwfit-toolbar .spinner-whirlpool canvas {
position: relative;
top: -2px;
}
#hwfit-cache-scan .spinner-whirlpool canvas {
position: relative;
top: -2px;
}
.hwfit-toolbar select,
.hwfit-toolbar input {
height: 28px;
padding: 0 6px;
font-size: 11px;
font-family: inherit;
background: var(--bg);
color: var(--fg);
border: 1px solid var(--border);
border-radius: 4px;
box-sizing: border-box;
}
.hwfit-toolbar select:focus,
.hwfit-toolbar input:focus { outline: none; border-color: var(--red); }
.hwfit-toolbar .hwfit-usecase-btn {
min-width: 0;
width: 108px;
flex-shrink: 0;
font-size: 10px;
top: -3px;
}
.hwfit-toolbar .hwfit-quant { min-width: 0; width: 78px; flex-shrink: 0; font-size: 10px; }
.hwfit-toolbar .hwfit-engine-btn { min-width: 0; width: 116px; flex-shrink: 0; font-size: 10px; }
.hwfit-advanced-panel {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
margin-top: 6px;
padding: 7px;
border: 1px solid var(--border);
border-radius: 6px;
background: color-mix(in srgb, var(--fg) 4%, transparent);
}
.hwfit-advanced-panel.hidden { display: none; }
.hwfit-advanced-panel .hwfit-engine-btn {
top: -3px;
width: 116px;
min-width: 116px;
box-sizing: border-box;
font-size: 11px;
}
.hwfit-advanced-panel .hwfit-quant {
width: 78px;
min-width: 78px;
height: 28px;
box-sizing: border-box;
padding: 0 7px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--bg);
color: var(--fg);
font: inherit;
font-size: 11px;
position: relative;
top: 0;
}
.hwfit-advanced-panel .hwfit-quant option {
background: var(--panel, var(--bg));
color: var(--fg);
}
.hwfit-advanced-btn.active {
color: var(--accent, var(--red));
border-color: color-mix(in srgb, var(--accent, var(--red)) 55%, var(--border));
background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
}
/* Dot inside the Fit column header — click to toggle the fit-only filter
(off = show too-tight rows; on = hide them). */
.hwfit-fit-dot {
display: inline-block;
margin-right: 4px;
font-size: 8px;
line-height: 1;
color: color-mix(in srgb, var(--fg) 35%, transparent);
cursor: pointer;
vertical-align: middle;
position: relative;
top: -1px; /* nudge 1px up so the small dot sits centered with the "Fit" caps */
transition: color 0.12s ease, text-shadow 0.12s ease;
}
/* Quant suffix appended to model names when the storage format isn't in the
repo id — e.g. "(FP4-MoE-Mixed)" after DeepSeek-V4-Flash. Muted to read as
metadata, not part of the name. */
.hwfit-name-quant {
font-size: 0.78em;
opacity: 0.55;
font-weight: 400;
margin-left: 4px;
}
.hwfit-fit-dot:hover { color: var(--accent, var(--red)); }
.hwfit-fit-dot.active {
color: var(--green, #50fa7b);
text-shadow: 0 0 4px color-mix(in srgb, var(--green, #50fa7b) 55%, transparent);
}
.hwfit-help-chip {
width: 14px;
height: 14px;
flex: 0 0 14px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 50%;
border: 1px solid color-mix(in srgb, var(--fg) 22%, transparent);
color: color-mix(in srgb, var(--fg) 55%, transparent);
font-size: 9px;
font-weight: 700;
line-height: 1;
cursor: help;
position: relative;
top: -1px;
margin-left: -1px;
}
.hwfit-help-chip:hover {
color: var(--accent, var(--red));
border-color: color-mix(in srgb, var(--fg) 45%, transparent);
background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.hwfit-help-chip-inline {
margin-left: -2px;
margin-right: 0;
top: 0; /* parent rule sets top:-1px; nudge inline variant 1px lower */
}
/* Quant select + inline ? wrapper — the ? sits inside the dropdown's bordered
box, anchored on the right just left of the chevron. */
.hwfit-quant-wrap, .hwfit-engine-wrap, .hwfit-usecase-wrap {
position: relative;
display: inline-flex;
align-items: center;
}
.hwfit-engine-btn,
.hwfit-usecase-btn {
height: 28px;
padding: 0 30px 0 7px;
display: inline-flex;
align-items: center;
gap: 5px;
color: var(--fg);
background: var(--bg);
border: 1px solid var(--border);
border-radius: 4px;
font: inherit;
cursor: pointer;
text-align: left;
position: relative;
top: -7px;
}
.hwfit-engine-btn-icon,
.hwfit-engine-item-icon,
.hwfit-usecase-btn-icon,
.hwfit-usecase-item-icon {
width: 14px;
height: 14px;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--accent, var(--red));
flex-shrink: 0;
}
.hwfit-engine-btn-label,
.hwfit-usecase-btn-label {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.hwfit-engine-caret,
.hwfit-usecase-caret {
position: absolute;
right: 6px;
opacity: 0.55;
pointer-events: none;
}
.hwfit-engine-menu,
.hwfit-usecase-menu {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
z-index: 120;
padding: 4px;
background: var(--panel, var(--bg));
border: 1px solid var(--border);
border-radius: 6px;
box-shadow: 0 6px 20px rgba(0,0,0,0.22);
}
.hwfit-engine-item,
.hwfit-usecase-item {
width: 100%;
min-height: 26px;
padding: 5px 7px;
display: flex;
align-items: center;
gap: 7px;
color: var(--fg);
background: transparent;
border: 0;
border-radius: 5px;
font: inherit;
font-size: 11px;
text-align: left;
cursor: pointer;
box-sizing: border-box;
}
.hwfit-engine-item:hover,
.hwfit-engine-item.active,
.hwfit-usecase-item:hover,
.hwfit-usecase-item.active {
background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.hwfit-engine-item-label,
.hwfit-usecase-item-label {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.hwfit-quant-wrap .hwfit-quant-help,
.hwfit-engine-wrap .hwfit-engine-help {
position: absolute;
right: 20px; /* sits just left of the native select chevron */
top: 50%;
transform: translateY(-50%);
pointer-events: auto;
margin: 0;
}
.hwfit-ctx-control {
height: 28px;
min-width: 134px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
gap: 5px;
padding: 0 7px;
border: 1px solid var(--border);
border-radius: 4px;
color: var(--fg-muted);
background: var(--bg);
font-size: 10px;
box-sizing: border-box;
}
.hwfit-ctx-control span {
/* Match Quant/Engine select label style: no uppercase, no letter-spacing. */
text-transform: none;
letter-spacing: 0;
opacity: 0.9;
}
.hwfit-ctx-control input[type="range"] {
width: 64px;
min-width: 64px;
height: 4px;
padding: 0;
border: 0;
-webkit-appearance: none;
appearance: none;
/* Hardcoded grey rail — was background:transparent here, which was the
LATER-in-cascade override that kept making the rail invisible. */
background: rgba(150, 150, 150, 0.65) !important;
border-radius: 999px;
accent-color: var(--accent, var(--red));
}
.hwfit-ctx-control output {
min-width: 28px;
text-align: right;
color: var(--fg);
font-weight: 600;
}
.hwfit-server-toggle { flex-shrink: 0; font-size: 10px !important; padding: 3px 8px !important; white-space: nowrap; }
.hwfit-env-row { gap: 6px; flex-wrap: wrap; }
.hwfit-hw {
display: flex; flex-wrap: wrap; column-gap: 4px; row-gap: 7px; padding: 4px 0;
}
.hwfit-hw-chip {
font-size: 10px; padding: 0 8px; border-radius: 6px;
background: color-mix(in srgb, var(--fg) 8%, transparent);
color: var(--fg); opacity: 0.7; white-space: nowrap;
border: 0;
font-family: inherit;
line-height: 1;
height: 17px;
box-sizing: border-box;
display: inline-flex;
align-items: center;
gap: 3px;
/* Cap chip width so a long label (e.g. heterogeneous GPU group
"1× RTX 4090 + 1× RTX 3060") wraps to the next row instead of
overflowing the modal. Full text stays in the tooltip. */
max-width: 100%;
}
.hwfit-hw-chip-toggle {
/* Allow the chip body to truncate with an ellipsis when the chip
itself is capped at its container's width. Without this, the
toggle button keeps its intrinsic width and pushes the × button
off-screen on narrow viewports. */
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
.hwfit-hw-chip button,
.hwfit-hw-chip-dismiss,
.hwfit-hw-chip-manual,
.hwfit-hw-chip-toggle,
.hwfit-hw-chip-x {
appearance: none;
-webkit-appearance: none;
background: none;
border: 0;
color: inherit;
font: inherit;
padding: 0;
/* Inherit the chip's line-height so a slightly larger × glyph
doesn't push past the 17px chip height (which was clipping the
text vertically in the multi-button layout). */
line-height: inherit;
}
.hwfit-chip-x {
display: inline-block;
transform: translateY(-1px);
}
.hwfit-hw-chip-dismiss:hover { opacity: 1; }
/* Two-part chip: text body toggles dim on click, × removes the chip.
Visual minimal — just a slightly bigger × so it's easier to hit. */
.hwfit-hw-chip-toggle {
cursor: pointer;
/* `!important` because the earlier `.hwfit-hw-chip button { font:
inherit }` reset (same specificity, defined later in source)
was winning over the plain rule and leaving the manual chip's
button at the browser-default ~13px. Forcing it locally so the
manual chip's text matches every other chip's text. */
font-size: 10px !important;
line-height: 1 !important;
transform: translateY(-3px);
}
.hwfit-hw-chip-x {
cursor: pointer;
/* `!important` for the same cascade reason as the toggle button —
the `.hwfit-hw-chip button { font: inherit }` reset was leaving
the regular chips' × inheriting the chip's 10px while the
manual chip's × landed on the browser default (~13px), making
manual look bigger. Lock every × at 13px. */
font-size: 11px !important;
line-height: 1 !important;
transform: translateY(-3px);
}
.hwfit-hw-chip-x:hover {
opacity: 0.75;
color: color-mix(in srgb, var(--fg) 72%, transparent);
}
.hwfit-hw-chip-off {
/* Ghosted: drop the pill background so it reads as "off" rather than
"selected but faded". Lower text opacity matches the muted feel. */
background: transparent !important;
opacity: 0.4;
}
.hwfit-hw-chip-manual {
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 45%, transparent);
background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
color: var(--accent, var(--red));
opacity: 1;
cursor: pointer;
font-family: inherit;
margin-inline: 3px;
padding-left: 7px;
padding-right: 7px;
}
.hwfit-hw-chip-manual:hover {
background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
}
.hwfit-hw-manual-btn {
min-width: 42px;
}
.hwfit-manual-panel {
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
width: 100%;
margin-top: 6px;
padding: 8px 10px;
border: 1px solid var(--border);
border-radius: 6px;
background: color-mix(in srgb, var(--fg) 3%, transparent);
box-sizing: border-box;
}
.hwfit-manual-panel.hidden { display: none; }
.hwfit-manual-panel label {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 9px;
color: var(--fg-muted);
}
.hwfit-manual-panel select,
.hwfit-manual-panel input,
.hwfit-manual-panel button {
height: 24px;
border: 1px solid var(--border);
background: var(--bg);
color: var(--fg);
border-radius: 4px;
font: inherit;
font-size: 10px;
padding: 0 6px;
box-sizing: border-box;
}
.hwfit-manual-panel input { width: 72px; }
.hwfit-manual-panel button { cursor: pointer; }
.hwfit-manual-panel button svg {
flex: 0 0 auto;
display: inline-block;
width: 14px;
height: 14px;
}
.hwfit-manual-panel .hwfit-hw-manual-clear svg,
.hwfit-manual-panel .hwfit-hw-manual-save svg {
position: relative;
top: 2px;
color: var(--accent, var(--red));
}
.hwfit-hw-manual-btn svg,
.hwfit-manual-panel .hwfit-hw-manual-clear svg {
color: var(--accent, var(--red));
}
.hwfit-manual-panel .hwfit-hw-manual-clear > span,
.hwfit-manual-panel .hwfit-hw-manual-save > span {
position: relative;
top: -2px;
}
.hwfit-manual-panel .hwfit-hw-manual-save {
margin-left: auto;
}
.hwfit-manual-panel button:hover { border-color: var(--fg); }
/* GPU driver error (e.g. NVML version mismatch) — stands out from the muted
chips and hints there's a real problem, with the full message on hover. */
.hwfit-hw-chip-error {
background: color-mix(in srgb, var(--red) 16%, transparent);
color: var(--red); opacity: 1; cursor: help;
border: 1px solid color-mix(in srgb, var(--red) 40%, transparent);
}
.hwfit-list {
max-height: 52vh; overflow-y: auto; display: flex; flex-direction: column; gap: 2px;
}
.hwfit-loading {
display: flex; align-items: center; justify-content: center;
color: var(--fg-muted); padding: 16px 0; font-size: 12px;
}
.hwfit-hw-visibility-warning {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
text-align: left;
margin-top: 8px;
}
.hwfit-hw-visibility-warning-title {
font-weight: 600;
}
.hwfit-hw-visibility-warning-body {
opacity: 0.78;
line-height: 1.45;
}
.hwfit-hw-visibility-warning-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.hwfit-row {
display: flex; align-items: center; gap: 6px; padding: 5px 8px;
border-radius: 6px; cursor: pointer; font-size: 11px;
transition: background 0.1s;
}
.hwfit-row:hover { background: color-mix(in srgb, var(--fg) 6%, transparent); }
/* Scan/Download model rows enter like the other library lists. Keep the
column header static and only replay this when the list had no model rows,
so ordinary filtering and sorting remain instant. */
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:not(.hwfit-header) {
animation: section-domino-in 0.36s cubic-bezier(0.22, 1.61, 0.36, 1) backwards;
}
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(2) { animation-delay: 0.04s; }
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(3) { animation-delay: 0.08s; }
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(4) { animation-delay: 0.12s; }
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(5) { animation-delay: 0.16s; }
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(6) { animation-delay: 0.20s; }
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(7) { animation-delay: 0.24s; }
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(8) { animation-delay: 0.28s; }
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(9) { animation-delay: 0.32s; }
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(10) { animation-delay: 0.36s; }
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(11) { animation-delay: 0.40s; }
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(12) { animation-delay: 0.44s; }
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(13) { animation-delay: 0.48s; }
.hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:nth-child(n+14) { animation-delay: 0.48s; }
/* Already-downloaded rows: dim slightly so attention falls on undownloaded
options. The green dot stays bright as the "this is local" cue. */
.hwfit-row:has(.hwfit-dl-dot) { opacity: 0.55; }
.hwfit-header {
cursor: default; position: sticky; top: 0; z-index: 1;
background: var(--panel); border-bottom: 1px solid var(--border);
padding: 4px 8px; font-weight: 600;
}
.hwfit-header:hover { background: var(--panel); }
.hwfit-header .hwfit-col { font-size: 9px; color: var(--fg-muted); text-transform: uppercase; letter-spacing: 0.3px; }
.hwfit-sortable:hover { color: var(--fg) !important; }
.hwfit-sort-active { color: var(--red) !important; }
.hwfit-col {
flex-shrink: 0; white-space: nowrap; text-align: left;
font-size: 9px; color: var(--fg-muted);
}
.hwfit-fit { width: 52px; font-weight: 700; text-transform: uppercase; }
.hwfit-name {
flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
font-weight: 500; font-size: 11px; color: var(--fg);
}
.hwfit-c-score { width: 40px; font-weight: 700; font-size: 11px; color: var(--fg); }
.hwfit-moe {
display: inline-block; padding: 0 4px; border-radius: 4px; margin-left: 4px;
background: color-mix(in srgb, var(--red) 15%, transparent);
color: var(--red); font-weight: 600; font-size: 8px;
}
.hwfit-sort, .hwfit-quant { width: auto; min-width: 70px; flex-shrink: 0; }
.hwfit-row-active { background: color-mix(in srgb, var(--red) 8%, transparent); }
/* ── Inline action panel (expands below a model row) ── */
.hwfit-action-panel {
border: 1px solid var(--border); border-left: 3px solid var(--red);
border-radius: 0 6px 6px 6px; background: var(--panel);
padding: 10px 12px; margin: 2px 0 6px; font-size: 11px;
display: flex; flex-direction: column; gap: 8px;
animation: hwfit-panel-in 0.15s ease-out;
}
.hwfit-panel-header {
display: flex; align-items: center; gap: 8px; min-width: 0;
}
.hwfit-panel-model {
font-size: 11px; font-weight: 600; color: var(--fg);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0;
}
.hwfit-panel-badge {
font-size: 9px; padding: 2px 8px; border-radius: 10px;
background: color-mix(in srgb, var(--red) 12%, transparent);
color: var(--red); font-weight: 600; flex-shrink: 0;
}
.hwfit-panel-fields {
display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
}
.hwfit-panel-fields label, .hwfit-adv-grid label {
display: flex; align-items: center; gap: 4px; font-size: 10px; color: var(--fg-muted); white-space: nowrap;
}
.hwfit-panel-fields input, .hwfit-panel-fields select,
.hwfit-adv-grid input, .hwfit-adv-grid select {
background: var(--bg); border: 1px solid var(--border); border-radius: 4px;
color: var(--fg); font-size: 11px; padding: 3px 6px; font-family: inherit;
outline: none; transition: border-color 0.15s;
}
.hwfit-panel-fields input:focus, .hwfit-adv-grid input:focus,
.hwfit-panel-fields select:focus, .hwfit-adv-grid select:focus {
border-color: var(--red);
}
.hwfit-panel-fields input[type="text"] { width: 60px; }
.hwfit-adv-grid input[type="text"] { width: 80px; }
.hwfit-adv-grid {
display: flex; flex-wrap: wrap; gap: 6px 12px; padding: 6px 0;
}
.hwfit-cb input[type="checkbox"] { margin: 0 2px 0 0; }
.hwfit-panel-advanced, .hwfit-panel-settings {
font-size: 10px;
}
.hwfit-panel-advanced summary, .hwfit-panel-settings summary {
cursor: pointer; color: var(--fg-muted); font-size: 10px;
user-select: none; padding: 2px 0;
}
.hwfit-panel-advanced summary:hover, .hwfit-panel-settings summary:hover { color: var(--fg); }
.hwfit-panel-cmd {
font-family: 'Berkeley Mono', 'SF Mono', 'Fira Code', monospace;
font-size: 10px; padding: 6px 8px; border-radius: 4px;
background: var(--bg); border: 1px solid var(--border);
white-space: pre-wrap; word-break: break-all; max-height: 60px; overflow-y: auto;
color: var(--fg-muted);
}
.hwfit-panel-actions {
display: flex; gap: 4px; flex-wrap: wrap;
}
.hwfit-panel-note {
font-size: 10px;
line-height: 1.35;
color: var(--fg-muted);
background: color-mix(in srgb, var(--yellow, #f1fa8c) 8%, transparent);
border: 1px solid color-mix(in srgb, var(--yellow, #f1fa8c) 18%, var(--border));
border-radius: 4px;
padding: 5px 7px;
}
/* ── Saved presets ── */
.hwfit-preset {
display: flex; align-items: center; gap: 8px; padding: 6px 10px;
border: 1px solid var(--border); border-radius: 6px; font-size: 11px;
transition: border-color 0.15s;
}
.hwfit-preset:hover { border-color: var(--red); }
.hwfit-preset-name { font-weight: 600; color: var(--fg); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hwfit-preset-model { font-size: 9px; color: var(--fg-muted); max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hwfit-preset-backend { font-size: 9px; padding: 1px 6px; border-radius: 8px; background: color-mix(in srgb, var(--fg) 8%, transparent); color: var(--fg-muted); }
@media (max-width: 600px) {
.hwfit-c-ctx, .hwfit-c-speed, .hwfit-c-mode { display: none; }
.hwfit-panel-fields { flex-direction: column; align-items: stretch; }
.hwfit-panel-fields input[type="text"] { width: 100%; }
.hwfit-preset-model { display: none; }
}
/* ===== Settings Modal Layout ===== */
.settings-modal-content {
width: min(1040px, 94vw);
max-height: 85vh;
padding: 0;
container-type: inline-size;
container-name: settings-modal;
}
/* Issue #208 — anchor the Settings window to the TOP of the chat area instead
of vertically centering it. The base .modal uses `align-items:center`, so a
centered window grows and shrinks around its own midpoint when you switch
between tabs whose content differs in height (Add Models vs. Shortcuts,
etc.). That makes the in-modal nav rail — and the whole window — appear to
jump up and down between pages. Pinning the top edge keeps the nav rail and
surrounding layout visually stable; the panel only ever grows downward.
Desktop only: on mobile the panel is a full-height bottom sheet that is
already top-stable, and a margin there would push it past the viewport. The
drag/dock code clears this margin (sets inline margin:0) the moment a window
is dragged, so moving the window still works exactly as before. */
@media (min-width: 769px) {
#settings-modal { align-items: flex-start; }
#settings-modal .settings-modal-content { margin-top: 7vh; }
}
.settings-modal-content .modal-header {
padding: 16px 20px;
border-bottom: 1px solid var(--border);
}
.settings-layout {
display: flex;
min-height: 400px;
max-height: calc(85vh - 60px);
}
.settings-modal-content[style*="height"],
#settings-modal.modal-right-docked .settings-modal-content {
overflow: hidden;
}
.settings-modal-content[style*="height"] .settings-layout,
#settings-modal.modal-right-docked .settings-layout {
flex: 1 1 0;
min-height: 0;
max-height: none;
}
.settings-modal-content[style*="height"] .settings-panels,
#settings-modal.modal-right-docked .settings-panels,
.settings-modal-content[style*="height"] .settings-sidebar,
#settings-modal.modal-right-docked .settings-sidebar {
min-height: 0;
}
.settings-sidebar {
position: relative;
width: var(--settings-sidebar-width, 220px);
flex: 0 0 var(--settings-sidebar-width, 220px);
min-width: 0;
border-right: 1px solid var(--border);
padding: 8px;
transition: width 0.16s ease, flex-basis 0.16s ease;
display: flex;
flex-direction: column;
gap: 2px;
background: color-mix(in srgb, var(--fg) 2%, transparent);
}
.settings-sidebar-divider { height: 1px; background: var(--border); margin: 8px 12px; }
.settings-sidebar-label {
font-size: 9px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
opacity: 0.35;
padding: 4px 12px 2px;
}
.settings-nav-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 10px;
border: none;
background: none;
color: color-mix(in srgb, var(--fg) 60%, transparent);
font-family: inherit;
font-size: 12px;
font-weight: 500;
cursor: pointer;
border-radius: 6px;
transition: all 0.1s;
text-align: left;
white-space: nowrap;
}
.settings-nav-item:hover {
background: color-mix(in srgb, var(--fg) 8%, transparent);
color: var(--fg);
}
.settings-nav-item.active {
background: color-mix(in srgb, var(--red) 12%, transparent);
color: var(--red);
}
.settings-nav-item svg {
flex-shrink: 0;
opacity: 0.7;
}
.settings-nav-item.active svg {
opacity: 1;
}
.settings-sidebar-divider { height: 1px; background: var(--border); margin: 8px 12px; }
/* Keyboard shortcuts */
.shortcut-category {
font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;
opacity: 0.4; padding: 8px 0 4px; margin-top: 4px;
}
.shortcut-category:first-child { margin-top: 0; padding-top: 0; }
.shortcut-row {
display: flex; align-items: center; justify-content: space-between;
padding: 5px 0; border-bottom: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
}
.shortcut-row:last-child { border-bottom: none; }
.shortcut-row.shortcut-conflict { background: color-mix(in srgb, var(--warn) 6%, transparent); border-radius: 4px; padding: 5px 6px; }
.shortcut-label { font-size: 12px; display: flex; align-items: center; gap: 6px; }
.shortcut-icon { display: inline-flex; opacity: 0.5; flex-shrink: 0; }
.shortcut-warn {
display: inline-flex; align-items: center; justify-content: center;
width: 14px; height: 14px; border-radius: 50%; background: var(--warn); color: #fff;
font-size: 9px; font-weight: 700; margin-left: 4px;
}
.shortcut-controls { display: flex; align-items: center; gap: 4px; }
/* Inline hint shown while rebinding a shortcut ("press a key" → "↵ Enter
to save"). Subtle so it doesn't compete with the key caps. */
.shortcut-hint {
font-size: 10px;
opacity: 0.6;
color: var(--accent, var(--red));
white-space: nowrap;
margin-right: 2px;
}
.shortcut-hint[hidden] { display: none; }
.shortcut-key {
font-family: inherit; font-size: 0; padding: 2px 4px;
background: transparent; border: none; border-radius: 4px;
color: var(--fg); cursor: pointer; display: flex; align-items: center; gap: 2px;
transition: all 0.15s;
}
.shortcut-key:hover { background: color-mix(in srgb, var(--accent, #cc6a3a) 8%, transparent); }
/* Unbound shortcut — show a dashed "Set" placeholder instead of keycaps. */
.shortcut-key-unset { font-size: 10px; }
.shortcut-unset {
font-size: 10px;
padding: 2px 8px;
border: 1px dashed var(--border);
border-radius: 4px;
color: color-mix(in srgb, var(--fg) 45%, transparent);
}
.shortcut-key-unset:hover .shortcut-unset { border-color: var(--accent, var(--red)); color: var(--fg); }
.shortcut-key kbd {
display: inline-block; font-family: inherit; font-size: 10px;
padding: 2px 6px; min-width: 20px; text-align: center;
/* Highlight kbd chips in the theme accent so they stand out from
normal text and clearly mark "this is a key you press". */
background: color-mix(in srgb, var(--accent, var(--red)) 14%, var(--bg));
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 40%, var(--border));
color: var(--accent, var(--red));
border-radius: 3px;
box-shadow: 0 1px 0 color-mix(in srgb, var(--accent, var(--red)) 25%, transparent);
line-height: 1.4;
font-weight: 600;
}
.shortcut-key.listening {
background: color-mix(in srgb, var(--accent, #cc6a3a) 10%, transparent);
animation: shortcut-pulse 1s infinite;
}
.shortcut-key.listening kbd { border-color: var(--accent, #cc6a3a); }
.shortcut-action-btn {
width: 24px; height: 24px; border-radius: 4px; border: 1px solid var(--border);
background: transparent; color: var(--fg); cursor: pointer; font-size: 13px;
display: flex; align-items: center; justify-content: center; transition: all 0.15s;
}
.shortcut-action-btn:not(.is-reset):hover { border-color: var(--accent, #cc6a3a); background: color-mix(in srgb, var(--accent, #cc6a3a) 10%, var(--bg)); }
.shortcut-action-btn.is-reset { opacity: 0.55; border-color: transparent; background: transparent; }
.shortcut-action-btn.is-reset:hover,
.shortcut-action-btn.is-reset:focus,
.shortcut-action-btn.is-reset:active { opacity: 0.55; background: transparent; border-color: transparent; }
@keyframes shortcut-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
.settings-panels {
flex: 1;
overflow-y: auto;
padding: 16px 20px;
min-width: 0;
}
.settings-appearance-panel:not(.hidden) {
display: flex;
}
.settings-appearance-panel > .admin-card:nth-of-type(1) { order: 3; }
.settings-appearance-panel > .admin-card:nth-of-type(2) { order: 1; }
.settings-appearance-panel > .admin-card:nth-of-type(3) { order: 2; }
.settings-appearance-panel > .admin-card:nth-of-type(n+4) { order: 4; }
/* Mobile: stack tabs on top */
@media (max-width: 600px) {
.settings-layout {
flex-direction: column;
}
.settings-sidebar {
width: auto;
flex-direction: row;
border-right: none;
border-bottom: 1px solid var(--border);
overflow-x: auto;
padding: 6px;
}
.settings-nav-item {
padding: 6px 10px;
font-size: 11px;
}
}