1266 lines
51 KiB
HTML
1266 lines
51 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||
<title>Devices — Silo admin (redesign)</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||
<link href="https://fonts.googleapis.com/css2?family=Instrument+Sans:ital,wght@0,400..700;1,400..700&family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
|
||
<style>
|
||
/* -------- design tokens -------- */
|
||
:root {
|
||
/* surfaces — warm near-black with quiet elevation */
|
||
--bg: #0a0a0b;
|
||
--bg-elev-1: #111113;
|
||
--bg-elev-2: #17171a;
|
||
--bg-row-hover: #1c1c20;
|
||
|
||
--border: #232327;
|
||
--border-strong: #2e2e34;
|
||
|
||
/* type */
|
||
--text: #f3f1ec;
|
||
--text-2: #a6a39e;
|
||
--text-3: #65615d;
|
||
--text-4: #3a3835;
|
||
|
||
/* one strong accent: sodium-vapor amber. Reads as instrument panel,
|
||
not "AI gradient". Used sparingly. */
|
||
--accent: #f3c969;
|
||
--accent-soft: rgba(243, 201, 105, 0.12);
|
||
--accent-line: rgba(243, 201, 105, 0.45);
|
||
|
||
/* status — desaturated to feel like instrumentation */
|
||
--good: #84d59a;
|
||
--warn: #f3c969;
|
||
--bad: #f0846a;
|
||
|
||
/* fonts */
|
||
--serif: "Instrument Serif", "Times New Roman", serif;
|
||
--sans: "Instrument Sans", system-ui, -apple-system, sans-serif;
|
||
--mono: "JetBrains Mono", ui-monospace, "SF Mono", monospace;
|
||
|
||
--rad: 6px;
|
||
--rad-lg: 10px;
|
||
}
|
||
|
||
* { box-sizing: border-box; }
|
||
html, body {
|
||
margin: 0; padding: 0;
|
||
height: 100%; overflow: hidden;
|
||
background: var(--bg); color: var(--text);
|
||
font-family: var(--sans);
|
||
font-size: 13px; line-height: 1.45;
|
||
font-feature-settings: "ss01", "cv11";
|
||
-webkit-font-smoothing: antialiased;
|
||
text-rendering: optimizeLegibility;
|
||
}
|
||
button, input, select { font-family: inherit; color: inherit; }
|
||
|
||
/* faint film-grain so the dark surfaces have atmosphere instead of looking flat */
|
||
.grain {
|
||
pointer-events: none;
|
||
position: fixed; inset: 0; z-index: 999;
|
||
opacity: 0.035; mix-blend-mode: overlay;
|
||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
|
||
}
|
||
|
||
/* -------- layout -------- */
|
||
.app {
|
||
display: grid;
|
||
grid-template-rows: auto auto 1fr;
|
||
height: 100vh;
|
||
}
|
||
|
||
/* page header */
|
||
.page-h {
|
||
padding: 28px 36px 18px;
|
||
border-bottom: 1px solid var(--border);
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
gap: 32px;
|
||
align-items: end;
|
||
}
|
||
.page-h h1 {
|
||
margin: 0;
|
||
font-family: var(--serif);
|
||
font-weight: 400;
|
||
font-size: 64px;
|
||
line-height: 0.92;
|
||
letter-spacing: -0.025em;
|
||
}
|
||
.page-h h1 em { font-style: italic; color: var(--accent); }
|
||
.page-h p {
|
||
margin: 10px 0 0;
|
||
max-width: 560px;
|
||
color: var(--text-2);
|
||
font-size: 13px;
|
||
}
|
||
.page-h p code { font-family: var(--mono); font-size: 11.5px; color: var(--text); background: var(--bg-elev-1); padding: 1px 5px; border-radius: 3px; border: 1px solid var(--border); }
|
||
|
||
/* command bar (search + global actions) */
|
||
.cmdbar { display: flex; align-items: center; gap: 8px; }
|
||
.search {
|
||
display: flex; align-items: center; gap: 8px;
|
||
width: 340px;
|
||
padding: 8px 10px 8px 12px;
|
||
background: var(--bg-elev-1);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--rad);
|
||
color: var(--text-2);
|
||
transition: border-color 140ms ease, background 140ms ease;
|
||
}
|
||
.search:focus-within {
|
||
border-color: var(--accent-line);
|
||
background: var(--bg-elev-2);
|
||
}
|
||
.search input {
|
||
flex: 1; background: transparent; border: 0; outline: 0;
|
||
color: var(--text); font-size: 13px;
|
||
}
|
||
.search input::placeholder { color: var(--text-3); }
|
||
kbd {
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
color: var(--text-3);
|
||
border: 1px solid var(--border-strong);
|
||
border-bottom-width: 2px;
|
||
border-radius: 3px;
|
||
padding: 1px 5px;
|
||
background: var(--bg);
|
||
}
|
||
|
||
/* -------- fleet pulse strip -------- */
|
||
.pulse {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
gap: 32px;
|
||
align-items: center;
|
||
padding: 14px 36px;
|
||
border-bottom: 1px solid var(--border);
|
||
background: linear-gradient(to bottom, var(--bg-elev-1), var(--bg));
|
||
}
|
||
.stats { display: flex; gap: 22px; flex-wrap: wrap; align-items: baseline; }
|
||
.stat { display: inline-flex; gap: 6px; align-items: baseline; }
|
||
.stat-n {
|
||
font-family: var(--mono); font-weight: 500;
|
||
font-size: 13px; color: var(--text);
|
||
}
|
||
.stat-n.warn { color: var(--bad); }
|
||
.stat-l {
|
||
font-size: 10px; color: var(--text-3);
|
||
text-transform: uppercase; letter-spacing: 0.1em;
|
||
}
|
||
|
||
.pulse-mid { display: flex; align-items: center; gap: 14px; justify-content: center; }
|
||
.pulse-mid .label {
|
||
font-size: 10px; color: var(--text-3);
|
||
text-transform: uppercase; letter-spacing: 0.1em;
|
||
}
|
||
.histogram { display: flex; gap: 2px; align-items: end; height: 28px; }
|
||
.histogram .bar {
|
||
width: 5px;
|
||
background: var(--border-strong);
|
||
border-radius: 1px;
|
||
transition: background 160ms;
|
||
}
|
||
.histogram .bar.hi { background: var(--accent); }
|
||
.histogram .bar.med { background: var(--text-3); }
|
||
|
||
.pivots { display: flex; justify-content: end; gap: 8px; flex-wrap: wrap; }
|
||
.seg {
|
||
display: inline-flex;
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--rad);
|
||
overflow: hidden;
|
||
background: var(--bg-elev-1);
|
||
}
|
||
.seg button {
|
||
padding: 5px 10px;
|
||
background: transparent; color: var(--text-2);
|
||
border: 0;
|
||
border-right: 1px solid var(--border);
|
||
font-size: 11.5px;
|
||
cursor: pointer;
|
||
transition: background 140ms, color 140ms;
|
||
}
|
||
.seg button:last-child { border-right: 0; }
|
||
.seg button:hover:not(.active) { background: var(--bg-row-hover); color: var(--text); }
|
||
.seg button.active {
|
||
background: var(--accent-soft);
|
||
color: var(--accent);
|
||
}
|
||
.seg .seg-l {
|
||
padding: 5px 8px 5px 10px;
|
||
color: var(--text-3);
|
||
font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
|
||
border-right: 1px solid var(--border);
|
||
align-self: center;
|
||
}
|
||
|
||
/* -------- main 3-pane -------- */
|
||
.main {
|
||
display: grid;
|
||
grid-template-columns: 240px 380px 1fr;
|
||
overflow: hidden;
|
||
min-height: 0;
|
||
}
|
||
.pane { overflow-y: auto; min-height: 0; }
|
||
.pane + .pane { border-left: 1px solid var(--border); }
|
||
|
||
/* scrollbar */
|
||
.pane::-webkit-scrollbar { width: 8px; height: 8px; }
|
||
.pane::-webkit-scrollbar-track { background: transparent; }
|
||
.pane::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 4px; }
|
||
.pane::-webkit-scrollbar-thumb:hover { background: var(--text-3); }
|
||
|
||
/* -------- filters (left) -------- */
|
||
.filters { padding: 22px 18px; }
|
||
.filter-grp { margin-bottom: 26px; }
|
||
.filter-h {
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em;
|
||
color: var(--text-3); font-weight: 600;
|
||
margin-bottom: 10px;
|
||
}
|
||
.filter-h .clear {
|
||
font-family: var(--serif); font-style: italic;
|
||
text-transform: none; letter-spacing: 0;
|
||
font-size: 12px; color: var(--text-3);
|
||
cursor: pointer;
|
||
}
|
||
.filter-h .clear:hover { color: var(--accent); }
|
||
|
||
.facet {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
padding: 5px 2px;
|
||
cursor: pointer;
|
||
color: var(--text-2);
|
||
transition: color 140ms;
|
||
}
|
||
.facet:hover { color: var(--text); }
|
||
.facet-l { display: flex; align-items: center; gap: 9px; font-size: 12.5px; }
|
||
.facet-l input[type=checkbox] {
|
||
appearance: none;
|
||
width: 13px; height: 13px;
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: 3px;
|
||
background: var(--bg-elev-1);
|
||
position: relative; cursor: pointer;
|
||
transition: all 140ms;
|
||
}
|
||
.facet-l input:checked {
|
||
background: var(--accent);
|
||
border-color: var(--accent);
|
||
}
|
||
.facet-l input:checked::after {
|
||
content: "";
|
||
position: absolute;
|
||
left: 3.5px; top: 0.5px;
|
||
width: 4px; height: 8px;
|
||
border: solid var(--bg);
|
||
border-width: 0 1.6px 1.6px 0;
|
||
transform: rotate(45deg);
|
||
}
|
||
.facet-c {
|
||
font-family: var(--mono); font-size: 10.5px;
|
||
color: var(--text-3);
|
||
}
|
||
.saved-view {
|
||
display: flex; align-items: center; gap: 8px;
|
||
padding: 6px 8px;
|
||
margin: 0 -8px;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
color: var(--text-2);
|
||
transition: background 140ms, color 140ms;
|
||
}
|
||
.saved-view:hover { background: var(--bg-elev-1); color: var(--text); }
|
||
.saved-view svg { color: var(--text-3); }
|
||
.saved-view .count {
|
||
margin-left: auto;
|
||
font-family: var(--mono); font-size: 10.5px;
|
||
color: var(--text-3);
|
||
}
|
||
.saved-view.flag svg { color: var(--bad); }
|
||
|
||
/* -------- device list (middle) -------- */
|
||
.list-toolbar {
|
||
position: sticky; top: 0; z-index: 2;
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
padding: 12px 18px;
|
||
background: var(--bg);
|
||
border-bottom: 1px solid var(--border);
|
||
font-size: 11px; color: var(--text-3);
|
||
text-transform: uppercase; letter-spacing: 0.08em;
|
||
}
|
||
.list-toolbar .lt-r { display: flex; gap: 12px; align-items: center; }
|
||
.list-toolbar a { color: var(--text-2); cursor: pointer; }
|
||
.list-toolbar a:hover { color: var(--text); }
|
||
|
||
.group {
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.group-h {
|
||
position: sticky; top: 41px; z-index: 1;
|
||
display: grid;
|
||
grid-template-columns: 16px 1fr auto;
|
||
gap: 10px;
|
||
align-items: center;
|
||
padding: 11px 18px 10px;
|
||
background: var(--bg-elev-1);
|
||
cursor: pointer;
|
||
transition: background 140ms;
|
||
}
|
||
.group-h:hover { background: var(--bg-elev-2); }
|
||
.group-h .chev {
|
||
transition: transform 160ms;
|
||
color: var(--text-3);
|
||
}
|
||
.group.open .group-h .chev { transform: rotate(90deg); }
|
||
.group-name {
|
||
font-weight: 500; font-size: 13px; color: var(--text);
|
||
display: flex; align-items: center; gap: 8px;
|
||
}
|
||
.group-name .anom-tag {
|
||
display: inline-flex; align-items: center; gap: 4px;
|
||
padding: 1px 6px;
|
||
border-radius: 3px;
|
||
background: rgba(240, 132, 106, 0.1);
|
||
color: var(--bad);
|
||
font-size: 10px;
|
||
letter-spacing: 0.04em;
|
||
}
|
||
.group-meta {
|
||
grid-column: 2 / 4;
|
||
display: flex; gap: 12px;
|
||
font-family: var(--mono); font-size: 10.5px; color: var(--text-3);
|
||
margin-top: 1px;
|
||
}
|
||
.group-meta span + span { padding-left: 12px; border-left: 1px solid var(--border-strong); }
|
||
.group .devices { display: none; }
|
||
.group.open .devices { display: block; }
|
||
|
||
.device {
|
||
display: grid;
|
||
grid-template-columns: 28px 1fr auto;
|
||
gap: 12px; align-items: center;
|
||
padding: 11px 18px 11px 36px;
|
||
cursor: pointer;
|
||
border-left: 2px solid transparent;
|
||
transition: background 120ms, border-color 120ms;
|
||
position: relative;
|
||
}
|
||
.device:hover { background: var(--bg-elev-1); }
|
||
.device.active {
|
||
background: var(--bg-elev-1);
|
||
border-left-color: var(--accent);
|
||
}
|
||
.device-icon {
|
||
width: 28px; height: 28px;
|
||
border-radius: 6px;
|
||
background: var(--bg-elev-2);
|
||
border: 1px solid var(--border);
|
||
display: grid; place-items: center;
|
||
color: var(--text-2);
|
||
transition: all 140ms;
|
||
}
|
||
.device.active .device-icon {
|
||
background: var(--accent-soft);
|
||
color: var(--accent);
|
||
border-color: var(--accent-line);
|
||
}
|
||
.device-info .name {
|
||
font-size: 13px; color: var(--text); font-weight: 500;
|
||
display: flex; align-items: center; gap: 6px;
|
||
}
|
||
.device-info .name .anom {
|
||
width: 5px; height: 5px; border-radius: 50%;
|
||
background: var(--bad);
|
||
box-shadow: 0 0 0 3px rgba(240, 132, 106, 0.15);
|
||
}
|
||
.device-info .meta {
|
||
font-family: var(--mono); font-size: 10.5px;
|
||
color: var(--text-3); margin-top: 2px;
|
||
display: flex; gap: 8px; align-items: center;
|
||
}
|
||
.device-info .meta .sep { color: var(--border-strong); }
|
||
.device-info .meta .last { color: var(--text-3); }
|
||
.device-info .meta .last.fresh { color: var(--good); }
|
||
|
||
.device-r { display: flex; flex-direction: column; align-items: end; gap: 4px; }
|
||
.ovr-gauge { display: inline-flex; align-items: center; gap: 4px; }
|
||
.ovr-bars { display: inline-flex; gap: 2px; }
|
||
.ovr-bars span {
|
||
width: 3px; height: 12px;
|
||
background: var(--border-strong);
|
||
border-radius: 1px;
|
||
transition: background 160ms;
|
||
}
|
||
.ovr-bars span.on { background: var(--accent); }
|
||
.ovr-bars span.dim { background: var(--text-4); }
|
||
.ovr-num {
|
||
font-family: var(--mono);
|
||
font-size: 11px;
|
||
color: var(--text-2);
|
||
min-width: 14px; text-align: right;
|
||
}
|
||
.device.active .ovr-num { color: var(--accent); }
|
||
|
||
/* -------- detail (right) -------- */
|
||
.detail { padding: 28px 36px 60px; }
|
||
|
||
.detail-h {
|
||
display: flex; align-items: start; justify-content: space-between;
|
||
gap: 24px;
|
||
padding-bottom: 22px;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.detail-title { display: flex; gap: 16px; align-items: start; }
|
||
.detail-icon {
|
||
width: 52px; height: 52px;
|
||
border-radius: 12px;
|
||
background: linear-gradient(135deg, var(--bg-elev-2), var(--bg-elev-1));
|
||
border: 1px solid var(--border-strong);
|
||
display: grid; place-items: center;
|
||
color: var(--accent);
|
||
}
|
||
.detail-name {
|
||
font-family: var(--serif);
|
||
font-weight: 400;
|
||
font-size: 36px;
|
||
line-height: 1;
|
||
letter-spacing: -0.015em;
|
||
}
|
||
.detail-meta {
|
||
display: flex; flex-wrap: wrap; gap: 10px;
|
||
margin-top: 10px; align-items: center;
|
||
color: var(--text-3); font-size: 12px;
|
||
}
|
||
.detail-meta .id {
|
||
font-family: var(--mono); font-size: 11px; color: var(--text-2);
|
||
padding: 2px 8px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 99px;
|
||
background: var(--bg-elev-1);
|
||
}
|
||
.detail-meta .pill {
|
||
padding: 2px 9px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 99px;
|
||
color: var(--text-2);
|
||
font-size: 11px;
|
||
}
|
||
.detail-meta .pill svg { display: inline-block; margin-right: 4px; vertical-align: -1px; }
|
||
.detail-meta .live { color: var(--good); }
|
||
.detail-meta .live::before {
|
||
content: ""; display: inline-block;
|
||
width: 6px; height: 6px; border-radius: 50%;
|
||
background: var(--good);
|
||
box-shadow: 0 0 0 3px rgba(132, 213, 154, 0.18);
|
||
margin-right: 6px; vertical-align: 1px;
|
||
}
|
||
|
||
.detail-actions { display: flex; gap: 8px; }
|
||
.btn {
|
||
padding: 7px 12px;
|
||
background: var(--bg-elev-1);
|
||
color: var(--text-2);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--rad);
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
transition: all 140ms;
|
||
display: inline-flex; gap: 6px; align-items: center;
|
||
}
|
||
.btn:hover { color: var(--text); border-color: var(--text-3); background: var(--bg-elev-2); }
|
||
.btn-warn { color: var(--bad); border-color: rgba(240, 132, 106, 0.3); }
|
||
.btn-warn:hover { color: var(--bad); background: rgba(240, 132, 106, 0.08); border-color: var(--bad); }
|
||
|
||
/* summary strip — instrument readout */
|
||
.readout {
|
||
margin-top: 22px;
|
||
padding: 16px 20px;
|
||
background: var(--bg-elev-1);
|
||
border: 1px solid var(--border);
|
||
border-left: 2px solid var(--accent);
|
||
border-radius: var(--rad-lg);
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 24px;
|
||
}
|
||
.readout-cell .l {
|
||
font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.12em;
|
||
color: var(--text-3);
|
||
}
|
||
.readout-cell .v {
|
||
font-family: var(--mono); font-size: 15px;
|
||
color: var(--text); margin-top: 4px;
|
||
}
|
||
.readout-cell .v.accent { color: var(--accent); }
|
||
.readout-cell .sub {
|
||
font-size: 11px; color: var(--text-3); margin-top: 2px;
|
||
}
|
||
|
||
/* profile tabs */
|
||
.tabs {
|
||
display: flex; gap: 0;
|
||
margin-top: 28px;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.tab {
|
||
padding: 11px 16px;
|
||
background: transparent; border: 0;
|
||
color: var(--text-2);
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
border-bottom: 2px solid transparent;
|
||
margin-bottom: -1px;
|
||
display: inline-flex; align-items: center; gap: 8px;
|
||
transition: color 140ms;
|
||
}
|
||
.tab .badge {
|
||
font-family: var(--mono); font-size: 10px;
|
||
color: var(--text-3);
|
||
background: var(--bg-elev-1);
|
||
padding: 1px 6px; border-radius: 3px;
|
||
border: 1px solid var(--border);
|
||
}
|
||
.tab .avatar {
|
||
width: 16px; height: 16px;
|
||
border-radius: 50%;
|
||
background: var(--bg-elev-2);
|
||
border: 1px solid var(--border-strong);
|
||
display: grid; place-items: center;
|
||
font-size: 9px; color: var(--text-2);
|
||
}
|
||
.tab:hover { color: var(--text); }
|
||
.tab.active { color: var(--text); border-bottom-color: var(--accent); }
|
||
.tab.active .badge {
|
||
color: var(--accent);
|
||
background: var(--accent-soft);
|
||
border-color: var(--accent-line);
|
||
}
|
||
|
||
.profile-meta {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
padding: 14px 0 10px;
|
||
color: var(--text-3); font-size: 11.5px;
|
||
}
|
||
.profile-meta .id {
|
||
font-family: var(--mono);
|
||
display: inline-flex; gap: 6px; align-items: center;
|
||
}
|
||
.profile-meta .id::before {
|
||
content: ""; width: 6px; height: 6px; border-radius: 50%;
|
||
background: var(--accent);
|
||
}
|
||
.reset-all {
|
||
display: inline-flex; gap: 5px; align-items: center;
|
||
color: var(--text-3); cursor: pointer;
|
||
font-size: 11.5px;
|
||
transition: color 140ms;
|
||
}
|
||
.reset-all:hover { color: var(--text); }
|
||
|
||
/* override rows */
|
||
.ovr {
|
||
display: grid;
|
||
grid-template-columns: 1fr 240px;
|
||
gap: 32px;
|
||
padding: 18px 0;
|
||
border-bottom: 1px solid var(--border);
|
||
align-items: start;
|
||
}
|
||
.ovr:last-child { border-bottom: 0; }
|
||
.ovr-name {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: var(--text);
|
||
display: flex; align-items: center; gap: 8px;
|
||
}
|
||
.ovr-name .flag {
|
||
font-size: 9.5px;
|
||
text-transform: uppercase; letter-spacing: 0.08em;
|
||
padding: 1px 6px;
|
||
border-radius: 3px;
|
||
background: rgba(240, 132, 106, 0.1);
|
||
color: var(--bad);
|
||
}
|
||
.ovr-key {
|
||
font-family: var(--mono); font-size: 11px;
|
||
color: var(--text-3); margin-top: 3px;
|
||
}
|
||
.ovr-desc {
|
||
color: var(--text-2); font-size: 12.5px;
|
||
margin-top: 8px; max-width: 60ch;
|
||
}
|
||
.ovr-state {
|
||
margin-top: 10px;
|
||
font-family: var(--mono);
|
||
font-size: 11px; color: var(--text-3);
|
||
display: flex; gap: 8px; align-items: center;
|
||
}
|
||
.ovr-state em { font-style: normal; color: var(--text); }
|
||
.ovr-state .dot { width: 4px; height: 4px; border-radius: 50%; background: var(--text-4); }
|
||
|
||
/* control side */
|
||
.ovr-c { display: flex; flex-direction: column; align-items: end; gap: 8px; }
|
||
.toggle {
|
||
width: 34px; height: 20px;
|
||
border-radius: 99px;
|
||
background: var(--border-strong);
|
||
border: 0; padding: 2px;
|
||
cursor: pointer; position: relative;
|
||
transition: background 200ms;
|
||
}
|
||
.toggle::after {
|
||
content: "";
|
||
display: block;
|
||
width: 16px; height: 16px;
|
||
border-radius: 50%;
|
||
background: var(--text-2);
|
||
transition: transform 200ms cubic-bezier(.5,1.4,.5,1), background 200ms;
|
||
}
|
||
.toggle.on { background: var(--accent); }
|
||
.toggle.on::after { transform: translateX(14px); background: var(--bg); }
|
||
|
||
.select {
|
||
padding: 7px 30px 7px 12px;
|
||
background: var(--bg-elev-1);
|
||
color: var(--text);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--rad);
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
appearance: none;
|
||
background-image:
|
||
linear-gradient(45deg, transparent 50%, var(--text-2) 50%),
|
||
linear-gradient(135deg, var(--text-2) 50%, transparent 50%);
|
||
background-position:
|
||
calc(100% - 14px) 50%,
|
||
calc(100% - 9px) 50%;
|
||
background-size: 5px 5px, 5px 5px;
|
||
background-repeat: no-repeat;
|
||
min-width: 160px;
|
||
transition: border-color 140ms;
|
||
}
|
||
.select:hover { border-color: var(--text-3); }
|
||
.select:focus { outline: 0; border-color: var(--accent-line); }
|
||
|
||
.reset {
|
||
display: inline-flex; gap: 4px; align-items: center;
|
||
color: var(--text-3); cursor: pointer;
|
||
font-size: 11px;
|
||
transition: color 140ms;
|
||
}
|
||
.reset:hover { color: var(--text); }
|
||
.reset.disabled { opacity: 0.4; cursor: default; }
|
||
|
||
.json-btn {
|
||
padding: 6px 11px;
|
||
background: var(--bg);
|
||
color: var(--text-2);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--rad);
|
||
font-family: var(--mono); font-size: 11px;
|
||
cursor: pointer;
|
||
transition: all 140ms;
|
||
}
|
||
.json-btn:hover { color: var(--accent); border-color: var(--accent-line); }
|
||
|
||
/* mini-graph: small bar charts inline */
|
||
.inline-bars {
|
||
display: inline-flex; gap: 2px; vertical-align: -2px;
|
||
margin-left: 4px;
|
||
}
|
||
.inline-bars span {
|
||
width: 3px;
|
||
background: var(--text-3);
|
||
border-radius: 1px;
|
||
}
|
||
|
||
/* page-load reveal */
|
||
@keyframes rise {
|
||
from { opacity: 0; transform: translateY(6px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
.page-h, .pulse, .filters, .list-toolbar, .group, .detail {
|
||
animation: rise 520ms cubic-bezier(.2,.7,.2,1) both;
|
||
}
|
||
.pulse { animation-delay: 60ms; }
|
||
.filters { animation-delay: 120ms; }
|
||
.list-toolbar { animation-delay: 160ms; }
|
||
.group { animation-delay: 200ms; }
|
||
.detail { animation-delay: 240ms; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="grain"></div>
|
||
|
||
<div class="app">
|
||
|
||
<!-- ============ HEADER ============ -->
|
||
<header class="page-h">
|
||
<div>
|
||
<h1>Devices<em>.</em></h1>
|
||
<p>
|
||
Inspect, tune, and reset per-profile playback overrides across the fleet.
|
||
Filter by user, platform, or override pattern. Press <code>⌘K</code> to jump anywhere.
|
||
</p>
|
||
</div>
|
||
<div class="cmdbar">
|
||
<label class="search">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7" /><path d="m21 21-4.3-4.3" /></svg>
|
||
<input placeholder="Search devices, users, IDs, override keys…" />
|
||
<kbd>⌘K</kbd>
|
||
</label>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- ============ FLEET PULSE ============ -->
|
||
<section class="pulse">
|
||
<div class="stats">
|
||
<div class="stat"><span class="stat-n">9</span><span class="stat-l">users</span></div>
|
||
<div class="stat"><span class="stat-n">28</span><span class="stat-l">devices</span></div>
|
||
<div class="stat"><span class="stat-n">33</span><span class="stat-l">profiles</span></div>
|
||
<div class="stat"><span class="stat-n">85</span><span class="stat-l">overrides</span></div>
|
||
<div class="stat"><span class="stat-n warn">3</span><span class="stat-l">anomalies</span></div>
|
||
</div>
|
||
|
||
<div class="pulse-mid">
|
||
<span class="label">override density · 28 devices</span>
|
||
<div class="histogram" id="histo"></div>
|
||
</div>
|
||
|
||
<div class="pivots">
|
||
<div class="seg">
|
||
<span class="seg-l">view</span>
|
||
<button class="active">Devices</button>
|
||
<button>Overrides</button>
|
||
<button>Platforms</button>
|
||
</div>
|
||
<div class="seg">
|
||
<span class="seg-l">group by</span>
|
||
<button class="active">User</button>
|
||
<button>Platform</button>
|
||
<button>Activity</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ THREE-PANE MAIN ============ -->
|
||
<main class="main">
|
||
|
||
<!-- ---- Filters ---- -->
|
||
<aside class="pane filters">
|
||
|
||
<div class="filter-grp">
|
||
<div class="filter-h">
|
||
Saved views
|
||
<span class="clear">manage</span>
|
||
</div>
|
||
<div class="saved-view flag">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
|
||
Anomalies <span class="count">3</span>
|
||
</div>
|
||
<div class="saved-view">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
|
||
Updated 7d <span class="count">11</span>
|
||
</div>
|
||
<div class="saved-view">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
|
||
HDR-related <span class="count">6</span>
|
||
</div>
|
||
<div class="saved-view">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
||
Subtitle custom <span class="count">4</span>
|
||
</div>
|
||
<div class="saved-view">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 8v8M8 12h8"/></svg>
|
||
Dormant 30d <span class="count">5</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="filter-grp">
|
||
<div class="filter-h">Platform <span class="clear">clear</span></div>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" checked /> tvOS</span><span class="facet-c">12</span></label>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> iOS</span><span class="facet-c">8</span></label>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> macOS Web</span><span class="facet-c">5</span></label>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> android-tv</span><span class="facet-c">3</span></label>
|
||
</div>
|
||
|
||
<div class="filter-grp">
|
||
<div class="filter-h">Override count</div>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> none</span><span class="facet-c">4</span></label>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> 1–2</span><span class="facet-c">11</span></label>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" checked /> 3–5</span><span class="facet-c">9</span></label>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> 6+</span><span class="facet-c">4</span></label>
|
||
</div>
|
||
|
||
<div class="filter-grp">
|
||
<div class="filter-h">Last seen</div>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> < 24h</span><span class="facet-c">7</span></label>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> < 7d</span><span class="facet-c">14</span></label>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> < 30d</span><span class="facet-c">22</span></label>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> > 30d</span><span class="facet-c">5</span></label>
|
||
</div>
|
||
|
||
<div class="filter-grp">
|
||
<div class="filter-h">Override key</div>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> playback.*</span><span class="facet-c">38</span></label>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> player.*</span><span class="facet-c">27</span></label>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> subtitle_*</span><span class="facet-c">12</span></label>
|
||
<label class="facet"><span class="facet-l"><input type="checkbox" /> hdr_*</span><span class="facet-c">8</span></label>
|
||
</div>
|
||
|
||
</aside>
|
||
|
||
<!-- ---- Device list ---- -->
|
||
<aside class="pane">
|
||
<div class="list-toolbar">
|
||
<span>28 devices · 11 shown</span>
|
||
<span class="lt-r">
|
||
<a>Select</a><span style="color:var(--border-strong)">·</span><a>Sort: activity</a>
|
||
</span>
|
||
</div>
|
||
|
||
<!-- admin -->
|
||
<div class="group open">
|
||
<div class="group-h">
|
||
<svg class="chev" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="m9 18 6-6-6-6"/></svg>
|
||
<div class="group-name">
|
||
admin
|
||
<span class="anom-tag">2 anomalies</span>
|
||
</div>
|
||
<div class="ovr-num">5</div>
|
||
<div class="group-meta">
|
||
<span>5 devices</span><span>17 overrides</span><span>updated 18h</span>
|
||
</div>
|
||
</div>
|
||
<div class="devices">
|
||
<div class="device active" data-d="a1">
|
||
<div class="device-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg></div>
|
||
<div class="device-info">
|
||
<div class="name">Apple TV 4K (3rd gen) <span class="anom" title="anomaly: Profile 7 fallback diverges from fleet"></span></div>
|
||
<div class="meta">tvOS 17.4<span class="sep">·</span>973df8c5<span class="sep">·</span><span class="last fresh">2d</span></div>
|
||
</div>
|
||
<div class="device-r">
|
||
<div class="ovr-gauge">
|
||
<span class="ovr-bars"><span class="on"></span><span class="on"></span><span class="on"></span><span class="on"></span><span class="on"></span></span>
|
||
<span class="ovr-num">7</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="device" data-d="a2">
|
||
<div class="device-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg></div>
|
||
<div class="device-info">
|
||
<div class="name">Apple TV 4K (3rd gen)</div>
|
||
<div class="meta">tvOS 17.4<span class="sep">·</span>4a08e2c9<span class="sep">·</span><span class="last">11d</span></div>
|
||
</div>
|
||
<div class="device-r">
|
||
<div class="ovr-gauge">
|
||
<span class="ovr-bars"><span class="on"></span><span class="on"></span><span class="on"></span><span></span><span></span></span>
|
||
<span class="ovr-num">3</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="device" data-d="a3">
|
||
<div class="device-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg></div>
|
||
<div class="device-info">
|
||
<div class="name">NVIDIA SHIELD Android TV <span class="anom"></span></div>
|
||
<div class="meta">android-tv<span class="sep">·</span>c7f1d0b4<span class="sep">·</span><span class="last">3d</span></div>
|
||
</div>
|
||
<div class="device-r">
|
||
<div class="ovr-gauge">
|
||
<span class="ovr-bars"><span class="on"></span><span class="on"></span><span class="on"></span><span class="on"></span><span class="on"></span></span>
|
||
<span class="ovr-num">5</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="device" data-d="a4">
|
||
<div class="device-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="2" y="4" width="20" height="13" rx="2"/><path d="M2 8h20M6 13h8"/></svg></div>
|
||
<div class="device-info">
|
||
<div class="name">Chrome on macOS</div>
|
||
<div class="meta">macOS Web<span class="sep">·</span>a09b3e21<span class="sep">·</span><span class="last fresh">4h</span></div>
|
||
</div>
|
||
<div class="device-r">
|
||
<div class="ovr-gauge">
|
||
<span class="ovr-bars"><span class="on"></span><span class="on"></span><span></span><span></span><span></span></span>
|
||
<span class="ovr-num">2</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="device" data-d="a5">
|
||
<div class="device-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="6" y="2" width="12" height="20" rx="2.5"/><path d="M11 19h2"/></svg></div>
|
||
<div class="device-info">
|
||
<div class="name">iPhone 17 Pro</div>
|
||
<div class="meta">iOS 18.4<span class="sep">·</span>5d2afc18<span class="sep">·</span><span class="last fresh">2h</span></div>
|
||
</div>
|
||
<div class="device-r">
|
||
<div class="ovr-gauge">
|
||
<span class="ovr-bars"><span class="on"></span><span class="on"></span><span class="on"></span><span class="on"></span><span></span></span>
|
||
<span class="ovr-num">4</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MeM -->
|
||
<div class="group open">
|
||
<div class="group-h">
|
||
<svg class="chev" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="m9 18 6-6-6-6"/></svg>
|
||
<div class="group-name">MeM</div>
|
||
<div class="ovr-num">2</div>
|
||
<div class="group-meta"><span>2 devices</span><span>9 overrides</span><span>updated 2d</span></div>
|
||
</div>
|
||
<div class="devices">
|
||
<div class="device" data-d="m1">
|
||
<div class="device-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg></div>
|
||
<div class="device-info">
|
||
<div class="name">Apple TV</div>
|
||
<div class="meta">tvOS 17.2<span class="sep">·</span>2b9efa01<span class="sep">·</span><span class="last">2d</span></div>
|
||
</div>
|
||
<div class="device-r">
|
||
<div class="ovr-gauge">
|
||
<span class="ovr-bars"><span class="on"></span><span class="on"></span><span class="on"></span><span></span><span></span></span>
|
||
<span class="ovr-num">3</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="device" data-d="m2">
|
||
<div class="device-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="6" y="2" width="12" height="20" rx="2.5"/><path d="M11 19h2"/></svg></div>
|
||
<div class="device-info">
|
||
<div class="name">iPhone <span class="anom"></span></div>
|
||
<div class="meta">iOS 17.5<span class="sep">·</span>e1c8db70<span class="sep">·</span><span class="last">9d</span></div>
|
||
</div>
|
||
<div class="device-r">
|
||
<div class="ovr-gauge">
|
||
<span class="ovr-bars"><span class="on"></span><span class="on"></span><span class="on"></span><span class="on"></span><span class="on"></span></span>
|
||
<span class="ovr-num">6</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- user@example.com -->
|
||
<div class="group open">
|
||
<div class="group-h">
|
||
<svg class="chev" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="m9 18 6-6-6-6"/></svg>
|
||
<div class="group-name">user@example.com</div>
|
||
<div class="ovr-num">1</div>
|
||
<div class="group-meta"><span>1 device</span><span>1 override</span><span>updated 1d</span></div>
|
||
</div>
|
||
<div class="devices">
|
||
<div class="device" data-d="n1">
|
||
<div class="device-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg></div>
|
||
<div class="device-info">
|
||
<div class="name">Apple TV</div>
|
||
<div class="meta">tvOS 17.4<span class="sep">·</span>fa3c1e88<span class="sep">·</span><span class="last fresh">1d</span></div>
|
||
</div>
|
||
<div class="device-r">
|
||
<div class="ovr-gauge">
|
||
<span class="ovr-bars"><span class="on"></span><span></span><span></span><span></span><span></span></span>
|
||
<span class="ovr-num">1</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Olek -->
|
||
<div class="group">
|
||
<div class="group-h">
|
||
<svg class="chev" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="m9 18 6-6-6-6"/></svg>
|
||
<div class="group-name">Olek</div>
|
||
<div class="ovr-num">2</div>
|
||
<div class="group-meta"><span>2 devices</span><span>4 overrides</span><span>updated 2d</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Plexshare973 -->
|
||
<div class="group">
|
||
<div class="group-h">
|
||
<svg class="chev" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="m9 18 6-6-6-6"/></svg>
|
||
<div class="group-name">Plexshare973
|
||
<span class="anom-tag">1 anomaly</span>
|
||
</div>
|
||
<div class="ovr-num">4</div>
|
||
<div class="group-meta"><span>4 devices</span><span>13 overrides</span><span>updated 1d</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- plexy -->
|
||
<div class="group">
|
||
<div class="group-h">
|
||
<svg class="chev" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="m9 18 6-6-6-6"/></svg>
|
||
<div class="group-name">plexy</div>
|
||
<div class="ovr-num">1</div>
|
||
<div class="group-meta"><span>1 device</span><span>2 overrides</span><span>updated 1d</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- roblenz -->
|
||
<div class="group">
|
||
<div class="group-h">
|
||
<svg class="chev" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="m9 18 6-6-6-6"/></svg>
|
||
<div class="group-name">roblenz</div>
|
||
<div class="ovr-num">3</div>
|
||
<div class="group-meta"><span>3 devices</span><span>11 overrides</span><span>updated 4h</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- spawnster -->
|
||
<div class="group">
|
||
<div class="group-h">
|
||
<svg class="chev" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="m9 18 6-6-6-6"/></svg>
|
||
<div class="group-name">spawnster</div>
|
||
<div class="ovr-num">4</div>
|
||
<div class="group-meta"><span>4 devices</span><span>14 overrides</span><span>updated 8h</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- guest -->
|
||
<div class="group">
|
||
<div class="group-h">
|
||
<svg class="chev" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="m9 18 6-6-6-6"/></svg>
|
||
<div class="group-name">guest</div>
|
||
<div class="ovr-num">6</div>
|
||
<div class="group-meta"><span>6 devices</span><span>14 overrides</span><span>updated 3d</span></div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- ---- Detail ---- -->
|
||
<section class="pane detail">
|
||
<div class="detail-h">
|
||
<div class="detail-title">
|
||
<div class="detail-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="detail-name">Apple TV 4K</div>
|
||
<div class="detail-meta">
|
||
<span class="id">973df8c5-7ada-4896-8d7b-5120b1fa04e8</span>
|
||
<span class="pill">tvOS 17.4</span>
|
||
<span class="pill">admin <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M7 17 17 7M7 7h10v10"/></svg></span>
|
||
<span class="pill live">live · 2h</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="detail-actions">
|
||
<button class="btn">
|
||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-3.5-7.1"/><path d="M21 4v5h-5"/></svg>
|
||
Compare to fleet
|
||
</button>
|
||
<button class="btn btn-warn">
|
||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
||
Reset all 11
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- instrument readout -->
|
||
<div class="readout">
|
||
<div class="readout-cell">
|
||
<div class="l">Overrides</div>
|
||
<div class="v accent">11 <span style="color:var(--text-3); font-size: 11px">/ 24 fields</span></div>
|
||
<div class="sub">across 3 profiles</div>
|
||
</div>
|
||
<div class="readout-cell">
|
||
<div class="l">Profiles touched</div>
|
||
<div class="v">admin · Test · aaa</div>
|
||
<div class="sub">7 / 2 / 2 overrides</div>
|
||
</div>
|
||
<div class="readout-cell">
|
||
<div class="l">Anomaly</div>
|
||
<div class="v" style="color: var(--bad)">Profile 7 fallback</div>
|
||
<div class="sub">no other tvOS device disables it</div>
|
||
</div>
|
||
<div class="readout-cell">
|
||
<div class="l">Last update</div>
|
||
<div class="v">2d 04h</div>
|
||
<div class="sub">subtitle_appearance · admin</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- profile tabs -->
|
||
<div class="tabs">
|
||
<button class="tab active"><span class="avatar">A</span>admin <span class="badge">7</span></button>
|
||
<button class="tab"><span class="avatar">T</span>Test <span class="badge">2</span></button>
|
||
<button class="tab"><span class="avatar">a</span>aaaaaaaa <span class="badge">2</span></button>
|
||
</div>
|
||
|
||
<div class="profile-meta">
|
||
<span class="id">8bf465bc — 3a0b — 4cca — 87b9 — 4a1473890be6 · 7 overrides</span>
|
||
<span class="reset-all">
|
||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v5h5"/></svg>
|
||
Reset profile overrides
|
||
</span>
|
||
</div>
|
||
|
||
<div class="ovrs">
|
||
<!-- 1 — anomaly -->
|
||
<div class="ovr">
|
||
<div>
|
||
<div class="ovr-name">Profile 7 HDR10 fallback <span class="flag">anomaly</span></div>
|
||
<div class="ovr-key">player.dv_profile7_hdr10_fallback</div>
|
||
<div class="ovr-desc">Play Dolby Vision Profile 7 as the HDR10 base layer on this device.</div>
|
||
<div class="ovr-state"><em>Disabled</em><span class="dot"></span>fleet default <em>Enabled</em><span class="dot"></span>last 2d ago</div>
|
||
</div>
|
||
<div class="ovr-c">
|
||
<button class="toggle" aria-label="toggle"></button>
|
||
<span class="reset"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v5h5"/></svg> reset</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 2 -->
|
||
<div class="ovr">
|
||
<div>
|
||
<div class="ovr-name">Auto-play next episode</div>
|
||
<div class="ovr-key">playback.auto_play_next</div>
|
||
<div class="ovr-desc">Start the next episode automatically on this device after the current one ends.</div>
|
||
<div class="ovr-state"><em>Enabled</em><span class="dot"></span>matches default<span class="dot"></span>last 11d ago</div>
|
||
</div>
|
||
<div class="ovr-c">
|
||
<button class="toggle on" aria-label="toggle"></button>
|
||
<span class="reset"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v5h5"/></svg> reset</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 3 -->
|
||
<div class="ovr">
|
||
<div>
|
||
<div class="ovr-name">Subtitle appearance</div>
|
||
<div class="ovr-key">subtitle_appearance</div>
|
||
<div class="ovr-desc">Customize subtitles on this device — font, size, edge style, and background. Stored as a JSON document; edit inline for fine-grained control.</div>
|
||
<div class="ovr-state"><em>Custom</em><span class="dot"></span>5 fields differ from default<span class="dot"></span>last 2d ago</div>
|
||
</div>
|
||
<div class="ovr-c">
|
||
<button class="json-btn">{ } Edit JSON</button>
|
||
<span class="reset"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v5h5"/></svg> reset</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 4 -->
|
||
<div class="ovr">
|
||
<div>
|
||
<div class="ovr-name">Playback speed</div>
|
||
<div class="ovr-key">player.playback_speed</div>
|
||
<div class="ovr-desc">Choose the default playback speed for this device.</div>
|
||
<div class="ovr-state"><em>1×</em><span class="dot"></span>matches default<span class="dot"></span>last 18d ago</div>
|
||
</div>
|
||
<div class="ovr-c">
|
||
<select class="select"><option>1× (Normal)</option><option>1.25×</option><option>1.5×</option><option>2×</option></select>
|
||
<span class="reset disabled">reset</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 5 -->
|
||
<div class="ovr">
|
||
<div>
|
||
<div class="ovr-name">Video fit</div>
|
||
<div class="ovr-key">player.video_gravity</div>
|
||
<div class="ovr-desc">Control how video should fill the screen on this device.</div>
|
||
<div class="ovr-state"><em>Fit</em><span class="dot"></span>matches default<span class="dot"></span>last 14d ago</div>
|
||
</div>
|
||
<div class="ovr-c">
|
||
<select class="select"><option>Fit</option><option>Fill</option><option>Stretch</option></select>
|
||
<span class="reset disabled">reset</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 6 -->
|
||
<div class="ovr">
|
||
<div>
|
||
<div class="ovr-name">Preferred audio language</div>
|
||
<div class="ovr-key">playback.audio_language</div>
|
||
<div class="ovr-desc">Choose which spoken language Silo should prefer first on this device.</div>
|
||
<div class="ovr-state"><em>English</em><span class="dot"></span>fleet default <em>Auto</em><span class="dot"></span>last 7d ago</div>
|
||
</div>
|
||
<div class="ovr-c">
|
||
<select class="select"><option>English</option><option>Auto</option><option>Original</option><option>Japanese</option></select>
|
||
<span class="reset"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v5h5"/></svg> reset</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 7 -->
|
||
<div class="ovr">
|
||
<div>
|
||
<div class="ovr-name">Preferred quality</div>
|
||
<div class="ovr-key">playback.preferred_quality</div>
|
||
<div class="ovr-desc">Pick the quality Silo should prefer on this device for the current profile.</div>
|
||
<div class="ovr-state"><em>2160p / 4K</em><span class="dot"></span>fleet default <em>Auto</em><span class="dot"></span>last 7d ago</div>
|
||
</div>
|
||
<div class="ovr-c">
|
||
<select class="select"><option>2160p / 4K</option><option>1080p</option><option>720p</option><option>Auto</option></select>
|
||
<span class="reset"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v5h5"/></svg> reset</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
</div>
|
||
|
||
<script>
|
||
/* generate the override-density histogram (28 devices) */
|
||
const counts = [7,3,5,2,4,3,6,1,2,1,4,3,2,5,2,1,0,3,4,2,1,3,5,2,1,2,3,4];
|
||
const max = Math.max(...counts);
|
||
const histo = document.getElementById("histo");
|
||
counts.forEach(n => {
|
||
const b = document.createElement("div");
|
||
b.className = "bar" + (n >= 5 ? " hi" : n >= 2 ? " med" : "");
|
||
b.style.height = (Math.max(n / max, 0.06) * 100) + "%";
|
||
histo.appendChild(b);
|
||
});
|
||
|
||
/* simple selection */
|
||
document.querySelectorAll(".device").forEach(d => {
|
||
d.addEventListener("click", () => {
|
||
document.querySelectorAll(".device.active").forEach(x => x.classList.remove("active"));
|
||
d.classList.add("active");
|
||
});
|
||
});
|
||
|
||
/* group expand/collapse */
|
||
document.querySelectorAll(".group-h").forEach(h => {
|
||
h.addEventListener("click", () => h.parentElement.classList.toggle("open"));
|
||
});
|
||
|
||
/* toggle interactivity */
|
||
document.querySelectorAll(".toggle").forEach(t => {
|
||
t.addEventListener("click", () => t.classList.toggle("on"));
|
||
});
|
||
|
||
/* tab swap */
|
||
document.querySelectorAll(".tab").forEach(t => {
|
||
t.addEventListener("click", () => {
|
||
document.querySelectorAll(".tab.active").forEach(x => x.classList.remove("active"));
|
||
t.classList.add("active");
|
||
});
|
||
});
|
||
|
||
/* segment toggles (visual only) */
|
||
document.querySelectorAll(".seg").forEach(seg => {
|
||
seg.querySelectorAll("button").forEach(b => {
|
||
b.addEventListener("click", () => {
|
||
seg.querySelectorAll("button.active").forEach(x => x.classList.remove("active"));
|
||
b.classList.add("active");
|
||
});
|
||
});
|
||
});
|
||
|
||
/* ⌘K focus */
|
||
document.addEventListener("keydown", (e) => {
|
||
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
|
||
e.preventDefault();
|
||
document.querySelector(".search input").focus();
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|