/* Admin webui styling.

   One small stylesheet, no framework. This is a single-admin tool that gets
   looked at during setup and rarely after, so it aims for legible and quick to
   scan rather than designed. Respects the OS light/dark preference. */

:root {
  --bg: #f6f7f9;
  --panel: #ffffff;
  --ink: #1b1d21;
  --muted: #6b7280;
  --line: #dfe3e8;
  --accent: #3b6ea5;
  --ok: #1f7a4d;
  --err: #b3261e;
  --filter: #8a5a00;
  --preference: #3b6ea5;
  /* Text laid over a solid --err fill. Light mode's red is dark enough to
     carry white; dark mode's is a pastel, which needs dark ink instead. */
  --on-err: #ffffff;

  /* Plex's brand gold, and a darker press state mixed from it. Fixed in both
     schemes: it identifies the service you are signing in to, so it is not
     ours to re-tint the way the palette above gets re-tinted. It is bright
     enough that only near-black text clears contrast on it — white would land
     around 2:1 — hence --on-plex. */
  --plex: #e5a00d;
  --plex-hover: #cc8e0b;
  --on-plex: #1a1205;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14161a;
    --panel: #1c1f24;
    --ink: #e6e8eb;
    --muted: #9aa1ab;
    --line: #2c3138;
    --accent: #7aa9dd;
    --ok: #6ed49b;
    --err: #f08a84;
    --filter: #e0b060;
    --preference: #7aa9dd;
    --on-err: #14161a;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
}

.wrap { max-width: 1040px; margin: 0 auto; padding: 24px 20px 64px; }
h1 { font-size: 22px; margin: 8px 0 20px; }
h2 { font-size: 15px; text-transform: uppercase; letter-spacing: .04em;
     color: var(--muted); margin: 28px 0 10px; }

/* Header ------------------------------------------------------------------ */

.topbar {
  display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
  padding: 12px 20px; background: var(--panel); border-bottom: 1px solid var(--line);
}
.brand { font-weight: 600; }
.topbar nav { display: flex; gap: 16px; flex-wrap: wrap; }
.topbar nav a { color: var(--muted); text-decoration: none; }
.topbar nav a:hover { color: var(--ink); }
.topbar nav a.on { color: var(--accent); font-weight: 600; }
.signout { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.who { color: var(--muted); }

/* Panels ------------------------------------------------------------------ */

.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 8px; padding: 20px;
}
.card.narrow { max-width: 460px; margin: 48px auto; }
.lede { color: var(--muted); margin-top: 0; }
.hint { color: var(--muted); font-size: 13px; margin: 4px 0 14px; }
.empty { color: var(--muted); padding: 24px 0; }
.muted { color: var(--muted); }
.right { text-align: right; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }
.hidden { display: none !important; }

/* A setting this UI shows but cannot change — currently just the Seerr host,
   which lives in the environment. Deliberately not a disabled <input>: that
   reads as "a field you may be able to edit", and this one is never editable
   here. */
.readonly-value {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 14px; margin: 0 0 8px; word-break: break-all;
}
.readonly-value.unset { font-family: inherit; color: var(--muted); font-style: italic; }

/* Sign-in ----------------------------------------------------------------- */

/* The one page with no header and nothing to scan, so it gets centred as a
   block rather than starting at the top of the viewport like every other page.
   min-height leaves room to sit slightly above centre — optically better than
   true centring for a short stack — and the top padding is what it falls back
   to on a short window, where centring would otherwise clip the logo. */
.signin-wrap {
  min-height: 100vh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 48px 20px 96px;
}

.signin-logo { display: block; margin-bottom: 40px; }
.signin-logo img {
  display: block; width: 200px; height: auto;
}

.signin-card {
  /* Overrides .card.narrow's own vertical margin: spacing on this page is set
     by the flex column above, not by the card. */
  margin: 0; width: 100%; padding: 32px 28px 28px; text-align: center;
}
.signin-card .lede { margin-bottom: 10px; }

/* The host you are about to hand a Plex token to. The most important line on
   the page after the button, so it is the one thing set in the ink colour at
   full weight while the sentence introducing it stays muted. */
.signin-host {
  font-size: 16px; font-weight: 600; margin: 0 0 28px;
  word-break: break-all; line-height: 1.4;
}

.signin-card button.plex { padding: 10px 22px; font-size: 15px; }
.signin-card .hint { margin: 0; }
.signin-card .status:empty { min-height: 0; margin-top: 0; }
.signin-card .status:not(:empty) { margin-top: 20px; }

/* Forms ------------------------------------------------------------------- */

label { display: block; font-weight: 600; margin-bottom: 4px; font-size: 14px; }
label.check { font-weight: 400; display: flex; align-items: center; gap: 8px; }

input[type=text], input[type=url], input[type=password], input[type=number], select,
textarea {
  width: 100%; padding: 7px 9px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--bg); color: var(--ink); font: inherit;
}
/* A pasted .p8 is one long unbroken base64 run; monospace and no wrapping keeps
   it from reflowing into something that looks like a different key. */
textarea { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px;
  resize: vertical; white-space: pre; overflow-x: auto; }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
input:disabled, select:disabled {
  color: var(--muted); background: var(--line); cursor: not-allowed;
}

button, .button {
  font: inherit; padding: 7px 14px; border-radius: 6px; border: 1px solid transparent;
  background: var(--accent); color: #fff; cursor: pointer; text-decoration: none;
  display: inline-block;
}
button:disabled { opacity: .5; cursor: not-allowed; }
button.secondary, .button.secondary {
  background: transparent; color: var(--ink); border-color: var(--line);
}
/* Solid red is the committing step of a destructive flow; the outlined variant
   is what offers it in the first place, so arming a change does not look
   heavier on the page than confirming it. */
button.destructive, .button.destructive {
  background: var(--err); color: var(--on-err);
}
button.destructive.secondary, .button.destructive.secondary {
  background: transparent; color: var(--err); border-color: var(--err);
}
button.plex, .button.plex {
  background: var(--plex); color: var(--on-plex); font-weight: 600;
}
button.plex:hover:not(:disabled), .button.plex:hover { background: var(--plex-hover); }

button.link {
  background: none; border: none; color: var(--accent); padding: 0 6px; cursor: pointer;
}
button.link.danger, .danger { color: var(--err); }
button.icon {
  background: transparent; border: 1px solid var(--line); color: var(--ink);
  padding: 2px 8px; line-height: 1.4;
}
.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin: 14px 0; }
form.inline { display: inline; }
.inline-result { font-size: 14px; }
.status { min-height: 1.4em; margin-top: 14px; }
.ok { color: var(--ok); }
.err { color: var(--err); }
.errors { color: var(--err); padding-left: 18px; }

/* Tables ------------------------------------------------------------------ */

table {
  width: 100%; border-collapse: collapse; background: var(--panel);
  border: 1px solid var(--line); border-radius: 8px; overflow: hidden;
}
th, td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--line); }
th { font-size: 13px; color: var(--muted); font-weight: 600; }
tbody tr:last-child td { border-bottom: none; }
tr.system td { background: color-mix(in srgb, var(--accent) 7%, transparent); }
tr.new-row td { background: color-mix(in srgb, var(--ok) 6%, transparent); }
td.release, td.detail { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; }
.filter { margin-bottom: 16px; display: flex; gap: 10px; align-items: center; }
.filter select { width: auto; }

.badge {
  display: inline-block; padding: 1px 7px; border-radius: 999px; font-size: 12px;
  background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent);
}

/* Says who a column is for, in the header, so the difference between the
   admin's own name for an action and the copy its button carries is visible
   without reading the note under the table. */
.th-hint {
  font-weight: 400; text-transform: none; letter-spacing: 0;
  opacity: .75; font-size: 12px; margin-left: 4px;
}
.th-hint::before { content: "· "; }

/* Notifications ----------------------------------------------------------- */

/* The master switch. Larger than the per-type toggles and set on its own line,
   because it governs them and because it is the one control on the page that
   commits an admin to sending their users' data somewhere else. */
.notify-master {
  display: flex; align-items: center; gap: 10px;
  font-weight: 600; margin: 0 0 12px; cursor: pointer;
}
.notify-master input { width: 17px; height: 17px; margin: 0; cursor: pointer; }

/* Not an error — nothing has gone wrong — but not something to skim past
   either. A left rule and the accent colour, not a red panel. */
.hint.warn {
  border-left: 3px solid var(--filter);
  padding: 2px 0 2px 10px;
  color: var(--ink);
}

.notify-types { list-style: none; padding: 0; margin: 0; }
.notify-types li {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 14px 0; border-bottom: 1px solid var(--line);
}
.notify-types li:last-child { border-bottom: none; }
.notify-types li:first-child { padding-top: 0; }
.notify-switch { padding-top: 2px; }
.notify-copy { flex: 1; min-width: 0; }
.notify-copy .hint { margin: 3px 0 8px; }

/* A rough stand-in for a lock-screen alert: the two lines, in the order and
   weighting iOS gives them, so the switch above is describing something the
   admin can picture. */
.notify-preview {
  display: inline-flex; flex-direction: column; gap: 1px;
  padding: 8px 12px; border-radius: 10px; max-width: 100%;
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}
.preview-title { font-size: 13px; font-weight: 600; }
.preview-subtitle { font-size: 13px; color: var(--muted); }

/* Rule builder ------------------------------------------------------------ */

.rule {
  border: 1px solid var(--line); border-left-width: 4px; border-radius: 6px;
  padding: 12px 14px; margin-bottom: 10px; background: var(--bg);
}
.rule.kind-filter { border-left-color: var(--filter); }
.rule.kind-preference { border-left-color: var(--preference); }
.rule-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.rule-head .kind { color: var(--muted); font-size: 12px; text-transform: uppercase; }
.rule-controls { margin-left: auto; display: flex; gap: 4px; }
.add-rule select { width: auto; min-width: 220px; }

/* The three sections answer three different questions, and the numbered step
   is what says so at a glance — a filter and a tie-breaker look alike as form
   controls, which is exactly the confusion this layout is here to remove. */
.builder-section { margin: 0 0 28px; }
.builder-section > h2 { display: flex; align-items: center; gap: 8px; }
.builder-section .step {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--line); color: var(--ink);
  font-size: 12px; font-weight: 700;
}
.builder-section.filters .step { background: color-mix(in srgb, var(--filter) 35%, transparent); }
.builder-section.choices .step { background: color-mix(in srgb, var(--ok) 30%, transparent); }
.builder-section.preferences .step { background: color-mix(in srgb, var(--preference) 30%, transparent); }
.builder-section .lede { font-size: 13px; margin-bottom: 12px; }

/* A choice is a condition plus an ordering, so it gets more room than a rule
   row and its own accent — it is neither a filter nor a tie-breaker. */
.choice {
  border: 1px solid var(--line); border-left: 4px solid var(--ok);
  border-radius: 6px; padding: 12px 14px; margin-bottom: 10px; background: var(--bg);
}
.choice-reading { font-size: 13px; }
.choice-grid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.choice-field .choice-label {
  display: block; font-size: 12px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted); margin-bottom: 6px;
}
.choice-field .checks { display: flex; flex-wrap: wrap; gap: 4px 14px; }
.choice-field label.check { font-size: 13px; }
.choice-field .hint { margin-bottom: 0; }
.choice-field input.dim { opacity: .55; }

/* The profile in plain English, at the top of the builder. */
.profile-summary {
  border: 1px solid var(--line); border-radius: 6px;
  background: color-mix(in srgb, var(--accent) 6%, transparent);
  padding: 12px 14px; margin-bottom: 24px;
}
.profile-summary dl { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; }
.profile-summary dt {
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); padding-top: 2px;
}
.profile-summary dd { margin: 0; }
.profile-summary .summary-choices { margin: 0; padding-left: 18px; }
.profile-summary .hint { margin-bottom: 0; }

.profile-reading .reading-part { display: block; }
.profile-reading .reading-label {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  opacity: .8;
}

/* Editor and preview side by side on a wide window, stacked on a narrow one:
   the preview is a second opinion on the form, so it wants to be visible at
   the same time as the controls it is judging. */
/* The one page that earns more than the standard column: it is two panels that
   have to be read against each other, and release titles are long. Scoped by
   :has() so nothing else widens, and a browser without it just keeps the
   ordinary width. */
.wrap:has(.profile-editor) { max-width: 1320px; }

.profile-editor { display: grid; gap: 20px; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.profile-editor > .card { align-self: start; }
@media (max-width: 1000px) {
  .profile-editor { grid-template-columns: minmax(0, 1fr); }
}
.preview-card { position: sticky; top: 16px; }
.preview-card h2 { margin-top: 0; }
.preview-controls { margin-bottom: 14px; display: flex; flex-wrap: wrap; gap: 6px 18px; align-items: center; }
.preview-controls .row { flex-basis: 100%; }

/* Release titles are long and the tag row is wide, so the table's min-content
   width can exceed the panel. Scroll it inside the panel rather than letting
   it push the page sideways. */
#preview { overflow-x: auto; }

table.preview { font-size: 13px; }
table.preview td.release { word-break: break-word; }
table.preview td { vertical-align: top; padding: 7px 8px; }
table.preview tr.winner td { background: color-mix(in srgb, var(--ok) 10%, transparent); }
table.preview tr.dropped td { opacity: .55; }
table.preview tr.dropped td.release { text-decoration: line-through; }
table.preview .tags { display: block; margin-top: 3px; }
table.preview .tag {
  display: inline-block; margin-right: 4px; padding: 0 5px; border-radius: 3px;
  background: var(--line); color: var(--muted); font-size: 11px;
}
table.preview .verdict { display: block; }
table.preview .dropped-by { color: var(--err); }

/* Libraries & Home ---------------------------------------------------------- */

.reorder-list { margin: 10px 0; }
.reorder-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  border: 1px solid var(--line); border-radius: 6px; padding: 10px 12px;
  margin-bottom: 8px; background: var(--bg);
}
.reorder-row.dragging { opacity: .4; }
.drag-handle { color: var(--muted); cursor: grab; line-height: 1; user-select: none; }
/* The name field grows to fill the row, which is what pushes the trailing
   controls (Save, the drag handle, the remove button) to the right without
   a margin-left:auto trick — there is nothing else left to grow into. */
.reorder-row input[type=text] { width: auto; flex: 1; min-width: 140px; }
.row-actions { margin-left: auto; display: flex; gap: 8px; }

/* A trash-can remove button, inline at the end of a row. Same box as any
   other .icon button; .danger tints it red, same as the builder's own
   remove buttons. The <form> wrapping it is display:contents so it takes no
   layout space of its own — the button reads as a direct row child. */
.reorder-row form.remove-form { display: contents; }
.reorder-row button.icon {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 4px 7px;
}

/* A shelf is two tight rows — Content+Title, then Style+"Hide release
   year"+Remove — rather than one grid of labelled fields; that's what the
   old layout's height was actually going to, and none of it is information
   the controls don't already carry themselves. */
.shelf-row { flex-direction: column; align-items: stretch; padding: 8px 12px; }
/* The drag handle plus the Move Up/Down buttons that offer the same
   reorder without needing native HTML5 drag-and-drop — see libraries._moved. */
.reorder-controls { align-self: flex-start; margin-bottom: 2px; display: flex; align-items: center; gap: 2px; }
.reorder-controls button.icon { padding: 2px 5px; }
/* .move-form is display:contents (see .remove-form below), so each button —
   its only child — reads as a direct child of .reorder-controls. */
.move-form { display: contents; }
.shelf-fields { display: flex; flex-direction: column; gap: 6px; flex: 1; min-width: 0; }
/* A form with no visible content of its own — every real field lives outside
   it via form="<id>". display:none, not visibility:hidden, so it costs zero
   height and no flex gap in the column above. */
.shelf-form-anchor { display: none; }
.shelf-row-1, .shelf-row-2 { display: flex; align-items: center; gap: 8px; }
.shelf-row-1 select[name=source] { flex: 2 1 200px; width: auto; min-width: 0; }
.shelf-row-1 input[name=title] { flex: 1 1 140px; width: auto; min-width: 0; }
.shelf-row-2 select[name=style] { flex: 0 0 auto; width: auto; }
.shelf-row-2 label.check { flex: 1; margin: 0; }
/* .remove-form is display:contents (see above), so the button — its only
   child — is what actually needs pushing to the row's end. */
.shelf-row-2 form.remove-form button { margin-left: auto; }
/* The second picker "Collection Items…" reveals, between Content+Title and
   Style+"Hide release year" — its own line since it isn't always there,
   rather than crowding either fixed row. */
.shelf-row-collections select { width: auto; min-width: 220px; max-width: 100%; }
@media (max-width: 640px) {
  .shelf-row-1, .shelf-row-2 { flex-wrap: wrap; }
  .shelf-row-collections select { width: 100%; }
}

/* The Carousel and Top Shelf render one row's fields directly — no drag
   handle, no list — so .shelf-box gives them the same boxed look a shelf
   row gets from .reorder-row, without actually being one. */
.shelf-box {
  border: 1px solid var(--line); border-radius: 6px; padding: 10px 12px;
  margin: 10px 0;
}
/* Same shape as .notify-master. Unlike a plain switch, both of the
   Carousel's toggles swap in the whole #carousel-section (see
   partials/carousel_switch.html) rather than just themselves — the enabled
   switch's own state decides whether the row below it renders at all. */
.carousel-toggle { display: flex; align-items: center; gap: 10px; font-weight: 600; margin: 10px 0; }
.carousel-toggle input { width: 17px; height: 17px; margin: 0; cursor: pointer; }
