In-library hits now read as whatever they are: movies open the release deck as before, series hits render display-only until issue 129's detail view gives them a link target, and an episode hit reads as its series plus SxxEyy plus the episode title. TMDB results show TV alongside film, and the add panel filters roots by kind; a series adds the one §4.1 add-time choice, auto_track, stated plainly: future seasons are tracked automatically, past seasons are not.
1366 lines
27 KiB
CSS
1366 lines
27 KiB
CSS
/* 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);
|
|
--accent-bright: oklch(0.88 0.12 220);
|
|
|
|
/* dead lamp and pressed-control lowlight */
|
|
--lamp-off: oklch(0.3 0.012 250);
|
|
--lowlight: oklch(0 0 0 / 30%);
|
|
|
|
/* release verdicts (§9.3) — rejected is quiet slate, never fault red */
|
|
--verdict-eligible: oklch(0.8 0.17 150);
|
|
--verdict-waived: oklch(0.82 0.14 80);
|
|
--verdict-rejected: oklch(0.68 0.02 250);
|
|
|
|
/* score heat scale (issue #111) — one list-relative gradient from lowest
|
|
red to highest green; JS picks the stop, colour stays in tokens */
|
|
--score-0: oklch(0.68 0.19 25);
|
|
--score-1: oklch(0.7 0.185 46);
|
|
--score-2: oklch(0.72 0.175 67);
|
|
--score-3: oklch(0.75 0.165 88);
|
|
--score-4: oklch(0.77 0.16 108);
|
|
--score-5: oklch(0.79 0.165 129);
|
|
--score-6: oklch(0.8 0.17 150);
|
|
|
|
/* library status (§4.2) — informational, never a severity ramp:
|
|
activity in one violet family, satisfaction in neutrals */
|
|
--status-airing: oklch(0.78 0.14 305);
|
|
--status-incomplete: oklch(0.72 0.09 305);
|
|
--status-waiting: oklch(0.64 0.045 305);
|
|
--status-complete: oklch(0.85 0.01 250);
|
|
--status-ended: oklch(0.62 0.015 250);
|
|
|
|
/* 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;
|
|
|
|
/* type scale */
|
|
--text-2xs: 0.6875rem;
|
|
--text-xs: 0.75rem;
|
|
--text-sm: 0.8125rem;
|
|
--text-base: 1rem;
|
|
--text-md: 1.0625rem;
|
|
--text-lg: 1.3125rem;
|
|
--text-xl: 1.625rem;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* a scrollbar appearing must not reflow the rail's wrap */
|
|
html {
|
|
scrollbar-gutter: stable;
|
|
}
|
|
|
|
body {
|
|
min-height: 100dvh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: var(--ground);
|
|
color: var(--ink);
|
|
font-family: var(--font-label);
|
|
font-size: var(--text-base);
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.readout {
|
|
font-family: var(--font-readout);
|
|
font-size: var(--text-sm);
|
|
}
|
|
|
|
.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-id {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-3);
|
|
}
|
|
|
|
.wordmark {
|
|
margin: 0;
|
|
font-family: var(--font-display);
|
|
font-size: var(--text-xl);
|
|
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: var(--lamp-off);
|
|
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;
|
|
}
|
|
|
|
/* the deck swaps in over the board; flex would beat [hidden] otherwise */
|
|
.board[hidden] {
|
|
display: none;
|
|
}
|
|
|
|
.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: var(--text-md);
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.12em;
|
|
line-height: 1;
|
|
}
|
|
|
|
.module-master .module-name {
|
|
font-size: var(--text-lg);
|
|
}
|
|
|
|
.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: var(--text-2xs);
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.12em;
|
|
color: var(--ink-muted);
|
|
}
|
|
|
|
.module-detail {
|
|
margin: var(--space-1) 0 0;
|
|
font-size: var(--text-xs);
|
|
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: var(--text-sm);
|
|
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: var(--accent-bright);
|
|
}
|
|
|
|
.control:active {
|
|
box-shadow: inset 0 1px var(--lowlight);
|
|
}
|
|
|
|
.control:disabled {
|
|
color: var(--ink-faint);
|
|
border-color: var(--line);
|
|
cursor: default;
|
|
}
|
|
|
|
/* the engaged view control stays lit while its surface is up */
|
|
.control[aria-pressed="true"] {
|
|
border-color: var(--accent);
|
|
color: var(--accent-bright);
|
|
}
|
|
|
|
:focus-visible {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
/* ---- unified search (§9.2) ------------------------------------------- */
|
|
|
|
.rail-search {
|
|
position: relative;
|
|
flex: 1;
|
|
max-width: 30rem;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.search-input {
|
|
width: 100%;
|
|
min-height: 2.75rem;
|
|
padding: 0 var(--space-8) 0 var(--space-3);
|
|
color: var(--ink);
|
|
background: var(--ground);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
box-shadow: inset 0 1px var(--lowlight);
|
|
caret-color: var(--accent);
|
|
transition: border-color 150ms var(--ease-out);
|
|
}
|
|
|
|
.search-input::placeholder {
|
|
color: var(--ink-faint);
|
|
opacity: 1;
|
|
}
|
|
|
|
.search-input:hover {
|
|
border-color: var(--line-strong);
|
|
}
|
|
|
|
.search-input:focus {
|
|
border-color: var(--accent);
|
|
outline: none;
|
|
}
|
|
|
|
.search-hint {
|
|
position: absolute;
|
|
right: var(--space-3);
|
|
padding: 0 var(--space-1);
|
|
font-size: var(--text-2xs);
|
|
color: var(--ink-faint);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* ---- search deck ------------------------------------------------------ */
|
|
|
|
.deck {
|
|
width: 100%;
|
|
max-width: 68rem;
|
|
margin: 0 auto;
|
|
padding: var(--space-8) var(--space-6) var(--space-12);
|
|
flex: 1;
|
|
}
|
|
|
|
.deck-status {
|
|
margin: 0 0 var(--space-4);
|
|
color: var(--ink-muted);
|
|
}
|
|
|
|
.deck-status[data-tone="fault"] {
|
|
color: var(--signal-fault);
|
|
}
|
|
|
|
/* a sweep in flight borrows the rail's probing-lamp idiom, inline */
|
|
.deck-status .lamp {
|
|
display: inline-block;
|
|
margin-right: var(--space-2);
|
|
vertical-align: baseline;
|
|
}
|
|
|
|
.deck-group {
|
|
margin: 0 0 var(--space-8);
|
|
}
|
|
|
|
.deck-head {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: var(--space-3);
|
|
padding: 0 var(--space-1) var(--space-2);
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
|
|
/* the engraved micro-label is the heading itself, not a kicker */
|
|
.deck-label {
|
|
margin: 0;
|
|
font-size: var(--text-2xs);
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.12em;
|
|
color: var(--ink-muted);
|
|
}
|
|
|
|
.deck-count {
|
|
font-size: var(--text-xs);
|
|
color: var(--ink-faint);
|
|
}
|
|
|
|
.deck-rows {
|
|
margin: 0;
|
|
padding: 0;
|
|
list-style: none;
|
|
}
|
|
|
|
.row {
|
|
display: flex;
|
|
align-items: baseline;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-1) var(--space-3);
|
|
width: 100%;
|
|
padding: var(--space-2) var(--space-1);
|
|
border-bottom: 1px solid oklch(from var(--line) l c h / 45%);
|
|
text-align: left;
|
|
}
|
|
|
|
.row-id {
|
|
display: inline-flex;
|
|
align-items: baseline;
|
|
gap: var(--space-2);
|
|
min-width: 0;
|
|
}
|
|
|
|
/* titles are names: the display face, per the Readout Rule in design.json */
|
|
.row-title {
|
|
font-family: var(--font-display);
|
|
font-size: var(--text-md);
|
|
font-weight: 600;
|
|
letter-spacing: 0.02em;
|
|
color: var(--ink);
|
|
}
|
|
|
|
.row-year {
|
|
font-size: var(--text-xs);
|
|
}
|
|
|
|
.row-chips {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--space-1);
|
|
margin-left: auto;
|
|
}
|
|
|
|
.chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--space-1);
|
|
min-height: 1.375rem;
|
|
padding: 0 var(--space-2);
|
|
background: var(--panel-raised);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
font-family: var(--font-readout);
|
|
font-size: var(--text-xs);
|
|
color: var(--ink-muted);
|
|
}
|
|
|
|
.chip[data-verdict="eligible"] {
|
|
color: var(--verdict-eligible);
|
|
border-color: oklch(from var(--verdict-eligible) l c h / 45%);
|
|
}
|
|
|
|
/* the dashed border is the visible mark of a relaxed rule */
|
|
.chip[data-verdict="waived"] {
|
|
color: var(--verdict-waived);
|
|
border-style: dashed;
|
|
border-color: oklch(from var(--verdict-waived) l c h / 55%);
|
|
}
|
|
|
|
/* rejected is the policy working, never fault red */
|
|
.chip[data-verdict="rejected"] {
|
|
color: var(--verdict-rejected);
|
|
}
|
|
|
|
.chip[data-movie-state="available"] {
|
|
color: var(--ink);
|
|
}
|
|
|
|
/* library status (§4.2): informational, never a severity ramp — activity in
|
|
the channel-violet family, satisfaction in neutrals; the word is always
|
|
present, so the state survives with colour removed */
|
|
.chip[data-status="airing"] {
|
|
color: var(--status-airing);
|
|
border-color: oklch(from var(--status-airing) l c h / 45%);
|
|
}
|
|
|
|
.chip[data-status="incomplete"] {
|
|
color: var(--status-incomplete);
|
|
}
|
|
|
|
.chip[data-status="waiting"] {
|
|
color: var(--status-waiting);
|
|
}
|
|
|
|
.chip[data-status="complete"] {
|
|
color: var(--status-complete);
|
|
}
|
|
|
|
.chip[data-status="ended"] {
|
|
color: var(--status-ended);
|
|
}
|
|
|
|
/* a TMDB row is the affordance: the whole row opens the add flow */
|
|
.row-tmdb {
|
|
font: inherit;
|
|
background: none;
|
|
border: 0;
|
|
border-bottom: 1px solid oklch(from var(--line) l c h / 45%);
|
|
cursor: pointer;
|
|
transition: background 150ms var(--ease-out);
|
|
}
|
|
|
|
.row-tmdb:hover:not(:disabled),
|
|
.row-tmdb:focus-visible {
|
|
background: var(--panel);
|
|
}
|
|
|
|
.row-tmdb:disabled {
|
|
cursor: default;
|
|
}
|
|
|
|
.row-add {
|
|
font-size: var(--text-xs);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
color: var(--accent);
|
|
}
|
|
|
|
.row-overview {
|
|
flex-basis: 100%;
|
|
overflow: hidden;
|
|
font-size: var(--text-xs);
|
|
color: var(--ink-faint);
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
/* an episode hit's title sits beside its SxxEyy chip: a name, not a readout */
|
|
.row-sub {
|
|
font-size: var(--text-sm);
|
|
color: var(--ink-muted);
|
|
}
|
|
|
|
/* ---- add flow (inline, root and policy pre-filled) -------------------- */
|
|
|
|
.add-panel {
|
|
margin: 0 0 var(--space-2);
|
|
padding: var(--space-3) var(--space-3) var(--space-3);
|
|
background: var(--panel);
|
|
border: 1px solid var(--line);
|
|
border-top: 0;
|
|
border-radius: 0 0 var(--radius) var(--radius);
|
|
box-shadow: inset 0 1px var(--highlight);
|
|
}
|
|
|
|
.add-roots {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
|
|
gap: var(--space-2);
|
|
}
|
|
|
|
.root-option {
|
|
display: grid;
|
|
gap: var(--space-1);
|
|
padding: var(--space-2) var(--space-3);
|
|
font: inherit;
|
|
text-align: left;
|
|
color: var(--ink);
|
|
background: var(--panel-raised);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
box-shadow: inset 0 1px var(--highlight);
|
|
cursor: pointer;
|
|
transition: border-color 150ms var(--ease-out);
|
|
}
|
|
|
|
.root-option:hover {
|
|
border-color: var(--line-strong);
|
|
}
|
|
|
|
.root-option[aria-pressed="true"] {
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
.root-audience {
|
|
font-family: var(--font-display);
|
|
font-size: var(--text-md);
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.12em;
|
|
line-height: 1;
|
|
}
|
|
|
|
.root-policy,
|
|
.root-path {
|
|
font-size: var(--text-xs);
|
|
}
|
|
|
|
.add-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-3);
|
|
margin: var(--space-3) 0 0;
|
|
}
|
|
|
|
/* the one series-level add-time question (§4.1): the choice and what it does */
|
|
.add-autotrack {
|
|
display: grid;
|
|
justify-content: start;
|
|
gap: var(--space-1);
|
|
margin: var(--space-3) 0 0;
|
|
}
|
|
|
|
.add-note {
|
|
margin: 0;
|
|
font-size: var(--text-xs);
|
|
color: var(--ink-muted);
|
|
}
|
|
|
|
.add-note[data-tone="fault"] {
|
|
color: var(--signal-fault);
|
|
}
|
|
|
|
.add-done {
|
|
margin: 0;
|
|
color: var(--verdict-eligible);
|
|
}
|
|
|
|
/* ---- release deck (§9.3) ---------------------------------------------- */
|
|
|
|
.releases-head {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-2) var(--space-3);
|
|
margin: 0 0 var(--space-6);
|
|
}
|
|
|
|
.releases-id {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: var(--space-2);
|
|
}
|
|
|
|
.releases-title {
|
|
margin: 0;
|
|
font-family: var(--font-display);
|
|
font-size: var(--text-lg);
|
|
font-weight: 700;
|
|
letter-spacing: 0.04em;
|
|
line-height: 1.1;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.releases-year {
|
|
font-size: var(--text-xs);
|
|
}
|
|
|
|
/* ---- removal confirmation (§7.4, issue 104) --------------------------- */
|
|
|
|
/* remove is not the deck's errand: it reads as a quiet control until the
|
|
operator reaches for it, and never competes with the sweep */
|
|
.control-quiet {
|
|
color: var(--ink-muted);
|
|
border-color: var(--line);
|
|
}
|
|
|
|
.control-quiet:hover,
|
|
.control-quiet[aria-expanded="true"] {
|
|
color: var(--accent-bright);
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
.remove-panel {
|
|
margin: 0 0 var(--space-6);
|
|
}
|
|
|
|
.remove-body {
|
|
display: grid;
|
|
gap: var(--space-2);
|
|
padding: var(--space-3);
|
|
background: var(--panel);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
box-shadow: inset 0 1px var(--highlight);
|
|
}
|
|
|
|
/* armed is a state, not a toggle: it is amber whenever files are on disk to
|
|
delete */
|
|
.remove-body[data-armed="true"] {
|
|
border-color: oklch(from var(--signal-warn) l c h / 55%);
|
|
}
|
|
|
|
.remove-evidence,
|
|
.remove-path,
|
|
.remove-note {
|
|
margin: 0;
|
|
font-size: var(--text-xs);
|
|
}
|
|
|
|
/* the folder is evidence: it wraps rather than truncating, because a path
|
|
the operator cannot read whole is not a confirmation */
|
|
.remove-path {
|
|
color: var(--ink-muted);
|
|
overflow-wrap: anywhere;
|
|
white-space: pre-line;
|
|
}
|
|
|
|
.remove-note {
|
|
color: var(--ink-muted);
|
|
}
|
|
|
|
.remove-note[data-tone="warn"] {
|
|
color: var(--signal-warn);
|
|
}
|
|
|
|
.remove-note[data-tone="fault"] {
|
|
color: var(--signal-fault);
|
|
}
|
|
|
|
.remove-actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-2);
|
|
margin: var(--space-1) 0 0;
|
|
}
|
|
|
|
.remove-body[data-armed="true"] .remove-actions .control:first-child {
|
|
color: var(--signal-warn);
|
|
border-color: oklch(from var(--signal-warn) l c h / 55%);
|
|
}
|
|
|
|
.remove-body[data-armed="true"] .remove-actions .control:first-child:hover:not(:disabled) {
|
|
border-color: var(--signal-warn);
|
|
}
|
|
|
|
.bucket-head {
|
|
align-items: center;
|
|
}
|
|
|
|
.bucket-toggle {
|
|
margin-left: auto;
|
|
min-height: 2.75rem;
|
|
padding: 0 var(--space-2);
|
|
font-size: var(--text-xs);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
color: var(--accent);
|
|
background: none;
|
|
border: 0;
|
|
cursor: pointer;
|
|
transition: color 150ms var(--ease-out);
|
|
}
|
|
|
|
.bucket-toggle:hover {
|
|
color: var(--accent-bright);
|
|
}
|
|
|
|
/* fixed-width mono columns: chips and header labels share rem widths, so
|
|
the pseudo-table survives font-size differences and flex wrapping alike */
|
|
.cw {
|
|
display: inline-block;
|
|
flex: none;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
line-height: 1.25rem;
|
|
}
|
|
|
|
.cw-score {
|
|
width: 4.5rem;
|
|
text-align: right;
|
|
}
|
|
|
|
.cw-res {
|
|
width: 4rem;
|
|
}
|
|
|
|
.cw-src {
|
|
width: 4.5rem;
|
|
}
|
|
|
|
.cw-hdr {
|
|
width: 6rem;
|
|
}
|
|
|
|
.cw-aud {
|
|
width: 7.5rem;
|
|
}
|
|
|
|
.cw-size {
|
|
width: 4.5rem;
|
|
text-align: right;
|
|
}
|
|
|
|
.cw-seed {
|
|
width: 3.5rem;
|
|
text-align: right;
|
|
}
|
|
|
|
/* §9.3: headers never wrap — the narrow-deck block below sheds columns
|
|
instead */
|
|
.colhead {
|
|
display: flex;
|
|
gap: var(--space-1);
|
|
padding: var(--space-2) var(--space-1) 0;
|
|
}
|
|
|
|
.colhead .cw {
|
|
padding: 0 var(--space-2);
|
|
border: 1px solid transparent;
|
|
font-family: var(--font-readout);
|
|
font-size: var(--text-2xs);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
color: var(--ink-faint);
|
|
}
|
|
|
|
.rel {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-1) var(--space-2);
|
|
padding: var(--space-2) var(--space-1);
|
|
border-bottom: 1px solid oklch(from var(--line) l c h / 45%);
|
|
}
|
|
|
|
/* the line is plain content now — grab is its own control */
|
|
.rel-line {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-1);
|
|
}
|
|
|
|
/* §9.3: the release name is secondary — evidence, not the primary key */
|
|
.rel-name {
|
|
flex-basis: 100%;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
font-size: var(--text-xs);
|
|
color: var(--ink-faint);
|
|
}
|
|
|
|
.rel-none {
|
|
padding: var(--space-2) var(--space-1);
|
|
font-size: var(--text-xs);
|
|
border-bottom: 1px solid oklch(from var(--line) l c h / 45%);
|
|
}
|
|
|
|
.rel-note {
|
|
font-size: var(--text-xs);
|
|
color: var(--ink-muted);
|
|
}
|
|
|
|
.rel-note[data-tone="fault"] {
|
|
color: var(--signal-fault);
|
|
}
|
|
|
|
/* score heat: data-score is the list-relative stop, 0 lowest … 6 highest */
|
|
.rel-line .cw-score[data-score="0"] {
|
|
color: var(--score-0);
|
|
border-color: oklch(from var(--score-0) l c h / 45%);
|
|
}
|
|
.rel-line .cw-score[data-score="1"] {
|
|
color: var(--score-1);
|
|
border-color: oklch(from var(--score-1) l c h / 45%);
|
|
}
|
|
.rel-line .cw-score[data-score="2"] {
|
|
color: var(--score-2);
|
|
border-color: oklch(from var(--score-2) l c h / 45%);
|
|
}
|
|
.rel-line .cw-score[data-score="3"] {
|
|
color: var(--score-3);
|
|
border-color: oklch(from var(--score-3) l c h / 45%);
|
|
}
|
|
.rel-line .cw-score[data-score="4"] {
|
|
color: var(--score-4);
|
|
border-color: oklch(from var(--score-4) l c h / 45%);
|
|
}
|
|
.rel-line .cw-score[data-score="5"] {
|
|
color: var(--score-5);
|
|
border-color: oklch(from var(--score-5) l c h / 45%);
|
|
}
|
|
.rel-line .cw-score[data-score="6"] {
|
|
color: var(--score-6);
|
|
border-color: oklch(from var(--score-6) l c h / 45%);
|
|
}
|
|
|
|
/* ---- library view (§4.2) ---------------------------------------------- */
|
|
|
|
.rail-nav {
|
|
flex: none;
|
|
}
|
|
|
|
/* the library's master line: total readout left, the one §4.2 toggle right */
|
|
.library-bar {
|
|
margin: 0 0 var(--space-6);
|
|
}
|
|
|
|
/* the toggle's own padding would inset its text past the chip column's
|
|
right rag; drop it so SATISFIED sits flush with the chips below */
|
|
.library-bar .bucket-toggle {
|
|
padding: 0;
|
|
}
|
|
|
|
/* ---- attention queues (§5.2 + §5.7) ------------------------------------ */
|
|
|
|
/* the badge is a signal, not a control: amber attention on the cyan word */
|
|
.nav-count {
|
|
margin-left: var(--space-2);
|
|
font-size: var(--text-xs);
|
|
color: var(--signal-warn);
|
|
}
|
|
|
|
.queue-note {
|
|
margin: var(--space-2) var(--space-1) 0;
|
|
font-size: var(--text-xs);
|
|
color: var(--ink-faint);
|
|
}
|
|
|
|
/* like .rel: the line expands to fill, the action is its own control */
|
|
.queue-item {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-1) var(--space-3);
|
|
padding: var(--space-2) var(--space-1);
|
|
border-bottom: 1px solid oklch(from var(--line) l c h / 45%);
|
|
}
|
|
|
|
.queue-item .queue-line {
|
|
flex: 1;
|
|
min-width: 0;
|
|
width: auto;
|
|
padding: 0;
|
|
border-bottom: 0;
|
|
}
|
|
|
|
.queue-empty {
|
|
padding: var(--space-2) var(--space-1);
|
|
font-size: var(--text-xs);
|
|
border-bottom: 1px solid oklch(from var(--line) l c h / 45%);
|
|
}
|
|
|
|
.queue-feedback {
|
|
font-size: var(--text-xs);
|
|
color: var(--ink-muted);
|
|
}
|
|
|
|
.queue-feedback[data-tone="fault"] {
|
|
color: var(--signal-fault);
|
|
}
|
|
|
|
/* ---- manual intake ---------------------------------------------------- */
|
|
|
|
.intake .module-detail {
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
/* ---- small screens --------------------------------------------------- */
|
|
|
|
@media (max-width: 46rem) {
|
|
.rail {
|
|
flex-wrap: wrap;
|
|
gap: var(--space-3) var(--space-4);
|
|
}
|
|
|
|
.rail-verdict {
|
|
flex-basis: 100%;
|
|
order: 3;
|
|
}
|
|
|
|
.rail-search {
|
|
order: 4;
|
|
flex-basis: 100%;
|
|
max-width: none;
|
|
}
|
|
|
|
/* deterministic first row: identity, views, then meta pushed right —
|
|
never re-wrapped by content or scrollbar changes */
|
|
.rail-nav {
|
|
order: 2;
|
|
}
|
|
|
|
.rail-meta {
|
|
order: 2;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.deck {
|
|
padding: var(--space-6) var(--space-4) var(--space-8);
|
|
}
|
|
|
|
.row-chips {
|
|
margin-left: 0;
|
|
flex-basis: 100%;
|
|
}
|
|
|
|
.releases-id {
|
|
order: -1;
|
|
flex-basis: 100%;
|
|
}
|
|
|
|
/* the line shrinks rather than wrapping, so the grab control never
|
|
leaves the row — the release name absorbs the squeeze via ellipsis */
|
|
.rel-grab {
|
|
margin-left: auto;
|
|
}
|
|
|
|
/* same wrap on a queue entry: the one click drops below its line */
|
|
.queue-item .queue-line {
|
|
flex-basis: 100%;
|
|
}
|
|
|
|
.queue-allow {
|
|
margin-left: auto;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
}
|
|
|
|
/* ---- narrow deck (§9.3 at phone widths) ------------------------------- */
|
|
|
|
/* the full seven-column line is 36rem; under 40rem it cannot fit, so hdr
|
|
and audio leave the line and the five surviving columns tighten —
|
|
aligned, never wrapped, never scrolled */
|
|
@media (max-width: 40rem) {
|
|
.colhead .cw,
|
|
.rel-line .chip {
|
|
padding: 0 var(--space-1);
|
|
}
|
|
|
|
.cw-hdr,
|
|
.cw-aud {
|
|
display: none;
|
|
}
|
|
|
|
.cw-score {
|
|
width: 3rem;
|
|
}
|
|
|
|
.cw-res {
|
|
width: 3rem;
|
|
}
|
|
|
|
.cw-src {
|
|
width: 3.5rem;
|
|
}
|
|
|
|
.cw-size {
|
|
width: 3rem;
|
|
}
|
|
|
|
.cw-seed {
|
|
width: 2.5rem;
|
|
}
|
|
}
|
|
|
|
/* ---- settings (issue #116) -------------------------------------------- */
|
|
|
|
/* rows follow the queues pattern: line, then an action pair */
|
|
.settings-row-actions {
|
|
display: flex;
|
|
gap: var(--space-2);
|
|
margin: var(--space-1) 0 var(--space-2);
|
|
}
|
|
|
|
.form-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
|
|
gap: var(--space-3);
|
|
align-items: end;
|
|
}
|
|
|
|
.field {
|
|
display: grid;
|
|
gap: var(--space-1);
|
|
}
|
|
|
|
.field-label {
|
|
font-size: var(--text-2xs);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.12em;
|
|
}
|
|
|
|
.form-input {
|
|
min-height: 2.25rem;
|
|
width: 100%;
|
|
padding: 0 var(--space-2);
|
|
color: var(--ink);
|
|
font-family: var(--font-readout);
|
|
font-size: var(--text-xs);
|
|
background: var(--ground);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
box-shadow: inset 0 1px var(--lowlight);
|
|
caret-color: var(--accent);
|
|
transition: border-color 150ms var(--ease-out);
|
|
}
|
|
|
|
.form-input:hover {
|
|
border-color: var(--line-strong);
|
|
}
|
|
|
|
.form-input:focus {
|
|
border-color: var(--accent);
|
|
outline: none;
|
|
}
|
|
|
|
.form-input:disabled {
|
|
color: var(--ink-faint);
|
|
border-color: var(--line);
|
|
}
|
|
|
|
select.form-input {
|
|
appearance: none;
|
|
}
|
|
|
|
.form-input[data-invalid] {
|
|
border-color: var(--signal-fault);
|
|
}
|
|
|
|
/* one resolution's size band: label, floor, target, penalty on a line */
|
|
.band-line {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-2);
|
|
}
|
|
|
|
.band-line .field-label {
|
|
width: 3.5rem;
|
|
}
|
|
|
|
.band-num {
|
|
width: 4.5rem;
|
|
}
|