diff --git a/services/web-ui/public/styles-rest.css b/services/web-ui/public/styles-rest.css index 0f5183d..62c78e0 100644 --- a/services/web-ui/public/styles-rest.css +++ b/services/web-ui/public/styles-rest.css @@ -1019,6 +1019,38 @@ overflow: hidden; } +.stat-row { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); + gap: 10px; +} +.stat-card { + background: var(--bg-1); + border: 1px solid var(--border); + border-radius: var(--r-lg); + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 4px; +} +.stat-card .label { + font-size: 10.5px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--text-4); + display: flex; + align-items: center; + gap: 5px; +} +.stat-card .value { + font-size: 28px; + font-weight: 600; + letter-spacing: -0.02em; + color: var(--text-1); + line-height: 1.1; +} + /* ========== Settings ========== */ .settings-nav { display: flex; flex-direction: column; gap: 2px; diff --git a/services/web-ui/public/styles-screens.css b/services/web-ui/public/styles-screens.css index 92792b9..fce0775 100644 --- a/services/web-ui/public/styles-screens.css +++ b/services/web-ui/public/styles-screens.css @@ -470,6 +470,525 @@ text-align: right; } +/* ========== Dashboard page ========== */ +.page.dash { + padding: 0 28px 12px; +} + +.dash-section { + margin-bottom: 20px; +} +.dash-section:last-child { margin-bottom: 0; } + +.dash-sectionhead { + display: flex; + align-items: center; + gap: 8px; + padding: 16px 0 10px; + position: relative; +} +.dash-sectionhead[data-accent=live] .dash-sectionhead-title { color: var(--live); } +.dash-sectionhead[data-accent=danger] .dash-sectionhead-title { color: var(--danger); } +.dash-sectionhead-title { + font-size: 14px; + font-weight: 600; + color: var(--text-1); + letter-spacing: -0.01em; +} +.dash-sectionhead-count { + display: flex; + align-items: center; + gap: 4px; + margin-left: 2px; +} +.dash-sectionhead-num { + font-family: var(--font-mono); + font-size: 12px; + font-weight: 600; + color: var(--text-2); +} +.dash-sectionhead-label { + font-size: 11.5px; + color: var(--text-3); +} +.dash-sectionhead-more { + margin-left: auto; + font-size: 11.5px; + color: var(--text-3); + background: none; + border: 0; + padding: 4px 6px; + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + gap: 4px; + transition: background 100ms, color 100ms; +} +.dash-sectionhead-more:hover { + background: var(--bg-2); + color: var(--text-1); +} + +/* On-air grid */ +.dash-onair-grid { + display: grid; + gap: 10px; +} +.dash-onair-grid[data-count="1"] { grid-template-columns: 1fr; } +.dash-onair-grid[data-count="2"] { grid-template-columns: 1fr 1fr; } +.dash-onair-grid[data-count="3"] { grid-template-columns: 1fr 1fr 1fr; } +.dash-onair-grid[data-count="4"] { grid-template-columns: 1fr 1fr; } + +.dash-onair-tile { + background: var(--bg-1); + border: 1px solid var(--border); + border-radius: var(--r-lg); + overflow: hidden; + cursor: pointer; + transition: border-color 120ms; +} +.dash-onair-tile:hover { border-color: var(--border-stronger); } + +.dash-onair-video { + position: relative; + aspect-ratio: 16/9; + background: var(--bg-2); + overflow: hidden; +} +.dash-onair-rec-pip { + position: absolute; + top: 8px; left: 8px; + display: flex; + align-items: center; + gap: 5px; + font-size: 10.5px; + font-weight: 600; + color: white; + background: var(--danger); + padding: 2px 7px; + border-radius: 4px; + line-height: 1.3; +} +.dash-onair-rec-dot { + width: 6px; height: 6px; + border-radius: 50%; + background: white; + animation: dash-rec-pulse 1.5s ease-in-out infinite; +} +@keyframes dash-rec-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.3; } +} +.dash-onair-time { + position: absolute; + bottom: 8px; right: 8px; + font-family: var(--font-mono); + font-size: 11px; + color: white; + background: rgba(0,0,0,0.65); + padding: 2px 7px; + border-radius: 4px; + backdrop-filter: blur(4px); +} +.dash-onair-meta { + padding: 10px 12px; +} +.dash-onair-name { + font-size: 13px; + font-weight: 500; + color: var(--text-1); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.dash-onair-sub { + display: flex; + align-items: center; + gap: 4px; + margin-top: 3px; + font-size: 11.5px; + color: var(--text-3); +} +.dash-onair-dot { + color: var(--text-4); +} +.dash-onair-source, +.dash-onair-res, +.dash-onair-codec { font-family: var(--font-mono); font-size: 10.5px; } + +/* Inline empty state */ +.dash-inline-empty { + display: flex; + align-items: center; + gap: 8px; + padding: 14px 14px; + background: var(--bg-1); + border: 1px dashed var(--border); + border-radius: var(--r-md); + color: var(--text-3); + font-size: 12.5px; +} +.dash-inline-empty-cta { + margin-left: auto; + font-size: 11.5px; + color: var(--accent); + background: none; + border: 0; + padding: 4px 6px; + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + gap: 4px; +} +.dash-inline-empty-cta:hover { + background: var(--accent-soft); +} + +/* Up next strip */ +.dash-next-row { + display: flex; + gap: 10px; +} +.dash-next-card { + flex: 1; + background: var(--bg-1); + border: 1px solid var(--border); + border-radius: var(--r-lg); + overflow: hidden; + transition: border-color 120ms; +} +.dash-next-card.imminent { + border-color: var(--accent); +} +.dash-next-time { + padding: 8px 12px 4px; + display: flex; + align-items: center; + gap: 6px; +} +.dash-next-clock { + font-family: var(--font-mono); + font-size: 11px; + font-weight: 600; + color: var(--text-2); +} +.dash-next-rel { + font-size: 10.5px; + color: var(--accent-text); +} +.dash-next-body { + padding: 4px 12px 10px; +} +.dash-next-name { + font-size: 12.5px; + font-weight: 500; + color: var(--text-1); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.dash-next-sub { + display: flex; + align-items: center; + gap: 4px; + margin-top: 3px; + font-size: 11px; + color: var(--text-3); +} + +/* Attention section */ +.dash-attention { +} +.dash-attention-panel { + background: var(--bg-1); + border: 1px solid var(--border); + border-radius: var(--r-lg); + overflow: hidden; +} +.dash-attention-row { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border-bottom: 1px solid var(--border); + cursor: pointer; + transition: background 100ms; +} +.dash-attention-row:last-child { border-bottom: 0; } +.dash-attention-row.level-danger { + border-left: 3px solid var(--danger); +} +.dash-attention-row.level-warning { + border-left: 3px solid var(--warning); +} +.dash-attention-row:hover { background: var(--bg-2); } +.dash-attention-icon { + width: 24px; height: 24px; + display: grid; place-items: center; + flex-shrink: 0; +} +.dash-attention-title { + font-size: 12px; + font-weight: 500; + color: var(--text-1); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.dash-attention-detail { + font-size: 11px; + color: var(--text-3); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; +} +.dash-attention-arrow { + flex-shrink: 0; + color: var(--text-4); +} +.dash-attention-more { + display: flex; + align-items: center; + gap: 4px; + padding: 8px 12px; + font-size: 11.5px; + color: var(--text-3); + cursor: pointer; + justify-content: center; +} +.dash-attention-more:hover { color: var(--text-1); } + +/* Two-column work + cluster grid */ +.dash-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 14px; +} +.dash-col { + display: flex; + flex-direction: column; + gap: 8px; + min-width: 0; +} + +/* Job queue panel */ +.dash-jobs-panel { + background: var(--bg-1); + border: 1px solid var(--border); + border-radius: var(--r-lg); + overflow: hidden; +} +.dash-jobs-head { + display: grid; + grid-template-columns: 20px 1fr 1fr 1fr; + gap: 6px; + padding: 6px 12px; + border-bottom: 1px solid var(--border); + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-4); +} +.dash-jobs-row { + display: grid; + grid-template-columns: 20px 1fr 1fr 1fr; + gap: 6px; + padding: 7px 12px; + border-bottom: 1px solid var(--border); + align-items: center; + font-size: 12px; +} +.dash-jobs-row:last-child { border-bottom: 0; } +.dash-jobs-statr { justify-content: center; } +.dash-jobs-kind { + display: flex; + align-items: center; + gap: 5px; + color: var(--text-2); + overflow: hidden; +} +.dash-jobs-kind span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.dash-jobs-asset { + color: var(--text-1); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.dash-jobs-progress { + display: flex; + align-items: center; + gap: 6px; +} +.dash-jobs-bar { + flex: 1; + height: 4px; + background: var(--bg-3); + border-radius: 99px; + overflow: hidden; +} +.dash-jobs-bar-fill { + height: 100%; + background: var(--accent); + border-radius: 99px; + transition: width 300ms; +} +.dash-jobs-pct { + font-family: var(--font-mono); + font-size: 10px; + color: var(--text-3); + min-width: 28px; + text-align: right; +} +.dash-jobs-state { + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--text-4); +} +.dash-jobs-state.done { color: var(--success); } +.dash-jobs-state.failed { color: var(--danger); } + +/* Cluster panel (dashboard) */ +.dash-cluster-panel { + background: var(--bg-1); + border: 1px solid var(--border); + border-radius: var(--r-lg); + overflow: hidden; +} +.dash-cluster-head { + display: grid; + grid-template-columns: 20px 1fr 1fr 1fr; + gap: 6px; + padding: 6px 12px; + border-bottom: 1px solid var(--border); + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-4); +} +.dash-cluster-row { + display: grid; + grid-template-columns: 20px 1fr 1fr 1fr; + gap: 6px; + padding: 7px 12px; + border-bottom: 1px solid var(--border); + align-items: center; + font-size: 12px; +} +.dash-cluster-row:last-child { border-bottom: 0; } +.dash-cluster-row[data-online=false] { opacity: 0.55; } +.dash-cluster-status { display: flex; align-items: center; } +.dash-cluster-name { + display: flex; + align-items: center; + gap: 6px; + overflow: hidden; +} +.dash-cluster-host { + font-weight: 500; + color: var(--text-1); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.dash-cluster-role { + font-size: 9.5px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--accent-text); + background: var(--accent-soft); + padding: 1px 5px; + border-radius: 3px; + flex-shrink: 0; +} +.dash-cluster-metric { + display: flex; + align-items: center; + gap: 5px; +} +.dash-cluster-bar { + flex: 1; + height: 4px; + background: var(--bg-3); + border-radius: 99px; + overflow: hidden; +} +.dash-cluster-bar-fill { + height: 100%; + border-radius: 99px; + transition: width 200ms; +} +.dash-cluster-val { + font-family: var(--font-mono); + font-size: 10.5px; + color: var(--text-2); + min-width: 32px; + text-align: right; +} +.dash-cluster-val.muted { color: var(--text-4); } + +/* Panel empty state */ +.dash-panel-empty { + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + padding: 18px 12px; + color: var(--text-3); + font-size: 12px; +} + +/* Status bar */ +.dash-statusbar { + display: flex; + align-items: center; + gap: 6px; + padding: 10px 0 6px; + border-top: 1px solid var(--border); + margin-top: 6px; + font-family: var(--font-mono); + font-size: 11px; +} +.dash-stat-pip { + display: flex; + align-items: center; + gap: 5px; +} +.dash-stat-pip[data-tone=live] .dash-pip-dot { background: var(--live); } +.dash-stat-pip[data-tone=accent] .dash-pip-dot { background: var(--accent); } +.dash-stat-pip[data-tone=warning] .dash-pip-dot { background: var(--warning); } +.dash-stat-pip[data-tone=success] .dash-pip-dot { background: var(--success); } +.dash-stat-pip[data-tone=idle] .dash-pip-dot { background: var(--text-4); } +.dash-pip-dot { + width: 6px; height: 6px; + border-radius: 50%; + background: var(--text-4); +} +.dash-pip-num { + font-weight: 600; + color: var(--text-2); +} +.dash-pip-label { + color: var(--text-4); + text-transform: lowercase; +} +.dash-statusbar-sep { + color: var(--text-5, var(--text-4)); + opacity: 0.5; +} +.dash-statusbar-spacer { flex: 1; } +.dash-statusbar-clock { + color: var(--text-3); + margin-left: auto; +} + /* ========== Empty states ========== */ .dash-empty { padding: 16px 0;