  :root {
    --bg: #14181d; --panel: #1d232b; --panel2: #232b35; --border: #2e3947;
    --text: #dbe2ea; --muted: #8494a6;
    --plex: #e5a00d; --tautulli: #4fc3f7;
    --danger: #e05c5c; --ok: #58c17a;
  }
  * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
  body { margin: 0; background: var(--bg); color: var(--text);
         font: 14px/1.45 system-ui, -apple-system, "Segoe UI", sans-serif; }
  button { font: inherit; cursor: pointer; border-radius: 6px; border: 1px solid var(--border);
           background: var(--panel2); color: var(--text); padding: 7px 14px; }
  button:hover:not(:disabled) { border-color: var(--muted); }
  /* Android/touch browsers apply :active (not :hover) on tap — without this, removing
     the default tap-highlight above leaves buttons with no visible press feedback. */
  button:active:not(:disabled) { background: var(--border); }
  button.primary:active:not(:disabled),
  button.danger:active:not(:disabled) { filter: brightness(.9); }
  button:disabled { opacity: .45; cursor: not-allowed; }
  button.primary { background: var(--plex); border-color: var(--plex); color: #14181d; font-weight: 600; }
  button.danger { background: var(--danger); border-color: var(--danger); color: #fff; font-weight: 600; }
  button.ghost-danger { background: none; border-color: var(--danger); color: var(--danger); }
  button.ghost { background: none; }
  button.keep-on { background: none; border-color: var(--plex); color: var(--plex); font-weight: 600; }
  /* Kept, but not from a level this button can undo — see recKeepControl. */
  button.warn-outline { border-color: var(--warn, #d9a441); color: var(--warn, #d9a441); }
  select, input[type=text] { background: var(--panel2); color: var(--text); border: 1px solid var(--border);
    border-radius: 6px; padding: 7px 10px; }

  /* login */
  /* dvh accounts for Chrome/Android's collapsing address+nav bars; vh alone can
     leave the button clipped or the page jumping as those bars show/hide */
  #login { display: none; min-height: 100vh; min-height: 100dvh; align-items: center; justify-content: center; flex-direction: column; gap: 18px; }
  #login h1 { font-weight: 600; letter-spacing: 4px; font-size: 44px; margin: 0; }
  #login h1 span { color: var(--plex); }
  #login-error { color: var(--danger); min-height: 1.4em; }

  /* app frame */
  #app { display: none; }
  header { display: flex; align-items: center; gap: 16px; padding: 12px 20px;
           background: var(--panel); border-bottom: 1px solid var(--border);
           position: sticky; top: 0; z-index: 5; }
  header h1 { font-size: 17px; margin: 0; font-weight: 600; }
  header h1 span { color: var(--plex); }
  /* Both header controls ride in one right-aligned group, so the name stays
     put whether or not the bell is there to sit beside it. */
  #header-right { margin-left: auto; display: flex; align-items: center; gap: 2px; }
  /* alert bell — sits left of the name, and is quiet until it isn't */
  #bell { display: flex; align-items: center; gap: 6px;
          background: none; border: 1px solid transparent; padding: 5px 10px;
          color: var(--muted); font-size: 13px; }
  #bell:hover { color: var(--text); border-color: var(--border); background: var(--panel2); }
  #bell.has-unread { color: var(--plex); }
  #bell-wrap { position: relative; }
  /* Same panel as the profile menu, wider because these rows carry a subject
     line rather than a two-word label. */
  #bell-menu { display: none; position: absolute; right: 0; top: calc(100% + 6px);
               width: 340px; max-width: calc(100vw - 24px); background: var(--panel2);
               border: 1px solid var(--border); border-radius: 8px; padding: 4px;
               z-index: 20; box-shadow: 0 8px 24px rgba(0,0,0,.45); }
  #bell-menu.open { display: block; }
  #bell-menu .pm-head { color: var(--muted); font-size: 12px; padding: 7px 10px 9px; }
  #bell-list { max-height: 50vh; overflow-y: auto; }
  #bell-list .bell-empty { color: var(--muted); font-size: 13px; margin: 0;
                           padding: 4px 10px 12px; line-height: 1.5; }
  .bell-row { padding: 8px 10px; border-radius: 6px; }
  .bell-row + .bell-row { border-top: 1px solid var(--border); border-radius: 0; }
  .bell-subject { font-size: 13px; color: var(--text); }
  .bell-meta { font-size: 11px; color: var(--muted); padding-top: 2px; }
  .bell-row.bad .bell-subject { color: var(--bad, #e5736b); }
  /* A row with a stored message opens; one without stays the plain row it
     always was. Overrides `#bell-menu button` below (its bold, id-scoped
     rule otherwise wins on specificity) so an openable row still reads like
     the rest of the list — only the pointer and hover say it's a button. */
  #bell-menu button.bell-row { display: block; width: 100%; text-align: left;
                               background: none; border: 0; font: inherit;
                               font-weight: 400; color: inherit; cursor: pointer; }
  #bell-menu button.bell-row:hover { background: var(--panel2); color: inherit; }
  #bell-menu button { display: block; width: 100%; text-align: left; background: none;
                      border: none; color: var(--text); font-size: 13px; font-weight: 600;
                      padding: 8px 10px; border-radius: 6px; }
  #bell-menu button:hover { background: var(--panel); color: var(--plex); }
  #bell-menu .pm-sub { display: block; color: var(--muted); font-weight: 400; font-size: 12px;
                       padding-top: 2px; }
  #bell-menu .pm-sep { height: 1px; background: var(--border); margin: 6px 4px; }
  #bell #bell-count:empty { display: none; }
  #bell #bell-count { background: var(--plex); color: #14181d; font-size: 11px;
                      font-weight: 700; border-radius: 9px; padding: 1px 6px; line-height: 1.5; }
  /* profile menu (the owner's own panels live behind their name) */
  #profile { position: relative; }
  #whoami { display: flex; align-items: center; gap: 10px; color: var(--muted);
            background: none; border: 1px solid transparent; padding: 5px 10px; }
  #whoami:hover, #whoami[aria-expanded=true] { color: var(--text); border-color: var(--border);
            background: var(--panel2); }
  #whoami img { width: 28px; height: 28px; border-radius: 50%; }
  #whoami .chev { font-size: 13px; line-height: 1; color: var(--plex); transition: transform .12s; }
  #whoami[aria-expanded=true] .chev { transform: rotate(180deg); }
  .admin-badge { background: var(--plex); color: #14181d; font-size: 11px; font-weight: 700;
                 padding: 1px 7px; border-radius: 10px; }
  #profile-menu { display: none; position: absolute; right: 0; top: calc(100% + 6px);
    min-width: 270px; background: var(--panel2); border: 1px solid var(--border);
    border-radius: 10px; padding: 6px; z-index: 20;
    box-shadow: 0 10px 30px rgba(0,0,0,.5); }
  #profile-menu.open { display: block; }
  #profile-menu .pm-head { color: var(--muted); font-size: 12px; padding: 7px 10px 9px; }
  #profile-menu .pm-head b { color: var(--text); }
  #profile-menu button { display: block; width: 100%; text-align: left; background: none;
    border: none; border-radius: 6px; padding: 8px 10px; color: var(--text); font-weight: 600; }
  #profile-menu button:hover { background: var(--panel); color: var(--plex); }
  #profile-menu .pm-sub { display: block; color: var(--muted); font-weight: 400; font-size: 12px;
    margin-top: 1px; }
  #profile-menu button:hover .pm-sub { color: var(--muted); }
  #profile-menu .pm-sep { height: 1px; background: var(--border); margin: 6px 4px; }

  #imp-banner { display: none; align-items: center; gap: 12px; padding: 9px 20px;
    background: #3a2e12; border-bottom: 1px solid var(--plex); color: #f2d79a; }
  #imp-banner b { color: var(--plex); }
  #imp-banner button { margin-left: auto; background: var(--plex); border-color: var(--plex);
    color: #14181d; font-weight: 600; padding: 5px 12px; }

  main { max-width: 1100px; margin: 0 auto; padding: 18px 20px 120px; }
  .toolbar { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
  .toolbar label { color: var(--muted); }
  #user-picker-wrap { align-items: center; gap: 8px; }
  #filter, #radarr-alert-filter, #sonarr-alert-filter { flex: 1; min-width: 200px; }
  #btn-help { border-radius: 50%; width: 32px; height: 32px; padding: 0; font-weight: 700;
              color: var(--muted); }

  /* A job and its two buttons, boxed together: which preview belongs to which
     run is the thing people were guessing at when they were four loose buttons. */
  .job { display: inline-flex; align-items: center; gap: 8px; padding: 5px 8px 5px 11px;
         border: 1px solid var(--border); border-radius: 8px; }
  .job-name { color: var(--muted); font-size: 12px; white-space: nowrap; }
  .hint { border-bottom: 1px dotted currentColor; cursor: help; }
  .c-keep button { white-space: nowrap; }

  /* tabs */
  .tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 14px; }
  .tabs .tab { background: none; border: none; border-bottom: 2px solid transparent; border-radius: 0;
               padding: 9px 16px; color: var(--muted); font-weight: 600; white-space: nowrap;
               position: relative; }
  .tabs .tab:hover { color: var(--text); }
  .tabs .tab.active { color: var(--text); border-bottom-color: var(--plex); }
  /* count of things waiting on the admin, on the tab itself */
  .tabs .tab-badge { display: inline-block; min-width: 18px; margin-left: 7px;
    padding: 0 6px; border-radius: 9px; background: var(--plex); color: #14181d;
    font: 700 11px/18px system-ui, sans-serif; text-align: center; vertical-align: middle; }
  .tabs .tab[data-tip]:hover::after, .tabs .tab[data-tip]:focus-visible::after {
    content: attr(data-tip);
    position: absolute; top: calc(100% + 8px); left: 0; width: 280px;
    background: var(--panel2); border: 1px solid var(--border); border-radius: 8px;
    padding: 9px 12px; color: var(--text); font-weight: 400; font-size: 12.5px;
    line-height: 1.45; text-align: left; white-space: normal;
    box-shadow: 0 6px 20px rgba(0,0,0,.45); z-index: 15; pointer-events: none;
  }
  .tabs > .tab[data-tip]:nth-last-child(-n+2):hover::after,
  .tabs > .tab[data-tip]:nth-last-child(-n+2):focus-visible::after {
    left: auto; right: 0;
  }

  /* ── grouped tabs ─────────────────────────────────────────────────
     Thirteen labels in one strip ran off the right edge of the page. The
     related ones now sit behind three parents, which fits, and the menu
     borrows the profile menu's panel treatment so the two read as the same
     kind of thing. Children keep their .tab class and ids: switchTab(),
     applyServiceChrome() and the removal badge all still find them. */
  .tabgroup { position: relative; display: flex; }
  .tab-group { background: none; border: none; border-bottom: 2px solid transparent;
               border-radius: 0; padding: 9px 16px; color: var(--muted);
               font-weight: 600; white-space: nowrap; }
  .tab-group:hover, .tabgroup.open .tab-group { color: var(--text); }
  .tab-group.active { color: var(--text); border-bottom-color: var(--plex); }
  .tab-group::after { content: " \25be"; color: var(--muted); margin-left: 6px;
                      font-size: 11px; display: inline-block; transition: transform .12s; }
  .tabgroup.open .tab-group::after { transform: rotate(180deg); }
  /* the parent's own tooltip would sit on top of its open menu */
  .tabgroup.open .tab-group[data-tip]:hover::after { content: " \25be"; }

  .tabmenu { display: none; position: absolute; left: 0; top: calc(100% + 4px);
    min-width: 290px; background: var(--panel2); border: 1px solid var(--border);
    border-radius: 10px; padding: 6px; z-index: 20;
    box-shadow: 0 10px 30px rgba(0,0,0,.5); }
  .tabgroup.open .tabmenu { display: block; }
  /* label and badge on one line, the one-line description under them */
  .tabmenu .tab { display: flex; flex-wrap: wrap; align-items: center; width: 100%;
    text-align: left; border: none; border-radius: 6px; padding: 8px 10px;
    color: var(--text); }
  .tabmenu .tab:hover { background: var(--panel); color: var(--plex); }
  .tabmenu .tab.active { background: var(--border); color: var(--text); }
  .tabmenu .tm-sub { flex: 1 0 100%; color: var(--muted); font-weight: 400;
    font-size: 12px; margin-top: 1px; }
  .tabmenu .tab .tab-badge { margin-left: auto; }
  /* the strip's hover paragraph is unreadable inside a menu — the .tm-sub
     line under each label is what replaces it here */
  .tabmenu .tab[data-tip]:hover::after,
  .tabmenu .tab[data-tip]:focus-visible::after { content: none; }

  /* Settings is reached from the profile menu, where all six of its panels
     already are — it only appears in the strip to say you are on it. */
  #tab-btn-schedule { display: none; }
  .tabs #tab-btn-schedule.active { display: block; }

  /* per-tab description line */
  .tab-blurb { color: var(--muted); margin: 2px 0 12px; max-width: 78ch; }

  /* auto-purge schedule tab */
  /* Every section but the opening one starts hidden — switchTab() takes over
     from the first click, but nothing calls it at boot, so Watch History is the
     only tab that may default to visible. */
  #tab-schedule { display: none; }
  #tab-stale { display: none; }
  #tab-dropped { display: none; }
  #tab-radarr, #tab-sonarr { display: none; }
  #tab-queue, #tab-digest { display: none; }
  #tab-buffering { display: none; }
  #tab-leaving { display: none; }
  #tab-activity { display: none; }
  #tab-nowplaying { display: none; }
  #tab-downloads { display: none; }
  #tab-docs { display: none; }

  /* Docs tab — the two diagram pages, served from /docs and gated on the real
     admin, so they can't be linked from /static. They bring their own palette
     and their own scripts; an iframe keeps both out of this document. Tall
     enough that a diagram is readable in place, with a full-width link for
     when it isn't. */
  .docs-frame { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--bg); }
  .docs-frame iframe { display: block; width: 100%; height: calc(100vh - 210px); min-height: 520px; border: 0; }

  /* buffering tab: rows open onto the whole payload */
  .buffer-row { cursor: pointer; }
  #buffer-pane th:first-child, #buffer-pane td:first-child { width: 1%; padding-right: 0; }
  #buffer-pane input[type=checkbox] { cursor: pointer; vertical-align: middle; }
  .buffer-detail { display: flex; flex-wrap: wrap; gap: 4px 18px; margin-bottom: 10px; }
  .buffer-detail span { white-space: nowrap; }
  .buffer-detail b { color: var(--text); font-weight: 600; margin-right: 4px; }
  .buffer-raw-head { color: var(--muted); font-size: 12px; text-transform: uppercase;
                     letter-spacing: .04em; margin-bottom: 4px; }
  .buffer-raw { margin: 0; padding: 8px 10px; background: rgba(0,0,0,.25); border-radius: 4px;
                overflow-x: auto; font-size: 12px; line-height: 1.5; max-height: 320px; }
  /* what all the warnings add up to, above the log that recorded them */
  .diag { border: 1px solid var(--border); border-radius: 8px; background: var(--panel);
          margin: 0 0 14px; }
  .diag-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
               padding: 11px 14px; cursor: pointer; }
  .diag-head h3 { margin: 0; font-size: 14px; }
  .diag-lede { color: var(--muted); font-size: 13px; }
  .diag-toggle { margin-left: auto; color: var(--muted); font-size: 13px; white-space: nowrap; }
  .diag-body { padding: 0 14px 14px; display: grid; gap: 18px;
               grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); }
  .diag-col h4 { margin: 0 0 2px; color: var(--muted); font-size: 12px; font-weight: 600;
                 text-transform: uppercase; letter-spacing: .04em; }
  .diag-col .diag-why { color: var(--muted); font-size: 12px; margin: 0 0 8px; }
  .diag-row { padding: 8px 0; border-top: 1px solid var(--border); }
  .diag-row.clickable { cursor: pointer; }
  .diag-row.clickable:hover .diag-name { color: var(--plex); }
  .diag-name { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
  .diag-name b { font-weight: 600; }
  .diag-counts { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
  .diag-note { color: var(--muted); font-size: 12px; margin-top: 3px; }
  .verdict { display: inline-block; font-size: 10.5px; font-weight: 700; letter-spacing: .4px;
             padding: 1px 7px; border-radius: 10px; vertical-align: 1px;
             border: 1px solid var(--border); color: var(--muted); white-space: nowrap; }
  .verdict.bad  { border-color: var(--danger); color: var(--danger); }
  .verdict.mixed { border-color: var(--plex); color: var(--plex); }
  .verdict.wolf { border-color: var(--tautulli); color: var(--tautulli); }
  /* what the user said about the stutter, under the payload that recorded it */
  .survey-block { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
  .survey-head { color: var(--muted); font-size: 12px; text-transform: uppercase;
                 letter-spacing: .04em; margin-bottom: 8px; }
  .survey-qa { display: flex; flex-direction: column; gap: 8px; }
  .survey-qa .q { color: var(--muted); font-size: 12px; }
  .survey-qa .a { color: var(--text); }
  .outcome-chip { display: inline-block; font-size: 10.5px; font-weight: 700; letter-spacing: .4px;
                  padding: 1px 7px; border-radius: 10px; vertical-align: 1px;
                  border: 1px solid var(--border); color: var(--muted); }
  .outcome-chip.ok  { border-color: var(--ok); color: var(--ok); }
  .outcome-chip.bad { border-color: var(--danger, #d9534f); color: var(--danger, #d9534f); }
  .survey-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
  .survey-note { color: var(--muted); font-size: 13px; }
  /* the "record what they told you" form */
  .ra-q { padding: 12px 0; border-top: 1px solid var(--border); }
  .ra-q:first-child { border-top: none; }
  .ra-prompt, .ra-q > label { display: block; font-weight: 600; margin-bottom: 2px; }
  .ra-req { font-size: 10.5px; font-weight: 700; letter-spacing: .4px; margin-left: 7px;
            padding: 1px 7px; border-radius: 10px; border: 1px solid var(--border);
            color: var(--muted); }
  .survey-help { color: var(--muted); font-size: 13px; margin-bottom: 6px; }
  .ra-opt { display: flex; align-items: flex-start; gap: 7px; padding: 3px 0;
            font-size: 14px; cursor: pointer; }
  .ra-opt input { margin-top: 3px; flex: none; }
  .ra-q input[type=text] { width: 100%; }
  .survey-orphans { margin-top: 18px; }
  .arr-bar { display: flex; align-items: center; gap: 14px; margin-top: 12px; }
  .arr-bar .counts { color: var(--muted); }
  .arr-bar .counts b { color: var(--text); }
  .req-by { cursor: help; border-bottom: 1px dotted var(--muted); }
  td.c-title a, .c-title a, .title-link { color: inherit; text-decoration: none;
                 border-bottom: 1px dotted var(--muted); }
  td.c-title a:hover, .c-title a:hover, .title-link:hover {
                 color: var(--plex); border-bottom-color: var(--plex); }
  /* row actions: a compact right-aligned stack, so labels never wrap and the
     buttons line up down the column */
  td.c-reqdel { text-align: right; white-space: nowrap; }
  td.c-reqdel .acts { display: inline-flex; flex-direction: column; align-items: stretch; gap: 5px; }
  td.c-reqdel .acts button { font-size: 12px; padding: 5px 10px; white-space: nowrap; }

  /* Library / Notifications sub-tabs inside the Movies & Television tabs */
  .subtabs { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; }
  .subtabs .subtab { background: none; border: 1px solid var(--border); border-radius: 999px;
                     padding: 5px 16px; color: var(--muted); font-weight: 600; font-size: 13px; }
  .subtabs .subtab:hover { color: var(--text); }
  .subtabs .subtab.active { color: #14181d; background: var(--plex); border-color: var(--plex); }
  .contact-card { background: var(--panel2); border: 1px solid var(--border); border-radius: 8px;
                  padding: 12px 16px; margin-bottom: 12px; display: flex; gap: 10px;
                  align-items: center; flex-wrap: wrap; }
  .contact-card > span { flex: 1; min-width: 220px; }
  .mine-badge { display: inline-block; font-size: 11px; padding: 1px 8px; border-radius: 10px;
                background: var(--plex); color: #14181d; font-weight: 700; margin-right: 6px; }
  .notified-note { color: var(--muted); font-size: 12px; white-space: nowrap; }
  .field-label { display: block; color: var(--muted); font-size: 13px; margin: 12px 0 4px; }
  .modal select { width: 100%; margin-top: 6px; }
  .carrier-note { display: block; font-size: 13px; margin: 8px 0 0; line-height: 1.45; }
  .carrier-note.ok { color: var(--ok); }
  .carrier-note.warn { color: var(--plex); }
  .carrier-note.dead { color: var(--danger); }
  .sms-preview { display: block; margin: 8px 0 0; font-size: 13px; color: var(--muted); }
  .sms-preview b { color: var(--tautulli); font-family: ui-monospace, monospace; }
  .sched-card { background: var(--panel2); border: 1px solid var(--border); border-radius: 8px;
                padding: 18px 20px; max-width: 640px; }
  .sched-card h2 { margin: 0 0 8px; font-size: 16px; }
  .freq-options { display: flex; flex-direction: column; gap: 8px; margin: 14px 0; }
  .freq-options label { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px;
                        border: 1px solid var(--border); border-radius: 8px; cursor: pointer; }
  .freq-options label:hover { border-color: var(--muted); }
  .freq-options label.checked { border-color: var(--plex); background: rgba(229,160,13,.06); }
  .freq-options input { margin-top: 3px; }
  .freq-options .desc { display: block; color: var(--muted); font-size: 13px; }
  .sched-status { color: var(--muted); margin: 4px 0 12px; }
  .sched-status b { color: var(--text); }
  .sched-status .bad { color: var(--danger); }
  #sched-msg { margin-left: 10px; }
  #sched-msg.ok { color: var(--ok); }
  #sched-msg.bad { color: var(--danger); }
  #sim-card { margin-top: 14px; }

  /* Queue tab call-out: where to go to ask for something that isn't there yet */
  .promo-card { display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
                background: rgba(229,160,13,.07); border: 1px solid var(--plex);
                border-radius: 8px; padding: 13px 16px; margin-bottom: 12px; }
  .promo-card > span { flex: 1; min-width: 240px; }
  .promo-card b { color: var(--plex); }
  .promo-btn { background: var(--plex); color: #14181d; font-weight: 700; text-decoration: none;
               padding: 8px 16px; border-radius: 6px; white-space: nowrap; }
  .promo-btn:hover { filter: brightness(1.12); }

  /* Settings tab: Notifications + Services panels (admin) */
  .set-card { background: var(--panel2); border: 1px solid var(--border); border-radius: 8px;
              padding: 18px 20px; max-width: 760px; margin-bottom: 14px; }
  .set-card h2 { margin: 0 0 6px; font-size: 16px; }
  .set-card .blurb { color: var(--muted); margin: 0 0 12px; }
  .set-row { display: flex; gap: 10px; align-items: flex-start; padding: 10px 0;
             border-top: 1px solid var(--border); }
  .set-row:first-of-type { border-top: none; }
  .set-row input[type=checkbox] { margin-top: 3px; flex: none; }
  .set-row .txt { flex: 1; min-width: 0; }
  /* direct child only — a <b> used for emphasis inside .desc must stay inline */
  .set-row .txt > b { display: block; font-weight: 600; }
  .set-row .txt .desc { color: var(--muted); font-size: 13px; }
  /* Per-event test button. Quiet by default — forty of these down the page
     should read as a column of affordances, not forty calls to action — and
     it holds its width across "test"/"sending…"/"sent (muted)" so the rows
     don't shuffle sideways while one is in flight. */
  .set-row .ev-test { flex: none; align-self: center; min-width: 92px;
                      padding: 4px 8px; font-size: 12px; border-radius: 6px;
                      border: 1px solid var(--border); background: transparent;
                      color: var(--muted); cursor: pointer; }
  .set-row .ev-test:hover:not(:disabled) { color: var(--text); border-color: var(--muted); }
  .set-row .ev-test:disabled { cursor: default; opacity: .8; }
  .set-row .ev-test.ok { color: var(--ok); border-color: currentColor; }
  .set-row .ev-test.bad { color: var(--danger); border-color: currentColor; }
  .lk-controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
                 margin-top: 8px; font-size: 13px; }
  .lk-controls label { display: inline-flex; align-items: center; gap: 5px; color: var(--muted); }
  .lk-controls input[type=checkbox] { margin-top: 0; }
  .lk-controls input[type=email] { min-width: 190px; }
  .via-chip { display: inline-block; font-size: 10.5px; font-weight: 700; letter-spacing: .4px;
              padding: 1px 7px; border-radius: 10px; margin-left: 6px; vertical-align: 1px;
              border: 1px solid var(--seerr, var(--border)); color: var(--muted); }
  .actor-chip { display: inline-block; font-size: 10.5px; font-weight: 700; letter-spacing: .4px;
                padding: 1px 7px; border-radius: 10px; margin-left: 7px; vertical-align: 1px;
                border: 1px solid var(--border); color: var(--muted); }
  .actor-chip.user { border-color: var(--tautulli); color: var(--tautulli); }
  .actor-chip.system { border-color: var(--ok); color: var(--ok); }
  /* The heading above a run of switchboard groups — says who the alerts under
     it are about, which is the whole reason the groups are split. */
  .set-section-head { margin: 22px 0 10px; border-top: 1px solid var(--border); padding-top: 16px; }
  .set-section-head:first-child { margin-top: 4px; border-top: none; padding-top: 0; }
  .set-section-head h2 { margin: 0 0 4px; font-size: 15px; color: var(--plex); }
  .set-section-head p { margin: 0; color: var(--muted); font-size: 13px; max-width: 68ch; }
  .set-group-head { display: flex; align-items: baseline; gap: 10px; margin: 0 0 4px; }
  .set-group-head h3 { margin: 0; font-size: 14px; }
  .set-group-head button { background: none; border: none; color: var(--muted);
                           font-size: 12.5px; padding: 2px 4px; text-decoration: underline; }
  .set-group-head button:hover { color: var(--plex); }
  .set-field { margin: 0 0 12px; }
  .set-field label { display: block; color: var(--muted); font-size: 13px; margin-bottom: 4px; }
  .set-field input[type=text], .set-field input[type=password], .set-field input[type=number] {
    width: 100%; background: var(--panel); color: var(--text); border: 1px solid var(--border);
    border-radius: 6px; padding: 7px 10px; font: inherit; }
  .set-field .help { display: block; color: var(--muted); font-size: 12.5px; margin-top: 4px; }
  .set-field .env-note { color: var(--plex); font-size: 11px; font-weight: 700; margin-left: 6px; }
  .set-field.check { display: flex; gap: 9px; align-items: flex-start; }
  .set-field.check input { margin-top: 3px; }
  .set-field.check label { margin: 0; color: var(--text); font-size: 14px; }
  .set-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 6px; }
  .set-msg { font-size: 13px; }
  .set-msg.ok { color: var(--ok); }
  .set-msg.bad { color: var(--danger); }
  /* The one-time reveal of an API token. Boxed and set apart from the form that
     made it, because it is the only thing on the page that cannot be fetched
     again — somebody who scrolls past it has to revoke and start over. */
  .tok-row { align-items: center; }
  .tok-reveal { border: 1px solid var(--plex); border-radius: 8px; padding: 12px 14px;
    margin-top: 14px; background: var(--panel2); }
  .tok-reveal h3 { margin: 0 0 4px; font-size: 15px; }
  .tok-reveal .desc { color: var(--muted); font-size: 13px; margin: 8px 0 6px; }
  .tok-secret, .tok-config { background: var(--bg); border: 1px solid var(--border);
    border-radius: 6px; padding: 9px 11px; font-size: 13px; margin: 0;
    overflow-x: auto; white-space: pre; }
  .tok-secret { user-select: all; }
  #tok-label, #tok-scope { background: var(--panel2); color: var(--text);
    border: 1px solid var(--border); border-radius: 6px; padding: 6px 9px; font-size: 14px; }
  .set-banner { border-radius: 8px; padding: 11px 15px; margin-bottom: 14px; max-width: 760px; }
  .set-banner.warn { background: rgba(224,92,92,.09); border: 1px solid var(--danger); }
  .set-banner.info { background: var(--panel2); border: 1px solid var(--border); color: var(--muted); }
  .set-banner a { color: var(--plex); }

  /* Who has assistant access (Settings → MCP). A switched-on person's tokens
     and connections are indented under their row, so the list reads as one
     person per block rather than as two lists that happen to be adjacent. */
  .mcp-access-row { align-items: center; }
  .mcp-access-switch { display: flex; align-items: center; gap: 7px; font-size: 13px;
                       color: var(--muted); white-space: nowrap; cursor: pointer; }
  .mcp-holding { padding-left: 22px; border-left: 2px solid var(--border); margin-left: 4px; }
  .mcp-holding .txt > b { font-weight: 500; font-size: 13.5px; }

  /* MCP tools reference (Settings → MCP) */
  .mcp-tool-row .txt > b { display: flex; align-items: center; gap: 8px; font-family: ui-monospace, monospace;
                           font-size: 13.5px; font-weight: 600; }
  .mcp-kind { display: inline-block; font-size: 10.5px; font-weight: 700; letter-spacing: .4px;
              padding: 1px 7px; border-radius: 10px; border: 1px solid var(--border); color: var(--muted);
              font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
  .mcp-kind-read { border-color: var(--ok); color: var(--ok); }
  .mcp-kind-write { border-color: var(--plex); color: var(--plex); }
  .mcp-kind-remove { border-color: var(--danger); color: var(--danger); }
  .mcp-scope { color: var(--muted); font-size: 12.5px; white-space: nowrap; padding-top: 2px; }
  .mcp-note { color: var(--plex); }
  .sim-src { font-weight: 600; font-size: 13px; }
  .sim-src.t { color: var(--tautulli); }
  .sim-src.p { color: var(--plex); }
  .sim-src.a { color: var(--ok); }

  .counts-line { color: var(--muted); margin: 0 2px 10px; }
  .counts-line b { color: var(--text); }
  .dot { margin: 0 6px; }

  /* Reconcile sub-tab: the class breakdown above the table. The counts are the
     finding and the table is only the detail behind them, so they get the
     weight — and the unattributed one is the one worth looking at twice. */
  .rec-chip { display: inline-block; margin: 0 8px 8px 0; padding: 6px 12px;
              border: 1px solid var(--border); border-radius: 8px;
              background: var(--panel2); cursor: help; }
  .rec-chip b { color: var(--text); margin-right: 4px; }
  .rec-chip .muted { margin-left: 6px; }
  .rec-chip-warn { border-color: var(--danger); }
  .rec-chip-warn b { color: var(--danger); }

  .pane { background: var(--panel2); border: 1px solid var(--border); border-radius: 8px; }
  .src-error { color: var(--danger); padding: 12px 16px; border-bottom: 1px solid var(--border); }
  .empty { color: var(--muted); padding: 24px 16px; text-align: center; }
  /* watch-time chart */
  .chart-host { position: relative; margin-top: 10px; }
  .chart-svg { width: 100%; height: 260px; display: block; overflow: visible; }
  .chart-grid { stroke: var(--border); stroke-width: 1; }
  .chart-ylab { fill: var(--muted); font-size: 11px; text-anchor: end; }
  .chart-xlab { fill: var(--muted); font-size: 11px; text-anchor: middle; }
  .chart-line { stroke-width: 2; stroke-linejoin: round; stroke-linecap: round;
                vector-effect: non-scaling-stroke; }
  .chart-dot  { vector-effect: non-scaling-stroke; }
  .chart-bar  { opacity: .85; }
  .chart-guide { stroke: var(--muted); stroke-width: 1; stroke-dasharray: 3 3;
                 vector-effect: non-scaling-stroke; pointer-events: none; }
  .chart-tip { position: absolute; pointer-events: none; z-index: 5;
               background: var(--panel2); border: 1px solid var(--border);
               border-radius: 6px; padding: 7px 9px; font-size: 12.5px;
               box-shadow: 0 6px 18px rgba(0,0,0,.35); white-space: nowrap; }
  .chart-tip-head { color: var(--muted); margin-bottom: 4px; }
  .chart-tip-row { display: flex; align-items: center; gap: 7px; }
  .chart-tip-row b { margin-left: auto; padding-left: 12px; }
  .chart-tip-row i, .chart-legend-item i { width: 9px; height: 9px; border-radius: 2px;
                                           display: inline-block; flex: none; }
  .chart-legend { display: flex; flex-wrap: wrap; gap: 8px 14px; margin-top: 8px; }
  .chart-legend-item { display: flex; align-items: center; gap: 7px; background: none;
                       border: none; padding: 2px 0; color: var(--text); font-size: 13px; }
  .chart-legend-item.off { color: var(--muted); }
  .chart-legend-item.off i { opacity: .3; }
  .chart-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
                    margin-top: 4px; }
  .chart-note { color: var(--muted); font-size: 12.5px; }
  .chart-warn { color: var(--plex); font-size: 12.5px; }
  /* segmented day/week/month/year and line/bar switches */
  .seg { display: inline-flex; border: 1px solid var(--border); border-radius: 6px;
         overflow: hidden; }
  .seg button { background: none; border: none; border-radius: 0; padding: 5px 11px;
                color: var(--muted); font-size: 13px; }
  .seg button + button { border-left: 1px solid var(--border); }
  .seg button.on { background: var(--panel2); color: var(--text); font-weight: 600; }
  /* now playing: one row per live stream, with progress shown rather than stated */
  .np-title b { font-weight: 600; }
  .np-sub { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
  .np-bar { position: relative; height: 5px; border-radius: 3px; background: var(--border);
            margin-top: 6px; overflow: hidden; min-width: 120px; }
  .np-bar i { position: absolute; inset: 0 auto 0 0; background: var(--plex); display: block; }
  .np-bar.paused i { background: var(--muted); }
  .np-state { font-size: 10.5px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase;
              color: var(--muted); }
  .np-state.playing { color: var(--ok); }
  .np-idle { color: var(--muted); padding: 28px 16px; text-align: center; }
  /* a transcoder losing ground against playback — buffering in the making */
  .mtype.bad { border-color: var(--danger); color: var(--danger); }
  /* downloads: one row per in-flight fetch, progress shown as a bar rather than
     a percentage buried in the row */
  .dl-bar { position: relative; height: 5px; border-radius: 3px; background: var(--border);
            margin-top: 6px; overflow: hidden; min-width: 100px; }
  .dl-bar i { position: absolute; inset: 0 auto 0 0; background: var(--plex); display: block; }
  .dl-sub { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
  .dl-errs { color: var(--danger); font-size: 12px; margin-top: 2px; }
  /* activity log: rows open onto exactly what was written */
  .audit-row { cursor: pointer; }
  .audit-row td.when { white-space: nowrap; color: var(--muted); }
  .audit-row td.who { white-space: nowrap; }
  .audit-sum { color: var(--text); }
  .audit-fail { color: var(--danger); font-weight: 600; margin-left: 8px; }
  .audit-raw { margin: 0; padding: 8px 10px; background: rgba(0,0,0,.25); border-radius: 4px;
               overflow-x: auto; font-size: 12px; line-height: 1.5; max-height: 320px; }
  /* alerts log: one card per push, not a table row — the body is prose, not a
     handful of short fields */
  .nlog-row { padding: 12px 14px; border-bottom: 1px solid var(--border); }
  .nlog-row:last-child { border-bottom: none; }
  .nlog-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 4px; }
  .nlog-body { color: var(--text); white-space: pre-line; line-height: 1.5; }
  .nlog-link { display: inline-block; margin-top: 6px; color: var(--plex); font-size: 13px; }
  .nlog-state-muted, .nlog-state-not_configured { color: var(--muted); border-style: dashed; }
  .nlog-state-failed { color: var(--danger); border-color: var(--danger); }
  .nlog-high { color: var(--danger); border-color: var(--danger); }
  .nlog-targets { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
  .nlog-target { display: flex; align-items: center; gap: 8px; font-size: 13px;
                 background: var(--panel2); border: 1px solid var(--border); border-radius: 8px;
                 padding: 4px 10px; }

  table { width: 100%; border-collapse: collapse; }
  th, td { text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--border); }
  th { color: var(--muted); font-weight: 600; user-select: none; white-space: nowrap;
       position: sticky; top: 53px; background: var(--panel2); z-index: 2; }
  th.sortable { cursor: pointer; }
  tr:hover td { background: rgba(255,255,255,.025); }
  td.chev { width: 26px; padding-right: 0; color: var(--muted); cursor: pointer; }
  /* keep values like "21.3 GB", dates, and "7/7 episodes" on one line —
     let the Title column absorb the wrapping instead */
  td.c-date, td.c-size, td.c-status, td.c-req { white-space: nowrap; }
  .mtype { display: inline-block; font-size: 11px; padding: 1px 8px; border-radius: 10px;
           border: 1px solid var(--border); color: var(--muted); text-transform: capitalize; }
  .req-status { display: inline-block; font-size: 11px; padding: 1px 8px; border-radius: 10px;
                border: 1px solid var(--border); color: var(--muted); font-weight: 600; }
  .req-status.available { color: var(--ok); border-color: var(--ok); }
  .req-status.partial { color: var(--ok); }
  .req-status.processing { color: var(--tautulli); border-color: var(--tautulli); }
  .req-status.pending { color: var(--plex); border-color: var(--plex); }
  .req-status.declined { color: var(--danger); border-color: var(--danger); }
  .req-status.removed { color: var(--muted); border-style: dashed; cursor: help; }
  .req-status.dropped { color: var(--plex); border-color: var(--plex); border-style: dashed; cursor: help; }
  /* "On server" and "Request" can both read "Available" for a fulfilled
     request — they're two different systems' opinions of the same title, and
     without some visual split that reads as an unexplained duplicate. */
  .c-status .req-status::before { content: "▮ "; opacity: .55; }
  .c-reqstatus .req-status::before { content: "✉ "; opacity: .55; }
  /* the per-row "why" button, and the per-season verdicts it opens */
  button.why { border: 1px solid var(--border); background: none; color: var(--muted);
               border-radius: 50%; width: 18px; height: 18px; line-height: 1;
               padding: 0; font-size: 11px; cursor: help; }
  button.why:hover { color: var(--fg); border-color: var(--fg); }
  ul.fates { list-style: none; padding: 0; margin: 12px 0; }
  ul.fates li { padding: 8px 0; border-top: 1px solid var(--border); font-size: 13px; }
  .fate { display: inline-block; min-width: 88px; font-size: 11px; font-weight: 600;
          text-transform: uppercase; letter-spacing: .04em; }
  /* one per season, coloured by what happens to that season alone */
  .seasonchip { cursor: help; border-bottom: 2px solid transparent; }
  .seasonchip.leaving { border-bottom-color: var(--danger); }
  .seasonchip.waiting { border-bottom-color: var(--plex); }
  .seasonchip.clock   { border-bottom-color: var(--tautulli); }
  .seasonchip.safe    { border-bottom-color: var(--ok); }
  .claims { font-size: 12px; margin-top: 4px; }
  .claims div { margin-top: 2px; }
  .claim { font-weight: 600; }
  .claim.owns  { color: var(--fg); }
  .claim.views { color: var(--tautulli); }
  .scope { font-size: 10px; text-transform: uppercase; letter-spacing: .04em;
           border: 1px solid var(--border); border-radius: 8px; padding: 0 5px;
           color: var(--muted); }
  .scope.show { border-style: dashed; }
  .fate.leaving { color: var(--danger); }
  .fate.waiting { color: var(--plex); }
  .fate.clock   { color: var(--tautulli); }
  .fate.safe    { color: var(--ok); }

  /* dropped shows: one card per report, since each carries the user's own words */
  .drop-card { background: var(--panel2); border: 1px solid var(--border); border-radius: 8px;
               padding: 14px 16px; margin-bottom: 10px; }
  .drop-card.pending { box-shadow: inset 3px 0 0 var(--plex); }
  .drop-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  .drop-head b { font-size: 15px; }
  .drop-meta { color: var(--muted); font-size: 13px; margin-top: 5px; }
  .drop-meta b { color: var(--text); }
  .drop-reason { margin: 11px 0 0; padding: 8px 12px; border-left: 2px solid var(--plex);
                 background: rgba(229,160,13,.06); border-radius: 0 6px 6px 0;
                 white-space: pre-wrap; }
  .drop-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 13px; }
  .drop-hist { display: flex; align-items: flex-start; gap: 12px; color: var(--muted);
               font-size: 13px; padding: 9px 2px; border-bottom: 1px solid var(--border); }
  .drop-hist .body { flex: 1; min-width: 0; }
  .drop-hist button { padding: 2px 9px; font-size: 12px; line-height: 1.4;
                      background: none; color: var(--muted); }
  .drop-hist button:hover:not(:disabled) { color: var(--danger); border-color: var(--danger); }
  .drop-history-head { display: flex; align-items: baseline; gap: 12px; }
  .drop-history-head button { padding: 3px 10px; font-size: 12px; }
  .drop-hist b { color: var(--text); }
  .drop-hist .said { font-style: italic; }
  .drop-history-head { color: var(--muted); margin: 22px 0 4px; font-size: 14px; }
  .drop-history-head .spacer { flex: 1; }

  .stale-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px;
               flex-wrap: wrap; padding: 10px 16px; border-bottom: 1px solid var(--border);
               color: var(--muted); font-size: 13px; }

  tr.child td { background: rgba(0,0,0,.18); color: var(--muted); font-size: 13px;
                border-bottom: 1px dashed var(--border); }
  tr.child td:first-child { padding-left: 38px; }
  tr.child .src-label { font-weight: 600; }
  tr.child .src-label.t { color: var(--tautulli); }
  tr.child .src-label.p { color: var(--plex); }

  footer.actionbar { position: fixed; bottom: 0; left: 0; right: 0; background: var(--panel);
    border-top: 1px solid var(--border);
    padding: 12px 20px calc(12px + env(safe-area-inset-bottom)); display: none;
    align-items: center; gap: 14px; z-index: 6; }
  footer.actionbar .counts { color: var(--muted); }
  footer.actionbar .counts b { color: var(--text); }
  footer.actionbar .counts span[title] { cursor: help; border-bottom: 1px dotted var(--muted); }
  .spacer { flex: 1; }

  /* modal */
  .overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); display: none;
             align-items: center; justify-content: center; z-index: 20; }
  .modal { background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
           padding: 22px 24px; width: min(560px, 92vw);
           max-height: 88vh; max-height: 88dvh; overflow-y: auto; }
  .modal h2 { margin: 0 0 10px; font-size: 17px; }
  .modal .warn { color: var(--danger); font-weight: 600; }
  .modal ul { margin: 10px 0; padding-left: 20px; }
  .modal .row { display: flex; gap: 10px; justify-content: flex-end; margin-top: 18px; }
  .modal label.src { display: flex; gap: 8px; align-items: center; margin: 6px 0; }
  .modal input[type=text] { width: 100%; margin-top: 6px; }
  .modal textarea { width: 100%; margin-top: 6px; min-height: 92px; resize: vertical;
    background: var(--panel2); color: var(--text); border: 1px solid var(--border);
    border-radius: 6px; padding: 7px 10px; font: inherit; }
  .modal p.help { margin: 10px 0; }
  .modal .help b.t { color: var(--tautulli); }
  .modal .help b.p { color: var(--plex); }
  .muted { color: var(--muted); }

  /* results */
  #results, #stale-results, #dropped-results, #radarr-results, #sonarr-results { display: none; margin-bottom: 14px; background: var(--panel); border: 1px solid var(--border);
             border-radius: 8px; padding: 14px 16px; }
  #results h3, #stale-results h3, #dropped-results h3, #radarr-results h3, #sonarr-results h3 { margin: 0 0 8px; font-size: 15px; }
  #results .ok, #stale-results .ok, #dropped-results .ok, #radarr-results .ok, #sonarr-results .ok { color: var(--ok); }
  #results .bad, #stale-results .bad, #dropped-results .bad, #radarr-results .bad, #sonarr-results .bad { color: var(--danger); }
  #results button, #stale-results button, #dropped-results button, #radarr-results button, #sonarr-results button { float: right; padding: 2px 10px; }
  /* The busy spinner is amber wherever it lands, so "the app is working" reads
     as the accent does everywhere else, and a full track ring keeps it visible
     against a panel instead of dissolving into it. Inside a button it borrows
     the label's colour instead — that is the one place the background isn't
     the panel, and grey-on-amber was unreadable. */
  .spin { display: inline-block; width: 14px; height: 14px; border-radius: 50%;
          border: 2px solid color-mix(in srgb, var(--plex) 28%, transparent);
          border-top-color: var(--plex);
          animation: spin .7s linear infinite; vertical-align: -2px; }
  button .spin { border-color: color-mix(in srgb, currentColor 30%, transparent);
                 border-top-color: currentColor; }
  @keyframes spin { to { transform: rotate(360deg); } }

  /* ── "still working" ─────────────────────────────────────────────────
     A slow load used to say so in muted grey tacked onto the end of a tab's
     blurb, which is the last place an eye lands — and on a phone it was often
     below the fold entirely. Every one of those now floats as the same pill,
     in the same place, whichever tab raised it. core.js gathers them into
     #busy-host at boot so two overlapping loads stack rather than print on top
     of each other, and the host clears the history tab's action bar by the
     bar's measured height, since that wraps to two rows on a phone. */
  #busy-host { position: fixed; left: 0; right: 0; z-index: 30;
    bottom: calc(var(--actionbar-h, 0px) + 16px + env(safe-area-inset-bottom));
    display: flex; flex-direction: column-reverse; align-items: center; gap: 8px;
    padding: 0 12px; pointer-events: none; }
  .busy { display: none; align-items: center; gap: 10px; max-width: 100%;
    background: var(--panel2); color: var(--text); font-size: 13px;
    border: 1px solid color-mix(in srgb, var(--plex) 32%, var(--border));
    border-radius: 999px; padding: 9px 17px 9px 14px;
    box-shadow: 0 10px 28px rgba(0,0,0,.55);
    animation: busy-in .18s ease-out; }
  .busy .spin { flex: none; width: 15px; height: 15px; }
  @keyframes busy-in { from { opacity: 0; transform: translateY(10px) scale(.96); } }

  /* The spin itself is the message, so it stays; only the flourish goes. */
  @media (prefers-reduced-motion: reduce) {
    .busy { animation: none; }
    .spin { animation-duration: 1.6s; }
  }

  /* ── Email tab ──────────────────────────────────────────────── */
  .dg-toggle { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px;
               border: 1px solid var(--border); border-radius: 8px; cursor: pointer; margin: 14px 0; }
  .dg-toggle:hover { border-color: var(--muted); }
  .dg-toggle.checked { border-color: var(--plex); background: rgba(229,160,13,.06); }
  .dg-toggle input { margin-top: 3px; }
  .dg-toggle .desc { display: block; color: var(--muted); font-size: 13px; }
  .dg-field { display: block; margin: 14px 0; color: var(--muted); font-size: 13px; }
  .dg-field input { display: block; width: min(360px, 100%); margin-top: 6px; }
  .dg-sections { margin: 16px 0; }
  .dg-sections > b { font-size: 14px; }
  .dg-sections > .desc { display: block; color: var(--muted); font-size: 13px; margin: 2px 0 8px; }
  .dg-time input { width: 140px; }
  .dg-time .desc { display: block; margin-top: 4px; }

  /* One row per section: include it, and move it. Rows rather than chips
     because the order is now part of the answer, and a wrapped row of chips
     has no reading order to speak of — least of all on a phone. */
  .dg-order { list-style: none; margin: 0; padding: 0; display: flex;
              flex-direction: column; gap: 6px; }
  .dg-sec { display: flex; align-items: center; gap: 10px; padding: 9px 10px;
            border: 1px solid var(--border); border-radius: 8px; font-size: 13px; }
  .dg-sec.checked { border-color: var(--plex); background: rgba(229,160,13,.06); }
  .dg-sec.dragging { opacity: .5; }
  .dg-sec label { display: flex; align-items: center; gap: 8px; flex: 1; cursor: pointer;
                  min-width: 0; }
  .dg-sec label span { overflow: hidden; text-overflow: ellipsis; }
  .dg-grip { color: var(--muted); cursor: grab; font-size: 15px; line-height: 1; }
  .dg-move { display: flex; gap: 4px; }
  /* 34px so the arrows stay tappable on a phone, where dragging a list inside
     a scrolling page is a fight the page usually wins */
  .dg-move button { min-width: 34px; height: 34px; padding: 0; font-size: 14px;
                    line-height: 1; }
  @media (max-width: 560px) {
    .dg-sec { padding: 8px; gap: 8px; }
    .dg-grip { display: none; }
  }
  .dg-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 16px; }
  #dg-msg { display: block; margin: 10px 0 0; color: var(--muted); }
  #dg-msg.ok { color: var(--ok); }
  #dg-msg.bad { color: var(--danger); }
  .dg-admin-test { display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
               border-top: 1px solid var(--border); margin-top: 16px; padding-top: 14px; }
  .dg-admin-test input { width: min(280px, 100%); }

  /* admin overview: one compact aligned grid row per user */
  #dg-admin-card { max-width: none; }   /* the per-user grid needs the full page width */
  #dg-admin-rows { margin-top: 4px; }
  .dg-ov-only { display: inline-flex; gap: 6px; align-items: center; color: var(--muted);
            font-size: 13px; white-space: nowrap; cursor: pointer; }
  .dg-head, .dg-row { display: grid; align-items: center; gap: 4px 10px;
            grid-template-columns: minmax(110px, 1.2fr) 26px minmax(90px, .9fr) minmax(170px, 1.8fr) auto auto auto auto minmax(160px, 1fr); }
  .dg-head { color: var(--muted); font-size: 11px; text-transform: uppercase;
            letter-spacing: .05em; padding: 4px 2px; border-bottom: 1px solid var(--border); }
  .dg-row { padding: 3px 2px; border-bottom: 1px solid var(--border); font-size: 13px; }
  .dg-row .who { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .dg-row .who .sub { color: var(--muted); font-size: 11px; margin-left: 6px; }
  .dg-row > label { display: flex; align-items: center; justify-content: center; }
  .dg-row input[type=email], .dg-row input[type=text] { width: 100%; min-width: 0; font-size: 13px; padding: 4px 7px; }
  .dg-row select { width: auto; font-size: 13px; padding: 4px 4px; }
  .dg-row button { font-size: 12px; padding: 4px 10px; }
  .dg-row .stat { min-width: 0; color: var(--muted); font-size: 11px; white-space: nowrap;
            overflow: hidden; text-overflow: ellipsis; cursor: default; }
  .dg-row .stat.ok-dot::before { content: "● "; color: var(--ok); }
  .dg-row .stat.bad-dot::before { content: "● "; color: var(--danger); }
  .dg-row.off .who, .dg-row.off .stat { opacity: .55; }
  .dg-row.dirty { box-shadow: inset 3px 0 0 var(--plex); background: rgba(229,160,13,.04); }

  .modal iframe.dg-preview { width: 100%; height: min(70vh, 720px); border: 1px solid var(--border);
            border-radius: 8px; background: #191a1c; }
  .modal.wide { width: min(760px, 94vw); }
  /* A plain-text mailing opened from the bell — arrival texts, surveys.
     No markup to sandbox, so it's just the message. */
  .bell-body-text { white-space: pre-wrap; word-break: break-word; font-family: inherit;
                    font-size: 13px; color: var(--text); background: var(--panel);
                    border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px;
                    max-height: 60vh; overflow-y: auto; }

  .mobile-controls { display: none; gap: 8px; margin-bottom: 10px; }

  /* Touch fallback for info conveyed only via a hover `title` (e.g. the full
     requester list behind "A, B +2", or why a badge reads "Removal requested") —
     touch browsers have no reliable way to trigger those, see the script below. */
  .tap-tip { position: absolute; z-index: 30; max-width: min(320px, calc(100vw - 16px));
             background: var(--panel2); border: 1px solid var(--border); border-radius: 8px;
             padding: 9px 12px; color: var(--text); font-size: 13px; line-height: 1.45;
             white-space: pre-line; box-shadow: 0 6px 20px rgba(0,0,0,.45); }

  /* ── mobile: stack the table into cards ─────────────────────────── */
  @media (max-width: 640px) {
    header { flex-wrap: wrap; gap: 10px; padding: 10px 14px; }
    header h1 { font-size: 16px; }
    #whoami { gap: 8px; }
    #whoami span:not(.admin-badge):not(.chev) { max-width: 40vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* keep the menu on-screen on a narrow phone */
    #profile-menu { min-width: 0; width: min(300px, calc(100vw - 28px)); }

    main { padding: 14px 12px 160px; }
    .toolbar { gap: 8px; }
    #user-picker-wrap { display: flex; flex: 1 1 100%; gap: 8px; align-items: center; }
    #user-picker { flex: 1; min-width: 0; }
    #filter { flex: 1 1 auto; min-width: 0; }
    button { padding: 9px 14px; }
    input[type=checkbox] { width: 18px; height: 18px; }
    /* 16px stops iOS Safari from zooming the page when a field gets focus */
    select, input[type=text] { font-size: 16px; }
    #radarr-scope, #sonarr-scope, #radarr-alert-filter, #sonarr-alert-filter, #stale-user {
      flex: 1 1 100%; min-width: 0; }

    .mobile-controls { display: flex; }
    .mobile-controls button { flex: 1; }

    /* A strip of thirteen labels doesn't fit a phone width, scrolling sideways
       hides tabs with no hint they're there, and wrapping just trades that for
       a wall of buttons. Collapse to the current section's name as a single
       button; tapping it drops the full list down as a menu, closing again
       on selection (see the .tabs click handler in the script below). */
    .tabs-wrap { position: relative; }
    .tabs { border-bottom: none; flex-wrap: nowrap; }
    /* The desktop grouping is a width problem, and a phone doesn't have it —
       `display: contents` dissolves both wrappers so every .tab is a direct
       flex child again and every rule below applies unchanged. The parent
       buttons stay on as headings for the dropped-down list. */
    .tabgroup, .tabmenu { display: contents; }
    .tabs:not(.open) .tab-group { display: none; }
    .tabs.open .tab-group { display: block; padding: 12px 12px 2px; pointer-events: none;
      text-align: left; color: var(--muted); font-size: 11px; text-transform: uppercase;
      letter-spacing: .05em; }
    .tabs.open .tab-group::after { content: none; }
    .tabmenu .tab, .tabmenu .tab:hover, .tabmenu .tab.active { background: none; }
    .tabmenu .tm-sub { display: none; }
    .tabs #tab-btn-schedule.active { display: flex; }
    .tabs .tab:not(.active) { display: none; }
    .tabs .tab.active {
      display: flex; flex: 1 1 auto; align-items: center; justify-content: space-between;
      border: 1px solid var(--border); border-radius: 8px; background: var(--panel2);
      padding: 10px 14px;
    }
    .tabs .tab.active::after { content: "▾"; color: var(--muted); margin-left: 10px; }
    /* hover tooltips don't apply on touch — suppress just that pseudo-element,
       not ::after generally, since the chevron above needs it to stay free */
    .tabs .tab[data-tip]:hover::after, .tabs .tab[data-tip]:focus-visible::after { content: none !important; }

    .tabs.open {
      position: absolute; left: 0; right: 0; top: 0; z-index: 16;
      flex-direction: column; align-items: stretch; gap: 3px; padding: 5px;
      background: var(--panel2); border: 1px solid var(--border); border-radius: 10px;
      box-shadow: 0 10px 30px rgba(0,0,0,.5);
    }
    .tabs.open .tab {
      display: flex; align-items: center; justify-content: space-between;
      border: none; border-radius: 6px; padding: 10px 12px; background: none;
    }
    .tabs.open .tab.active { background: var(--border); }
    .tabs.open .tab.active::after { content: "▴"; }

    /* card layout */
    .pane thead { display: none; }
    .pane table, .pane tbody, .pane tbody tr, .pane tbody td { display: block; }

    .pane tbody tr {
      position: relative;
      padding: 12px 40px 12px 40px;
      border-bottom: 1px solid var(--border);
    }
    /* only reserve the side gutters when the row actually has a checkbox / chevron */
    .pane tbody tr:not(:has(> td.c-sel)) { padding-left: 14px; }
    .pane tbody tr:not(:has(> td.chev)) { padding-right: 14px; }
    .pane tbody tr:hover td { background: none; }
    .pane tbody td { border: none; padding: 0; }

    .c-sel { position: absolute; left: 12px; top: 14px; }
    td.chev { position: absolute; right: 8px; top: 10px; width: auto; padding: 4px 8px; font-size: 16px; }

    .c-title { font-weight: 600; font-size: 15px; margin-bottom: 5px; }
    /* metadata cells flow as one dot-separated line under the title;
       ".pane td" prefix outranks the ".pane tbody td { display: block }" reset */
    .pane td.c-type, .pane td.c-lib, .pane td.c-date, .pane td.c-req, .pane td.c-reqstatus {
      display: inline; color: var(--muted); font-size: 13px; margin: 0;
      white-space: normal;  /* the JS-built DOM has no text nodes between cells, so
                               nowrap would leave the line no place to break */
    }
    .c-lib::before, .c-date::before, .c-req::before, .c-reqstatus::before {
      content: " · "; color: var(--border); }
    .pane td.blank { display: none; }
    .pane td.c-reqdel { display: block; margin-top: 8px; }
    .pane td.c-reqdel::before { content: none; }
    /* block, not inline-block: notes like "notified …" precede the button with no
       whitespace between nodes, so an inline button could never wrap to its own line */
    td.c-reqdel { text-align: left; }
    td.c-reqdel .acts { display: flex; margin-top: 6px; }
    td.c-reqdel .acts button { font-size: 13px; padding: 7px 12px; }

    /* child (per-record) rows as indented sub-items */
    tr.child { display: flex !important; align-items: flex-start; gap: 10px;
               padding: 10px 12px 10px 40px !important; }
    tr.child td { display: none; }
    tr.child td:first-child { display: block; }        /* the checkbox */
    tr.child td:last-child { display: block; flex: 1; }  /* the detail text */
    tr.child td:first-child { padding-left: 0 !important; }

    footer.actionbar { flex-wrap: wrap;
      padding: 10px 12px calc(10px + env(safe-area-inset-bottom)); gap: 8px; }
    footer.actionbar .counts { flex: 1 1 100%; }
    footer.actionbar .spacer { display: none; }
    footer.actionbar button { flex: 1; }

    /* admin "Delete Selected Content" bar under the Movies / Television tables */
    .arr-bar { flex-wrap: wrap; gap: 8px; }
    .arr-bar .counts { flex: 1 1 100%; }
    .arr-bar .spacer { display: none; }
    .arr-bar button { flex: 1; }

    .contact-card button { flex: 1; }

    .sched-card { padding: 14px; }
    #btn-save-sched, #btn-sim-sched { display: block; width: 100%; margin-top: 8px; }
    #sched-msg { display: block; margin: 8px 0 0; }

    .promo-card .promo-btn { flex: 1 1 100%; text-align: center; }
    .set-card { padding: 14px; }
    .set-actions button { flex: 1 1 auto; }
    .set-actions .set-msg { flex: 1 1 100%; }

    /* Email tab — stack each admin user row into a compact card */
    .dg-head { display: none; }
    .dg-row { grid-template-columns: 1fr 1fr; gap: 8px 12px; padding: 12px 0; }
    .dg-row .who { grid-column: 1; }
    .dg-row > label { grid-column: 2; justify-self: end; }
    .dg-row input[type=email], .dg-row input[type=text], .dg-row select, .dg-row .stat { grid-column: 1 / -1; }
    .dg-row select { width: 100%; }
    .dg-row .ov-save, .dg-row .ov-preview, .dg-row .ov-test { width: 100%; }
    .dg-actions button { flex: 1; }
    .drop-actions button { flex: 1 1 100%; }
    .dg-admin-test input, .dg-admin-test button { flex: 1 1 100%; }

    #imp-banner { flex-wrap: wrap; }
    #imp-banner button { margin-left: 0; flex: 1 1 100%; }
  }
