/* ActivePush control room - visual language shared with RustProxy */ :root { color-scheme: dark; --bg: #131615; --panel: #1b1f1d; --panel2: #202622; --line: #323a34; --row-line: #2b332c; --muted: #a0aaa0; --subtle: #879082; --text: #edf0e9; --accent: #f69b74; --accent-hover: #ffb090; --accent-ink: #221810; --green: #bdd293; --red: #ffaaa2; --blue: #8ebcff; --violet: #d7adff; --sand: #c9b99c; --sidebar-bg: #101310; --brand2: #cfe0ac; --nav-active: #242b23; --nav-hover: #20251f; --field-bg: #131813; --field-line: #47523f; --outline: #717b67; --grid: #333c33; --code-bg: #101810; --notice-bg: #29251c; --notice-line: #554833; --pill-line: #6d5a45; --pill-live: #576847; --danger-line: #79514e; --shadow: #0009; --backdrop: rgba(0, 0, 0, .68); --r-surface: 6px; --r-control: 4px; --mono: ui-monospace, SFMono-Regular, "JetBrains Mono", Consolas, monospace; --sans: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; } html[data-theme=light] { color-scheme: light; --bg: #f4f6f2; --panel: #fff; --panel2: #edf1eb; --line: #cbd3c7; --row-line: #e0e5dd; --muted: #5e685f; --subtle: #687369; --text: #1d241f; --accent: #b85b38; --accent-hover: #a84f30; --accent-ink: #fff; --green: #5f783f; --red: #b64a43; --blue: #4f77a9; --violet: #7d4fa3; --sand: #685b45; --sidebar-bg: #eef2ec; --brand2: #6b8249; --nav-active: #e1e8dc; --nav-hover: #e8ede6; --field-bg: #fff; --field-line: #b7c1b5; --outline: #98a497; --grid: #d4dbd1; --code-bg: #edf3e9; --notice-bg: #fff9ef; --notice-line: #d4c2a5; --pill-line: #cbb28f; --pill-live: #a8ba8d; --danger-line: #d7a29d; --shadow: rgba(31, 39, 33, .22); --backdrop: rgba(31, 39, 33, .45); } * { box-sizing: border-box; scrollbar-color: #4f5d50 transparent; scrollbar-width: thin; } [hidden] { display: none !important; } html, body { margin: 0; } body { background: var(--bg); color: var(--text); font: 12px/1.5 var(--sans); min-height: 100vh; } a { color: inherit; text-decoration: none; } code, .mono { font-family: var(--mono); } h1 { margin: 0; font-size: clamp(26px, 3vw, 40px); font-weight: 520; letter-spacing: -1.5px; line-height: 1.1; } h2 { margin: 0; font-size: 15px; letter-spacing: -.3px; } h3 { margin: 0 0 8px; font-size: 13px; } /* ---------------------------------------------------------------- controls */ button { font: inherit; cursor: pointer; border: 0; } button:disabled { opacity: .5; cursor: wait; } .primary, .outline, .quiet, .danger { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 27px; padding: 5px 11px; font-size: 11px; border-radius: var(--r-control); white-space: nowrap; } .primary { background: var(--accent); color: var(--accent-ink); border: 1px solid var(--accent); font-weight: 650; } .primary:hover { background: var(--accent-hover); } .outline { background: transparent; color: var(--text); border: 1px solid var(--outline); } .outline:hover { border-color: var(--accent); } .quiet { background: transparent; color: var(--muted); border: 1px solid var(--line); } .quiet:hover { color: var(--text); border-color: var(--outline); } .danger { background: transparent; color: var(--red); border: 1px solid var(--danger-line); } .danger:hover { background: color-mix(in srgb, var(--red) 12%, transparent); } a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } input, select, textarea { font: inherit; font-size: 12px; width: 100%; min-width: 0; background: var(--field-bg); color: var(--text); border: 1px solid var(--field-line); border-radius: var(--r-control); padding: 6px 8px; min-height: 29px; } textarea { min-height: 70px; resize: vertical; font-family: var(--mono); } input[type=checkbox] { width: 15px; height: 15px; min-height: 0; accent-color: var(--accent); } label { display: block; color: var(--muted); font-size: 11px; margin: 0 0 3px; } .small-label { font: 9px var(--mono); letter-spacing: 1.2px; color: var(--muted); text-transform: uppercase; } .fine { font-size: 11px; color: var(--muted); line-height: 1.6; overflow-wrap: anywhere; } .pill { display: inline-block; padding: 2px 7px; border: 1px solid var(--pill-line); color: var(--accent); font: 9px var(--mono); letter-spacing: .8px; border-radius: 3px; white-space: nowrap; text-transform: uppercase; } .pill.live, .pill.ok { border-color: var(--pill-live); color: var(--green); } .pill.bad { border-color: var(--danger-line); color: var(--red); } .pill.info { border-color: color-mix(in srgb, var(--blue) 45%, transparent); color: var(--blue); } .pill.muted { border-color: var(--line); color: var(--muted); } .signal { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--green); margin-right: 6px; } .signal.pulse { animation: pulse 1.6s infinite; } @keyframes pulse { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--green) 60%, transparent); } 70% { box-shadow: 0 0 0 7px transparent; } 100% { box-shadow: 0 0 0 0 transparent; } } .notice { display: flex; gap: 12px; align-items: flex-start; color: var(--sand); border: 1px solid var(--notice-line); background: var(--notice-bg); border-radius: var(--r-surface); padding: 8px 11px; margin-bottom: 10px; font-size: 11px; line-height: 1.6; } .notice b { font-family: var(--mono); color: var(--accent); } /* ---------------------------------------------------------------- shell */ .shell { display: grid; grid-template-columns: 218px minmax(0, 1fr); min-height: 100vh; } .sidebar { background: var(--sidebar-bg); border-right: 1px solid var(--line); padding: 18px 12px; display: flex; flex-direction: column; gap: 14px; position: sticky; top: 0; height: 100vh; overflow-y: auto; } .brand { display: flex; align-items: center; gap: 10px; font-weight: 650; font-size: 14px; letter-spacing: -.2px; } .brand-mark { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; white-space: nowrap; width: 34px; height: 34px; border: 1px solid var(--line); border-radius: 8px; font: 700 12px var(--mono); color: var(--accent); background: var(--panel); } .brand-mark span { color: var(--brand2); } .edition { font: 9px var(--mono); letter-spacing: 1.6px; color: var(--subtle); } .main-navigation { display: flex; flex-direction: column; gap: 2px; } .nav { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; background: transparent; color: var(--muted); padding: 7px 9px; border-radius: var(--r-control); font-size: 12px; } .nav span { font: 10px var(--mono); color: var(--subtle); width: 18px; } .nav b { font-weight: 500; } .nav:hover { background: var(--nav-hover); color: var(--text); } .nav.active { background: var(--nav-active); color: var(--brand2); } .nav.active span { color: var(--accent); } .nav .count { margin-left: auto; font: 10px var(--mono); color: var(--accent); } .sidebar-bottom { margin-top: auto; border-top: 1px solid var(--line); padding-top: 12px; } .sidebar-bottom p { margin: 6px 0; color: var(--subtle); font: 10px/1.6 var(--mono); } .local-tag { display: flex; align-items: center; gap: 6px; font: 9px var(--mono); letter-spacing: 1.2px; color: var(--green); } .mobile-toggle { display: none; } main { min-width: 0; padding: 0 24px 40px; } .topbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 0; border-bottom: 1px solid var(--line); margin-bottom: 18px; position: sticky; top: 0; background: var(--bg); z-index: 5; } .breadcrumb { font: 10px var(--mono); letter-spacing: 1.4px; color: var(--muted); } .breadcrumb span { color: var(--subtle); margin: 0 4px; } .breadcrumb b { color: var(--text); font-weight: 500; } .top-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; } .top-actions select { width: auto; min-height: 27px; padding: 4px 8px; } #theme { min-width: 30px; font-size: 13px; } .page-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 16px; margin-bottom: 16px; flex-wrap: wrap; } .page-head .eyebrow { margin-bottom: 6px; } .page-head p { margin: 6px 0 0; max-width: 720px; } /* ---------------------------------------------------------------- cards / grid */ .grid { display: grid; gap: 10px; } .grid.stats { grid-template-columns: repeat(4, minmax(0, 1fr)); } .grid.two { grid-template-columns: repeat(2, minmax(0, 1fr)); } .grid.three { grid-template-columns: repeat(3, minmax(0, 1fr)); } #view { display: flex; flex-direction: column; gap: 12px; } #view > .page-head, #view > .tabs { margin-bottom: 0; } .stack { display: flex; flex-direction: column; gap: 12px; } .stack > * + * { margin-top: 0; } .grid.stats > .notice { grid-column: 1 / -1; margin: 0; } .card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-surface); padding: 12px 14px; min-width: 0; } .stack > * + * { margin-top: 10px; } .card-heading { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; } .card-heading .small-label { display: block; margin-bottom: 2px; } .stat { position: relative; overflow: hidden; } .stat .value { font: 600 24px/1.15 var(--mono); letter-spacing: -1px; margin-top: 6px; } .stat .value small { font-size: 12px; color: var(--muted); letter-spacing: 0; margin-left: 4px; } .stat .sub { color: var(--muted); font-size: 10.5px; margin-top: 2px; } .stat svg.spark { position: absolute; right: 0; bottom: 0; width: 55%; height: 34px; opacity: .55; pointer-events: none; } .stat.accent .value { color: var(--accent); } .stat.good .value { color: var(--green); } .stat.bad .value { color: var(--red); } /* ---------------------------------------------------------------- tables */ .table-scroll { overflow: auto; } table { width: 100%; border-collapse: collapse; font-size: 11px; } th { font: 9.5px var(--mono); letter-spacing: .6px; color: var(--muted); font-weight: 400; text-transform: uppercase; border-bottom: 1px solid var(--line); padding: 7px 8px; text-align: left; white-space: nowrap; position: sticky; top: 0; background: var(--panel); } td { border-bottom: 1px solid var(--row-line); padding: 7px 8px; font-family: var(--mono); max-width: 280px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } tr.clickable { cursor: pointer; } tr.clickable:hover td { background: var(--nav-hover); } td.num, th.num { text-align: right; } td .sub { display: block; color: var(--muted); font-size: 10px; } .empty { color: var(--muted); padding: 18px 4px; text-align: center; } /* ---------------------------------------------------------------- forms */ .form-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px 14px; } .field.wide { grid-column: 1 / -1; } .field .help { font-size: 10.5px; color: var(--subtle); margin-top: 3px; line-height: 1.45; } .check { display: flex; align-items: center; gap: 8px; color: var(--text); font-size: 12px; margin-top: 18px; cursor: pointer; } .check input { flex: 0 0 auto; } .form-actions { display: flex; gap: 8px; align-items: center; justify-content: flex-end; margin-top: 12px; flex-wrap: wrap; } .section-title { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin: 2px 0 10px; } .dirty-flag { color: var(--accent); font: 10px var(--mono); } .test-row { display: grid; grid-template-columns: 1fr 1fr auto; gap: 8px; align-items: end; margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--line); } .result { margin-top: 8px; font: 11px/1.5 var(--mono); padding: 7px 9px; border-radius: var(--r-control); background: var(--code-bg); border: 1px solid var(--line); white-space: pre-wrap; overflow-wrap: anywhere; } .result.ok { color: var(--green); } .result.bad { color: var(--red); } .tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--line); margin-bottom: 12px; flex-wrap: wrap; } .tab { background: transparent; color: var(--muted); padding: 7px 11px; border-bottom: 2px solid transparent; font-size: 12px; } .tab.active { color: var(--text); border-bottom-color: var(--accent); } .toolbar { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; } .toolbar input, .toolbar select { width: auto; min-width: 180px; } /* ---------------------------------------------------------------- charts */ .chart { width: 100%; display: block; } .chart text { fill: var(--muted); font: 9.5px var(--mono); } .chart .grid-line { stroke: var(--grid); stroke-dasharray: 2 5; } .chart-wrap { position: relative; } .chart-tip { position: absolute; pointer-events: none; background: var(--panel2); border: 1px solid var(--line); border-radius: var(--r-control); padding: 5px 8px; font: 10.5px/1.5 var(--mono); box-shadow: 0 8px 20px var(--shadow); white-space: nowrap; z-index: 3; } .legend { display: flex; gap: 14px; flex-wrap: wrap; font: 10px var(--mono); color: var(--muted); margin-top: 6px; } .legend i { display: inline-block; width: 9px; height: 9px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; } .hbar { display: grid; grid-template-columns: 120px 1fr 54px; gap: 8px; align-items: center; font: 11px var(--mono); margin: 5px 0; } .hbar .track { height: 8px; background: var(--panel2); border-radius: 3px; overflow: hidden; } .hbar .fill { height: 100%; background: var(--accent); border-radius: 3px; } .hbar .n { text-align: right; color: var(--muted); } .donut-layout { display: grid; grid-template-columns: 130px 1fr; gap: 14px; align-items: center; } /* ---------------------------------------------------------------- live feed */ .feed { font: 11px/1.55 var(--mono); max-height: 460px; overflow: auto; } .feed-row { display: grid; grid-template-columns: 64px minmax(90px, 3fr) minmax(90px, 2fr) 72px 64px; gap: 10px; padding: 3px 4px; border-bottom: 1px solid var(--row-line); } .feed-row.fresh { animation: flash 1.2s ease-out; } @keyframes flash { from { background: color-mix(in srgb, var(--accent) 22%, transparent); } to { background: transparent; } } .feed-row .t { color: var(--subtle); } .feed-row .cmd { color: var(--accent); } .feed-row .err { color: var(--red); } .countdown { height: 3px; background: var(--panel2); border-radius: 2px; overflow: hidden; margin-top: 3px; } .countdown div { height: 100%; background: var(--green); } /* ---------------------------------------------------------------- dialog / toast */ .backdrop { position: fixed; inset: 0; background: var(--backdrop); z-index: 40; display: grid; place-items: center; padding: 16px; } .dialog { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; width: min(980px, 100%); max-height: calc(100vh - 32px); overflow: auto; box-shadow: 0 24px 60px var(--shadow); padding: 16px 18px; } .dialog.small { width: min(460px, 100%); } .dialog-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 12px; } .kv { display: grid; grid-template-columns: 150px 1fr; gap: 5px 12px; font-size: 11.5px; } .kv dt { color: var(--muted); } .kv dd { margin: 0; font-family: var(--mono); overflow-wrap: anywhere; } .actions-row { display: flex; gap: 6px; flex-wrap: wrap; margin: 12px 0; } .toast { position: fixed; right: 18px; bottom: 18px; z-index: 60; background: #d0dfb4; color: #1c2818; border: 1px solid #ebf2de; padding: 9px 13px; border-radius: 6px; box-shadow: 0 10px 28px var(--shadow); max-width: 420px; font-size: 12px; } .toast.error { background: #ffd2bd; color: #42241b; border-color: #ff9f7a; } /* ---------------------------------------------------------------- auth */ .auth-page { display: grid; place-items: center; min-height: 100vh; padding: 16px; } .auth-card { width: min(420px, 100%); } .auth-card .brand { margin-bottom: 22px; } .auth-card h1 { margin-bottom: 18px; } .auth-card form > * + * { margin-top: 10px; } .auth-card .primary { width: 100%; min-height: 34px; margin-top: 14px; } .command { display: block; font: 11px var(--mono); background: var(--code-bg); border: 1px solid var(--line); padding: 7px 9px; border-radius: 4px; margin: 6px 0; overflow-wrap: anywhere; } /* ---------------------------------------------------------------- loading */ .app-loading { display: grid; place-items: center; min-height: 100vh; color: var(--muted); font: 11px var(--mono); letter-spacing: 1.5px; } .spinner { width: 22px; height: 22px; border: 2px solid var(--line); border-top-color: var(--accent); border-radius: 50%; animation: spin .8s linear infinite; margin: 0 auto 12px; } @keyframes spin { to { transform: rotate(360deg); } } /* ---------------------------------------------------------------- responsive */ @media (max-width: 1100px) { .grid.stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } .grid.three { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (max-width: 880px) { .shell { grid-template-columns: 1fr; } .sidebar { position: static; height: auto; padding: 10px 14px; flex-direction: row; flex-wrap: wrap; align-items: center; } .sidebar .edition, .sidebar-bottom { display: none; } .mobile-toggle { display: inline-flex; margin-left: auto; } .main-navigation { display: none; width: 100%; } .main-navigation.open { display: flex; } main { padding: 0 14px 30px; } .grid.two, .grid.three { grid-template-columns: 1fr; } .grid.stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } .stat .value { font-size: 20px; } .test-row { grid-template-columns: 1fr; } .kv { grid-template-columns: 1fr; } .feed-row { grid-template-columns: 56px 1fr 60px; } .feed-row .u, .feed-row .ms { display: none; } .donut-layout { grid-template-columns: 1fr; } } /* chart colour classes (CSP: no inline styles) */ .k1 { stroke: var(--accent); fill: var(--accent); } .k2 { stroke: var(--green); fill: var(--green); } .k3 { stroke: var(--blue); fill: var(--blue); } .k4 { stroke: var(--violet); fill: var(--violet); } .k5 { stroke: var(--red); fill: var(--red); } .k6 { stroke: var(--sand); fill: var(--sand); } .k7 { stroke: var(--muted); fill: var(--muted); } .ln { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; } .ar { stroke: none; fill-opacity: .14; } .br { stroke: none; } .dn { fill: none; stroke-width: 14; } .cursor { stroke: var(--outline); stroke-dasharray: 3 3; } .sw.k1, .fill.k1 { background: var(--accent); } .sw.k2, .fill.k2 { background: var(--green); } .sw.k3, .fill.k3 { background: var(--blue); } .sw.k4, .fill.k4 { background: var(--violet); } .sw.k5, .fill.k5 { background: var(--red); } .sw.k6, .fill.k6 { background: var(--sand); } .sw.k7, .fill.k7 { background: var(--muted); } .donut-center { fill: var(--text); font: 600 15px var(--mono); } .mt { margin-top: 14px; } .card-heading input { max-width: 340px; } /* select dropdown colours (native popups ignore transparent backgrounds) */ select option, select optgroup { background: var(--panel); color: var(--text); } select.quiet { background: var(--panel); color: var(--text); } .feed-row > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .chip { display: inline-flex; align-items: center; gap: 5px; margin: 1px 4px 1px 0; padding: 1px 7px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel2); color: var(--text); font: 10.5px var(--mono); cursor: pointer; } .chip:hover { border-color: var(--accent); } .chip .signal { margin: 0; width: 6px; height: 6px; } td.wrap { white-space: normal; max-width: 420px; }