Homelabtoolkit v1

This commit is contained in:
2026-06-08 00:01:55 +12:00
parent c8838a485d
commit 040fbacc70
56 changed files with 12477 additions and 151 deletions
+799
View File
@@ -0,0 +1,799 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
/* ============================================================================
EmbyToolkit — Tracearr-modeled design system
Authored as a single overriding layer. Tokens use :root:root so they win over
each page's legacy inline :root, recoloring existing var() usage for free.
========================================================================== */
:root:root {
/* Canvas + surfaces — near-black, cool cyan-tinted neutrals */
--bg: #0a0c0f;
--bg-2: #0c0f13;
--surface: #101419;
--surface2: #151a21;
--surface3: #1b222b;
--surface4: #232c37;
/* Lines */
--border: rgba(151, 167, 187, 0.12);
--border-strong: rgba(151, 167, 187, 0.22);
--border-active: #36d6e0;
/* Text */
--text: #e8edf3;
--text-2: #9aa7b6;
--text-3: #5e6b7b;
/* Accent — vivid cyan */
--accent: #36d6e0;
--accent-h: #5ee7ef;
--accent-2: #2bb6c4;
--accent-glow: rgba(54, 214, 224, 0.15);
--accent-soft: rgba(54, 214, 224, 0.12);
/* Status */
--green: #46d99a;
--green-bg: rgba(70, 217, 154, 0.12);
--green-bd: rgba(70, 217, 154, 0.30);
--amber: #f3c969;
--amber-bg: rgba(243, 201, 105, 0.12);
--amber-bd: rgba(243, 201, 105, 0.30);
--red: #f0726f;
--red-bg: rgba(240, 114, 111, 0.12);
--red-bd: rgba(240, 114, 111, 0.30);
/* Elevation + radii */
--shadow-soft: 0 14px 38px rgba(2, 5, 10, 0.42);
--shadow-strong: 0 26px 60px rgba(2, 5, 10, 0.55);
--r: 10px;
--r-lg: 14px;
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
}
html { color-scheme: dark; }
body,
input,
button,
select,
textarea {
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
}
body {
background:
radial-gradient(900px 420px at 78% -8%, rgba(54, 214, 224, 0.07), transparent 60%),
var(--bg) !important;
color: var(--text);
}
::selection { background: var(--accent-glow); color: var(--text); }
/* Tabular numerics for data-ish fields */
.results-count,
.results-page,
.selection-meta,
.pager-meta,
.toolbar-meta,
.hero-badge,
.preview-meta,
.thumb-preview-meta,
.primary-preview-note,
.slider-val,
.asset-count,
.bd-counter,
.stat-value,
.cell-num,
.trust-score {
font-variant-numeric: tabular-nums;
letter-spacing: 0.01em;
}
/* Scrollbars */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-thumb {
background: var(--border-strong);
border-radius: 99px;
border: 2px solid transparent;
background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--text-3); background-clip: padding-box; }
/* ============================================================================
Sidebar
========================================================================== */
.app-nav {
width: 236px !important;
background: var(--surface) !important;
border-right: 1px solid var(--border) !important;
backdrop-filter: blur(14px);
}
.app-nav-brand {
padding: 18px 16px 14px !important;
gap: 11px !important;
border-bottom: 1px solid var(--border) !important;
}
.app-nav-logo {
width: 30px !important;
height: 30px !important;
border-radius: 9px !important;
background: linear-gradient(155deg, #5ee7ef 0%, #36d6e0 45%, #1f9aa6 100%) !important;
color: #04181b !important;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.35),
0 4px 14px rgba(54, 214, 224, 0.28) !important;
}
.app-nav-name {
font-size: 15px !important;
font-weight: 700 !important;
letter-spacing: -0.02em !important;
color: var(--text) !important;
}
/* Emby connection status chip (reuses #statusDot / #statusText) */
.app-nav-status {
display: flex;
align-items: center;
gap: 8px;
margin: 12px 12px 4px;
padding: 8px 11px;
border-radius: 9px;
background: var(--surface2);
border: 1px solid var(--border);
font-size: 12px;
font-weight: 600;
color: var(--text-2);
}
.app-nav-items {
padding: 8px 10px !important;
display: flex;
flex-direction: column;
gap: 2px;
}
.app-nav-item {
position: relative;
min-height: 38px;
gap: 11px !important;
padding: 8px 12px !important;
border: 1px solid transparent;
border-radius: 9px !important;
font-size: 13px !important;
font-weight: 500 !important;
color: var(--text-2) !important;
transition: background 160ms var(--ease-out), color 160ms var(--ease-out) !important;
}
.app-nav-item svg { opacity: 0.9; }
.app-nav-item:hover {
background: var(--surface2) !important;
border-color: transparent !important;
color: var(--text) !important;
}
.app-nav-item.active {
background: var(--accent-soft) !important;
border-color: transparent !important;
color: var(--accent-h) !important;
}
.app-nav-item.active svg { opacity: 1; }
/* Inset indicator pill (not a side-stripe border) */
.app-nav-item.active::before {
content: "";
position: absolute;
left: 4px;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 16px;
border-radius: 99px;
background: var(--accent);
box-shadow: 0 0 10px var(--accent-glow);
}
/* Footer: status + social + version */
.app-nav-footer,
.app-nav-foot {
padding: 12px 14px !important;
border-top: 1px solid var(--border) !important;
display: flex;
flex-direction: column;
gap: 12px;
flex-shrink: 0;
}
.app-nav-foot .app-nav-status { margin: 0; }
.app-nav-social {
display: flex;
align-items: center;
gap: 4px;
}
.app-nav-social a {
width: 30px;
height: 30px;
display: grid;
place-items: center;
border-radius: 8px;
color: var(--text-3);
transition: background 150ms var(--ease-out), color 150ms var(--ease-out);
}
.app-nav-social a:hover { background: var(--surface2); color: var(--accent-h); }
.app-nav-version {
font-size: 11px;
color: var(--text-3);
letter-spacing: 0.02em;
font-variant-numeric: tabular-nums;
}
.dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--green) !important;
box-shadow: 0 0 0 3px var(--green-bg), 0 0 8px var(--green) !important;
flex-shrink: 0;
}
.dot.off { background: var(--red) !important; box-shadow: 0 0 0 3px var(--red-bg), 0 0 8px var(--red) !important; }
/* ============================================================================
Secondary sidebar (search + results column on Generator / Collections)
One shared 16px gutter down the whole column so the search field, the
results-count row, every list item, and the pager align on the same left
edge. The header zone matches the primary nav brand height (64px) so the two
sidebars' top dividers line up across the seam.
========================================================================== */
.sidebar { background: rgba(16, 20, 25, 0.92) !important; }
.search-wrap {
display: flex;
flex-direction: column;
justify-content: center;
gap: 10px;
min-height: 64px;
padding: 13px 16px !important;
border-bottom: 1px solid var(--border) !important;
}
.results-toolbar,
.results-footer {
padding: 12px 16px !important;
gap: 10px;
}
.results-count {
font-size: 11px !important;
font-weight: 600 !important;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-3) !important;
}
.results-page { color: var(--text-3) !important; }
.results { padding: 8px !important; }
.results .result-item + .result-item { margin-top: 2px; }
.result-item {
gap: 11px !important;
padding: 9px 8px !important; /* 8 (results) + 8 = 16px content gutter */
border-radius: 9px !important;
}
.result-poster {
width: 38px !important;
height: 57px !important;
border-radius: 6px !important;
}
.result-name { font-size: 13px !important; }
.empty { padding: 48px 24px !important; line-height: 1.5; }
/* Align the primary nav brand divider with the search-wrap divider */
.app-nav-brand {
min-height: 64px;
padding: 0 16px !important;
align-items: center;
}
/* ============================================================================
Page chrome + header
========================================================================== */
.page,
.main,
.body { background: transparent !important; }
.app-topbar {
background: rgba(16, 20, 25, 0.86) !important;
border-bottom: 1px solid var(--border) !important;
backdrop-filter: blur(14px);
}
.hero {
align-items: flex-start !important;
}
.hero-copy h2,
.collection-name,
.card-title,
.panel-head h3 {
letter-spacing: -0.02em !important;
color: var(--text) !important;
}
.hero-copy h2 {
font-size: 28px !important;
font-weight: 700 !important;
}
.hero-copy p {
color: var(--text-2) !important;
max-width: 72ch;
line-height: 1.6;
}
.hero-copy p strong { color: var(--text); font-weight: 600; }
.hero-badge {
align-self: center;
background: var(--surface2) !important;
border: 1px solid var(--border) !important;
border-radius: 999px !important;
color: var(--text-2) !important;
font-weight: 600;
letter-spacing: 0.02em;
}
/* ============================================================================
Surfaces — toolbars, panels, cards
========================================================================== */
.sidebar,
.controls,
.results-toolbar,
.results-footer,
.toolbar,
.panel {
background: rgba(16, 20, 25, 0.88) !important;
border-color: var(--border) !important;
backdrop-filter: blur(14px);
}
.toolbar,
.panel,
.card,
.preview-shell,
.dropzone,
.asset-card,
.preview-frame,
.primary-preview-frame {
border-radius: var(--r-lg) !important;
box-shadow: var(--shadow-soft);
}
.toolbar,
.panel,
.card {
background: linear-gradient(180deg, rgba(19, 24, 31, 0.96), rgba(13, 17, 22, 0.96)) !important;
border: 1px solid var(--border) !important;
}
.panel-head { border-color: var(--border) !important; }
.preview-shell {
background:
radial-gradient(circle at top right, rgba(54, 214, 224, 0.1), transparent 32%),
linear-gradient(180deg, rgba(22, 28, 37, 0.94), rgba(11, 15, 21, 0.98)) !important;
border: 1px solid var(--border) !important;
}
.preview-frame,
.primary-preview-frame {
background: #0d141c !important;
box-shadow: var(--shadow-strong) !important;
}
.hero-badge,
.preview-meta,
.thumb-preview-tools,
.preview-tools,
.thumb-preview-hint,
.preview-hint,
.primary-preview-hint,
.bd-counter {
background: rgba(8, 12, 17, 0.8) !important;
border-color: var(--border-strong) !important;
}
/* ============================================================================
Stat cards (Tracearr summary row) — available vocabulary for the pages
========================================================================== */
.stat-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 14px;
}
.stat-card {
display: flex;
align-items: center;
gap: 14px;
padding: 18px 20px;
border-radius: var(--r-lg);
background: linear-gradient(180deg, rgba(19, 24, 31, 0.96), rgba(13, 17, 22, 0.96));
border: 1px solid var(--border);
box-shadow: var(--shadow-soft);
}
.stat-icon {
width: 38px;
height: 38px;
border-radius: 10px;
display: grid;
place-items: center;
background: var(--accent-soft);
color: var(--accent-h);
flex-shrink: 0;
}
.stat-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.stat-value { font-size: 22px; font-weight: 700; letter-spacing: -0.02em; color: var(--text); }
.stat-label { font-size: 12px; color: var(--text-2); }
/* ============================================================================
Segmented controls / date pills + dropdown buttons
========================================================================== */
.seg {
display: inline-flex;
padding: 3px;
gap: 2px;
border-radius: 10px;
background: var(--surface2);
border: 1px solid var(--border);
}
.seg-btn {
border: 0 !important;
background: transparent !important;
color: var(--text-2) !important;
padding: 6px 12px !important;
border-radius: 8px !important;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: background 150ms var(--ease-out), color 150ms var(--ease-out) !important;
}
.seg-btn:hover:not(:disabled) { color: var(--text) !important; background: transparent !important; }
.seg-btn.active {
background: var(--surface4) !important;
color: var(--text) !important;
box-shadow: inset 0 0 0 1px var(--border-strong);
}
/* ============================================================================
Inputs + buttons
========================================================================== */
.search-input,
.ctrl-input,
.ctrl-select,
.ctrl-textarea,
.color-wrap input[type="text"],
.color-wrap input[type="color"],
.picker-row input[type="text"],
.picker-row input[type="color"],
.btn,
.pager-btn,
.tab-btn,
.seg-btn,
.slider-step,
.asset-btn,
.chip-toggle,
.toggle-chip,
.studio-reset-btn,
.studio-bulk-btn,
.icon-btn {
border-radius: 10px !important;
}
.search-input,
.ctrl-input,
.ctrl-select,
.ctrl-textarea,
.color-wrap input[type="text"],
.color-wrap input[type="color"],
.picker-row input[type="text"],
.picker-row input[type="color"],
.asset-btn,
.slider-step,
.btn,
.pager-btn,
.tab-btn,
.toggle-chip,
.chip-toggle,
.studio-reset-btn,
.icon-btn {
background: var(--surface2) !important;
border: 1px solid var(--border) !important;
color: var(--text) !important;
}
.search-input::placeholder,
.ctrl-input::placeholder,
.ctrl-textarea::placeholder { color: var(--text-3) !important; }
.search-input:focus,
.ctrl-input:focus,
.ctrl-select:focus,
.ctrl-textarea:focus,
.color-wrap input[type="text"]:focus,
.picker-row input[type="text"]:focus {
border-color: var(--border-active) !important;
box-shadow: 0 0 0 3px var(--accent-glow) !important;
outline: none;
}
.search-inner svg { color: var(--text-3); }
.btn,
.pager-btn,
.tab-btn,
.seg-btn,
.slider-step,
.asset-btn,
.toggle-chip,
.chip-toggle,
.studio-bulk-btn,
.studio-reset-btn,
.icon-btn {
transition:
background 160ms var(--ease-out),
border-color 160ms var(--ease-out),
color 160ms var(--ease-out),
transform 160ms var(--ease-out),
opacity 160ms var(--ease-out) !important;
}
.btn:hover:not(:disabled),
.pager-btn:hover:not(:disabled),
.tab-btn:hover:not(:disabled),
.slider-step:hover:not(:disabled),
.asset-btn:hover:not(:disabled),
.toggle-chip:hover:not(:disabled),
.chip-toggle:hover:not(:disabled),
.icon-btn:hover:not(:disabled) {
background: var(--surface3) !important;
border-color: var(--border-strong) !important;
}
.btn:active:not(:disabled),
.pager-btn:active:not(:disabled) { transform: translateY(1px); }
.btn-primary,
.studio-bulk-btn {
background: var(--accent) !important;
border-color: rgba(54, 214, 224, 0.4) !important;
color: #04181b !important;
font-weight: 600;
}
.btn-primary:hover:not(:disabled),
.studio-bulk-btn:hover:not(:disabled) {
background: var(--accent-h) !important;
border-color: rgba(94, 231, 239, 0.5) !important;
}
.btn-green {
background: var(--green-bg) !important;
border-color: var(--green-bd) !important;
color: #b7f0d4 !important;
}
.btn-green:hover:not(:disabled) { background: rgba(70, 217, 154, 0.22) !important; }
/* Active / selected states across pickers */
.tab-btn.active,
.toggle-chip.active,
.pos-btn.active,
.corner-btn.active,
.studio-btn.active,
.result-item.active {
background: var(--accent-soft) !important;
border-color: var(--border-active) !important;
color: var(--text) !important;
}
.result-item:hover,
.asset-card:hover { background: var(--surface2) !important; }
.result-poster { background: var(--surface3) !important; }
.results::-webkit-scrollbar-thumb { background: var(--border-strong); }
/* ============================================================================
Tables (Tracearr history/users vocabulary)
========================================================================== */
.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.data-table thead th {
text-align: left;
padding: 11px 14px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-3);
border-bottom: 1px solid var(--border);
white-space: nowrap;
}
.data-table tbody td {
padding: 12px 14px;
border-bottom: 1px solid var(--border);
color: var(--text-2);
vertical-align: middle;
}
.data-table tbody tr { transition: background 140ms var(--ease-out); }
.data-table tbody tr:hover { background: rgba(54, 214, 224, 0.04); }
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table .cell-strong { color: var(--text); font-weight: 600; }
.data-table .cell-sub { color: var(--text-3); font-size: 12px; }
.data-table .cell-num { color: var(--text); font-variant-numeric: tabular-nums; }
/* Avatars */
.avatar {
width: 30px;
height: 30px;
border-radius: 50%;
display: grid;
place-items: center;
font-size: 11px;
font-weight: 700;
color: #04181b;
background: linear-gradient(155deg, #5ee7ef, #2bb6c4);
flex-shrink: 0;
}
/* Status badges */
.badge {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 2px 9px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.02em;
border: 1px solid var(--border);
background: var(--surface3);
color: var(--text-2);
}
.badge-ok,
.badge-watched,
.badge-direct,
.badge-trusted {
background: var(--green-bg);
border-color: var(--green-bd);
color: #b7f0d4;
}
.badge-warn,
.badge-sampled {
background: var(--amber-bg);
border-color: var(--amber-bd);
color: #f6d98c;
}
.badge-bad,
.badge-abandoned {
background: var(--red-bg);
border-color: var(--red-bd);
color: #ffb4b2;
}
/* Trust pill */
.trust-pill {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 3px 10px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
background: var(--green-bg);
border: 1px solid var(--green-bd);
color: #b7f0d4;
}
/* Thin progress bar */
.bar {
width: 84px;
height: 5px;
border-radius: 99px;
background: var(--surface4);
overflow: hidden;
}
.bar-fill {
height: 100%;
border-radius: 99px;
background: linear-gradient(90deg, var(--accent-2), var(--accent));
}
/* Generic chips / tags / pills used by the result lists */
.result-sub .tag,
.tag,
.meta-chip,
.pill,
.asset-pill {
letter-spacing: 0.03em !important;
}
.result-sub .tag,
.tag,
.meta-chip,
.pill-muted,
.asset-pill.off {
background: var(--surface3) !important;
border-color: var(--border) !important;
color: var(--text-2) !important;
}
.pill-green,
.meta-chip.ok,
.asset-pill {
background: var(--green-bg) !important;
border-color: var(--green-bd) !important;
color: #b7f0d4 !important;
}
/* ============================================================================
Sliders + spinner accent
========================================================================== */
input[type="range"] { accent-color: var(--accent); }
.spinner { border-color: var(--border) !important; border-top-color: var(--accent) !important; }
/* ============================================================================
Toast
========================================================================== */
.toast { border-radius: 12px !important; box-shadow: var(--shadow-soft); }
.toast.ok {
background: var(--green-bg) !important;
border: 1px solid var(--green-bd) !important;
color: #b7f0d4 !important;
}
.toast.err {
background: var(--red-bg) !important;
border: 1px solid var(--red-bd) !important;
color: #ffb4b2 !important;
}