feat(web): SPA skeleton embedded in the binary (#60)
This commit was merged in pull request #60.
This commit is contained in:
@@ -0,0 +1,488 @@
|
||||
/* arr — master-control board.
|
||||
Tokens mirror .impeccable/design.json; components consume tokens, never
|
||||
literals. Committed dark: a control room is lit by its lamps. */
|
||||
|
||||
@font-face {
|
||||
font-family: "Rajdhani";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url("/fonts/rajdhani-600-latin.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Rajdhani";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url("/fonts/rajdhani-700-latin.woff2") format("woff2");
|
||||
}
|
||||
|
||||
:root {
|
||||
/* ground and panels */
|
||||
--ground: oklch(0.14 0.01 250);
|
||||
--panel: oklch(0.18 0.012 250);
|
||||
--panel-raised: oklch(0.21 0.014 250);
|
||||
--line: oklch(0.3 0.015 250);
|
||||
--line-strong: oklch(0.42 0.02 250);
|
||||
--highlight: oklch(1 0 0 / 4%);
|
||||
|
||||
/* ink */
|
||||
--ink: oklch(0.93 0.008 250);
|
||||
--ink-muted: oklch(0.72 0.015 250);
|
||||
--ink-faint: oklch(0.58 0.015 250);
|
||||
|
||||
/* signal lamps */
|
||||
--signal-ok: oklch(0.8 0.17 150);
|
||||
--signal-warn: oklch(0.82 0.14 80);
|
||||
--signal-fault: oklch(0.68 0.21 25);
|
||||
--signal-idle: oklch(0.45 0.02 250);
|
||||
|
||||
/* the one interactive hue */
|
||||
--accent: oklch(0.8 0.12 220);
|
||||
|
||||
/* type */
|
||||
--font-label: system-ui, "Segoe UI", sans-serif;
|
||||
--font-readout:
|
||||
ui-monospace, "SF Mono", "Cascadia Mono", "JetBrains Mono", Menlo, Consolas, monospace;
|
||||
--font-display: "Rajdhani", "Avenir Next Condensed", "Arial Narrow", sans-serif;
|
||||
|
||||
/* rhythm */
|
||||
--space-1: 0.25rem;
|
||||
--space-2: 0.5rem;
|
||||
--space-3: 0.75rem;
|
||||
--space-4: 1rem;
|
||||
--space-6: 1.5rem;
|
||||
--space-8: 2rem;
|
||||
--space-12: 3rem;
|
||||
|
||||
--radius: 4px;
|
||||
--ease-out: cubic-bezier(0.25, 1, 0.5, 1);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--ground);
|
||||
color: var(--ink);
|
||||
font-family: var(--font-label);
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.readout {
|
||||
font-family: var(--font-readout);
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.dim {
|
||||
color: var(--ink-muted);
|
||||
}
|
||||
|
||||
/* ---- rails ---------------------------------------------------------- */
|
||||
|
||||
.rail {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-6);
|
||||
padding: var(--space-3) var(--space-6);
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: var(--panel);
|
||||
box-shadow: inset 0 1px var(--highlight);
|
||||
}
|
||||
|
||||
.rail-foot {
|
||||
margin-top: auto;
|
||||
border-bottom: 0;
|
||||
border-top: 1px solid var(--line);
|
||||
justify-content: space-between;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.rail-id {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.wordmark {
|
||||
margin: 0;
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.625rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1em;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.rail-verdict {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
color: var(--ink-muted);
|
||||
}
|
||||
|
||||
.rail-verdict[data-tone="ok"] {
|
||||
color: var(--signal-ok);
|
||||
}
|
||||
|
||||
.rail-verdict[data-tone="warn"] {
|
||||
color: var(--signal-warn);
|
||||
}
|
||||
|
||||
.rail-verdict[data-tone="fault"] {
|
||||
color: var(--signal-fault);
|
||||
}
|
||||
|
||||
.rail-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
/* ---- lamps ---------------------------------------------------------- */
|
||||
|
||||
.lamp {
|
||||
--lamp: var(--signal-idle);
|
||||
width: 0.625rem;
|
||||
height: 0.625rem;
|
||||
flex: none;
|
||||
border-radius: 50%;
|
||||
background: var(--lamp);
|
||||
/* the panel's own device: a lit indicator, not a depth shadow */
|
||||
box-shadow:
|
||||
0 0 6px var(--lamp),
|
||||
0 0 14px oklch(from var(--lamp) l c h / 35%);
|
||||
transition: background 300ms var(--ease-out);
|
||||
}
|
||||
|
||||
.lamp-big {
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
}
|
||||
|
||||
.lamp[data-state="ok"] {
|
||||
--lamp: var(--signal-ok);
|
||||
}
|
||||
|
||||
.lamp[data-state="unconfigured"] {
|
||||
--lamp: var(--signal-warn);
|
||||
}
|
||||
|
||||
.lamp[data-state="degraded"] {
|
||||
--lamp: var(--signal-warn);
|
||||
}
|
||||
|
||||
.lamp[data-state="unreachable"] {
|
||||
--lamp: var(--signal-fault);
|
||||
}
|
||||
|
||||
.lamp[data-state="probing"] {
|
||||
--lamp: var(--signal-idle);
|
||||
animation: lamp-probe 1.2s var(--ease-out) infinite alternate;
|
||||
}
|
||||
|
||||
/* dead air: dark, no glow, no motion */
|
||||
.lamp[data-state="off"] {
|
||||
--lamp: oklch(0.3 0.012 250);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@keyframes lamp-probe {
|
||||
from {
|
||||
opacity: 0.45;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* power-up: lamps flicker on left to right, once, on first verdict */
|
||||
@keyframes lamp-strike {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
40% {
|
||||
opacity: 1;
|
||||
}
|
||||
55% {
|
||||
opacity: 0.35;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.board.powered .lamp {
|
||||
animation: lamp-strike 420ms var(--ease-out) both;
|
||||
animation-delay: calc(var(--strike, 0) * 120ms);
|
||||
}
|
||||
|
||||
.board.powered .lamp[data-state="off"] {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.lamp[data-state="probing"],
|
||||
.board.powered .lamp {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- board and chain ------------------------------------------------- */
|
||||
|
||||
.board {
|
||||
width: 100%;
|
||||
max-width: 68rem;
|
||||
margin: 0 auto;
|
||||
padding: var(--space-12) var(--space-6);
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chain {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chain {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2.5rem 1.2fr 2.5rem 1fr;
|
||||
grid-template-areas:
|
||||
"tmdb t1 master out sink"
|
||||
"prow t2 master out sink";
|
||||
align-items: center;
|
||||
row-gap: var(--space-6);
|
||||
}
|
||||
|
||||
.area-tmdb {
|
||||
grid-area: tmdb;
|
||||
}
|
||||
|
||||
.area-prowlarr {
|
||||
grid-area: prow;
|
||||
}
|
||||
|
||||
.area-t1 {
|
||||
grid-area: t1;
|
||||
}
|
||||
|
||||
.area-t2 {
|
||||
grid-area: t2;
|
||||
}
|
||||
|
||||
.area-master {
|
||||
grid-area: master;
|
||||
}
|
||||
|
||||
.area-out {
|
||||
grid-area: out;
|
||||
}
|
||||
|
||||
.area-transmission {
|
||||
grid-area: sink;
|
||||
}
|
||||
|
||||
/* ---- modules --------------------------------------------------------- */
|
||||
|
||||
.module {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: inset 0 1px var(--highlight);
|
||||
padding: var(--space-4) var(--space-4) var(--space-3);
|
||||
}
|
||||
|
||||
.module-master {
|
||||
background: var(--panel-raised);
|
||||
border-color: var(--line-strong);
|
||||
padding: var(--space-6) var(--space-4) var(--space-4);
|
||||
}
|
||||
|
||||
.module-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-1) var(--space-2);
|
||||
}
|
||||
|
||||
.module-name {
|
||||
margin: 0;
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.module-master .module-name {
|
||||
font-size: 1.3125rem;
|
||||
}
|
||||
|
||||
.module-status {
|
||||
margin-left: auto;
|
||||
padding-left: var(--space-3);
|
||||
color: var(--ink-muted);
|
||||
}
|
||||
|
||||
.module-status[data-tone="ok"] {
|
||||
color: var(--signal-ok);
|
||||
}
|
||||
|
||||
.module-status[data-tone="warn"] {
|
||||
color: var(--signal-warn);
|
||||
}
|
||||
|
||||
.module-status[data-tone="fault"] {
|
||||
color: var(--signal-fault);
|
||||
}
|
||||
|
||||
.module-role {
|
||||
margin: var(--space-2) 0 0;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
color: var(--ink-muted);
|
||||
}
|
||||
|
||||
.module-detail {
|
||||
margin: var(--space-1) 0 0;
|
||||
font-size: 0.75rem;
|
||||
color: var(--ink-muted);
|
||||
min-height: 1.2em;
|
||||
}
|
||||
|
||||
/* ---- bus traces ------------------------------------------------------ */
|
||||
|
||||
.bus {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.bus-trace {
|
||||
--trace: var(--line-strong);
|
||||
width: 100%;
|
||||
height: 0.5rem;
|
||||
display: block;
|
||||
transition: filter 300ms var(--ease-out);
|
||||
}
|
||||
|
||||
.bus-trace line {
|
||||
stroke: var(--trace);
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.bus-arrow {
|
||||
fill: var(--trace);
|
||||
}
|
||||
|
||||
.bus-trace[data-tone="ok"] {
|
||||
--trace: var(--signal-ok);
|
||||
}
|
||||
|
||||
.bus-trace[data-tone="warn"] {
|
||||
--trace: var(--signal-warn);
|
||||
}
|
||||
|
||||
.bus-trace[data-tone="fault"] {
|
||||
--trace: var(--signal-fault);
|
||||
}
|
||||
|
||||
/* ---- controls -------------------------------------------------------- */
|
||||
|
||||
.control {
|
||||
min-height: 2.75rem;
|
||||
padding: 0 var(--space-4);
|
||||
font-family: var(--font-readout);
|
||||
font-size: 0.8125rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--accent);
|
||||
background: var(--panel-raised);
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: inset 0 1px var(--highlight);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 150ms var(--ease-out),
|
||||
color 150ms var(--ease-out);
|
||||
}
|
||||
|
||||
.control:hover {
|
||||
border-color: var(--accent);
|
||||
color: oklch(from var(--accent) calc(l + 0.08) c h);
|
||||
}
|
||||
|
||||
.control:active {
|
||||
box-shadow: inset 0 1px oklch(0 0 0 / 30%);
|
||||
}
|
||||
|
||||
.control:disabled {
|
||||
color: var(--ink-faint);
|
||||
border-color: var(--line);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ---- small screens --------------------------------------------------- */
|
||||
|
||||
@media (max-width: 46rem) {
|
||||
.rail {
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-3) var(--space-4);
|
||||
}
|
||||
|
||||
.rail-verdict {
|
||||
flex-basis: 100%;
|
||||
order: 3;
|
||||
}
|
||||
|
||||
.board {
|
||||
padding: var(--space-6) var(--space-4);
|
||||
}
|
||||
|
||||
.chain {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas:
|
||||
"tmdb"
|
||||
"prow"
|
||||
"t1"
|
||||
"master"
|
||||
"out"
|
||||
"sink";
|
||||
row-gap: var(--space-4);
|
||||
}
|
||||
|
||||
/* one junctioned trace on the stacked chain; the second is redundant */
|
||||
.area-t2 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bus {
|
||||
height: 2.25rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bus svg {
|
||||
transform: rotate(90deg);
|
||||
transform-origin: center;
|
||||
width: 2.25rem;
|
||||
height: 0.5rem;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user