/* Media Preview Generator — design tokens + custom styles
 *
 * Design tokens are deliberately lightweight: brand colour + a small
 * scale of surface elevations / radii / shadows / focus styles. The
 * rest of the visual language defers to Bootstrap 5.3 so we don't end
 * up maintaining a parallel design system.
 */

:root {
    /* Brand */
    --plex-orange: #e5a00d;
    --plex-orange-text: #e5a00d;
    --plex-orange-soft: rgba(229, 160, 13, 0.12);
    --plex-gray: #1a1a2e;
    --plex-dark: #0f0f1a;

    /* Surface elevation — used for cards/modals/popovers when we need
       more than Bootstrap's flat default. Two levels are enough; a third
       starts to feel like skeuomorphism. */
    --surface-1: rgba(255, 255, 255, 0.04);
    --surface-2: rgba(255, 255, 255, 0.06);
    --surface-border: rgba(255, 255, 255, 0.08);

    /* Radii — match Bootstrap's --bs-border-radius-* but expose them
       under shorter names so .css elsewhere doesn't have to know about
       Bootstrap internals. */
    --radius-sm: 0.375rem;
    --radius:    0.5rem;
    --radius-lg: 0.75rem;

    /* Shadows — only used for modals + dropdowns; we deliberately avoid
       elevating cards (would feel cluttered on the dense dashboard). */
    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.18), 0 1px 1px rgba(0, 0, 0, 0.12);
    --shadow-2: 0 6px 16px rgba(0, 0, 0, 0.22), 0 2px 6px rgba(0, 0, 0, 0.15);

    /* Focus ring — branded orange so it stands out on both dark and
       light themes (Bootstrap's default primary blue washes out on the
       dark Plex background and is invisible against dark form fields). */
    --focus-ring: 0 0 0 0.2rem rgba(229, 160, 13, 0.45);

    /* Dashboard redesign tokens. One hue per task type, reused for worker capability icons, job chips and queue
       rows; state colours come in a solid and a tinted "-soft" pair. --inset and --line alias the surface tokens
       above so every page shares one source. Amber is reserved for primary action, next schedule and job-ID links. */
    --inset: var(--surface-1);
    --line: var(--surface-border);
    --line-strong: rgba(255, 255, 255, 0.14);
    /* One navy family for every raised surface (dropdown, offcanvas, modal, tooltip): lighter than the card, never grey. */
    --panel-2: #242439;
    /* Dark text tokens, contrast on page #1a1a2e / card #252538 / inset #2e2e40 (tests/test_design_tokens.py checks them):
       body #d8dce6 12.4/10.9/9.7, muted 7.2/6.4/5.6, faint 6.1/5.3/4.7. Body stays below pure white on purpose: large
       tables of #fff text halate on a dark page. */
    --muted: #a3a8bd;
    --faint: #9499b0;
    --accent: #e5a00d;
    --accent-text: #e5a00d;
    --accent-ink: #1a1203;
    /* Button fill only: a touch deeper than the text/icon amber so a large button is not the brightest object on screen. */
    --accent-fill: #d19610;
    --accent-soft: rgba(229, 160, 13, 0.14);
    --ok: #4fbf8f;   --ok-soft: rgba(79, 191, 143, 0.14);
    --bad: #f1858e;  --bad-soft: rgba(241, 133, 142, 0.14);
    --run: #74adff;  --run-soft: rgba(91, 157, 255, 0.15);
    --warn: #e5a00d; --warn-soft: rgba(229, 160, 13, 0.14);
    --t-previews: #5b9dff; --t-previews-soft: rgba(91, 157, 255, 0.15);
    --t-intro: #b49cff;    --t-intro-soft: rgba(180, 156, 255, 0.15);
    --t-loud: #36d1c4;     --t-loud-soft: rgba(54, 209, 196, 0.15);
    --radius-card: 12px;
    --radius-ctl: 8px;
    --radius-xs: 6px;
    /* One control height and one icon-button size for every page (44px where a finger is the pointer). */
    --ctl-h: 36px;
    --ctl-icon: 32px;
    /* One page gutter and content width: .page-shell (base.html main + navbar) is the only horizontal inset a page gets. */
    --page-gutter: 24px;
    --page-max: 1440px;
    --mono: ui-monospace, "JetBrains Mono", "SFMono-Regular", Menlo, monospace;
}

html[data-bs-theme="dark"] {
    --bs-body-color: #d8dce6;
    --bs-body-color-rgb: 216, 220, 230;
    --bs-emphasis-color: #e8eaf2;
    --bs-emphasis-color-rgb: 232, 234, 242;
    --bs-secondary-bg: #2e2e40;
    --bs-tertiary-bg: #242439;
    --bs-border-color: rgba(255, 255, 255, 0.12);
    --bs-link-color: #e5a00d;
    --bs-link-color-rgb: 229, 160, 13;
    --bs-link-hover-color: #f0b232;
    --bs-link-hover-color-rgb: 240, 178, 50;
}

html[data-bs-theme="light"] {
    --plex-orange-text: #7a4f00;
    /* ──────────────────────────────────────────────────────────────────
     * Modern-clean light theme — Linear / Stripe / Vercel family.
     *
     * Design principles:
     *   1. Cool slate surfaces, never warm. Warm beiges read "consumer
     *      web 2010"; cool greys read "professional SaaS 2026".
     *   2. Status badges use TINTED bg + DARK text (Bootstrap 5.3's
     *      *-subtle pattern) instead of solid saturated bg + white text.
     *      Solid white-on-saturated reads as a warning at every size;
     *      tinted-on-tinted reads as data.
     *   3. Brand orange is a SCALPEL — primary CTA + active nav only.
     *      Never on cards, borders, or data accents.
     *   4. Cards lift via 1px slate border + bg contrast, not shadow.
     *      Shadows are reserved for genuine elevation (modals, popovers).
     *   5. Tonal scale (slate-50 → 900) for every neutral.
     * ────────────────────────────────────────────────────────────────── */

    /* Surfaces — every level uses cool slate so the eye reads them as
       one family. Body is darker than cards, but cards themselves are
       a soft off-white (NOT pure #fff) so they read as "paper" rather
       than "OLED display at full brightness". The contrast between
       body and card stays just visible — modern SaaS look (Notion,
       Linear) avoids both pure-white cards and pure-black text. */
    --bs-body-bg: #e4e7eb;          /* slate-200-ish — darker so cards visibly lift */
    --bs-secondary-bg: #fbfcfd;     /* soft off-white card surface */
    --bs-tertiary-bg: #eceff3;      /* code blocks / raised regions — slightly cooler */

    /* Text scale — never pure black; slate-900 reads more refined and
       pairs with the slate surfaces. Three levels are enough. */
    --bs-body-color: #1e293b;       /* slate-800 — primary text */
    --bs-secondary-color: #526075;  /* 5.2:1 on the page grey, 6.2:1 on a card (slate-500 was 3.8:1) */
    --bs-tertiary-color: #94a3b8;   /* slate-400 — disabled / placeholders */
    --bs-emphasis-color: #1e293b;   /* same as body: #0f172a headings read harder than the text under them */
    --bs-link-color: #7a4f00;
    --bs-link-color-rgb: 122, 79, 0;
    --bs-link-hover-color: #5c3b00;
    --bs-link-hover-color-rgb: 92, 59, 0;

    /* Borders — visible enough to define card edges, quiet enough to
       not compete with content. Two weights. */
    --bs-border-color: #cbd5e1;             /* slate-300: a card edge is 1.45:1 on the card, slate-200 was 1.2:1 */
    --bs-border-color-translucent: rgba(15, 23, 42, 0.08);

    /* Status palette — solid colours kept for the rare callsite that
       wants a vivid accent (the GPU stepper's "+" button), but every
       BADGE switches to subtle tinted bg + dark text via the .bg-*
       overrides further down. The subtle pattern is the actual
       sophistication move; this layer just keeps Bootstrap happy.

       We use Tailwind 600-weights for the solid colours (not 500) so
       on the rare solid usage they don't shout. */
    --bs-primary: #475569;           /* slate-600 — neutral data accent */
    --bs-primary-rgb: 71, 85, 105;
    --bs-info: #0e7490;              /* cyan-700 */
    --bs-info-rgb: 14, 116, 144;
    --bs-success: #047857;           /* emerald-700 — slightly deeper */
    --bs-success-rgb: 4, 120, 87;
    --bs-warning: #b45309;           /* amber-700 */
    --bs-warning-rgb: 180, 83, 9;
    --bs-danger: #b91c1c;            /* red-700 */
    --bs-danger-rgb: 185, 28, 28;

    /* Subtle bg + emphasis text pairs — these are what badges + tinted
       alerts use. Tailwind 100/800 pairings hit WCAG-AA at 12px+. */
    --bs-primary-bg-subtle: #e2e8f0;     /* slate-200 */
    --bs-primary-text-emphasis: #334155;  /* slate-700 */
    --bs-secondary-bg-subtle: #f1f5f9;    /* slate-100 */
    --bs-secondary-text-emphasis: #475569; /* slate-600 */
    --bs-success-bg-subtle: #d1fae5;     /* emerald-100 */
    --bs-success-text-emphasis: #065f46;  /* emerald-800 */
    --bs-info-bg-subtle: #cffafe;        /* cyan-100 */
    --bs-info-text-emphasis: #155e75;     /* cyan-800 */
    --bs-warning-bg-subtle: #fef3c7;     /* amber-100 */
    --bs-warning-text-emphasis: #92400e;  /* amber-800 */
    --bs-danger-bg-subtle: #fee2e2;      /* red-100 */
    --bs-danger-text-emphasis: #991b1b;   /* red-800 */

    /* Surface tokens. */
    --surface-1: rgba(15, 23, 42, 0.03);
    --surface-2: rgba(15, 23, 42, 0.05);
    --surface-border: #cbd5e1;

    /* Light-mode shadow tokens. Dark mode declares --shadow-1/-2 on :root
       with rgba(0,0,0,0.22) — appropriate for a near-black canvas but too
       heavy on a slate-200 page bg. Re-tune for the lighter canvas so
       modals/toasts/cards lift cleanly without the "drop-shadow on white
       paper" look. */
    --shadow-1: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 1px rgba(15, 23, 42, 0.04);
    --shadow-2: 0 8px 24px rgba(15, 23, 42, 0.08), 0 2px 6px rgba(15, 23, 42, 0.05);

    /* Dashboard redesign tokens, light values. Measured against the #e4e7eb page background, not just white cards:
       faint/ok/warn/accent-text >= 4.5:1 there, dark --accent-ink on the amber fill >= 4.5:1. Do not go lighter. */
    --focus-ring: 0 0 0 0.2rem rgba(122, 79, 0, 0.55);
    --line-strong: #cfd4e0;
    --panel-2: #f2f4f8;
    --muted: #5d6275;
    --faint: #5f6579;
    --accent: #b87a00;
    --accent-text: #7a4f00;
    --accent-ink: #1a1203;
    --accent-soft: rgba(229, 160, 13, 0.16);
    --ok: #0a6340;   --ok-soft: rgba(19, 138, 88, 0.12);
    --bad: #b01f2c;  --bad-soft: rgba(200, 50, 63, 0.10);
    --run: #1c55bd;  --run-soft: rgba(37, 99, 214, 0.10);
    --warn: #805300; --warn-soft: rgba(229, 160, 13, 0.16);
    --t-previews: #2563d6; --t-previews-soft: rgba(37, 99, 214, 0.10);
    --t-intro: #6d4fd8;    --t-intro-soft: rgba(109, 79, 216, 0.10);
    --t-loud: #096f67;     --t-loud-soft: rgba(12, 143, 132, 0.10);
    --accent-fill: #e5a00d;
}

.skip-link {
    position: fixed;
    top: 0.75rem;
    left: 0.75rem;
    z-index: 2000;
    transform: translateY(-200%);
    padding: 0.65rem 0.9rem;
    border-radius: var(--radius-sm);
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    box-shadow: var(--shadow-2);
}
.skip-link:focus { transform: translateY(0); }
#main-content:focus { outline: none; }

html[data-bs-theme="light"] .navbar .nav-link.active,
html[data-bs-theme="light"] .nav-pills .nav-link.active,
html[data-bs-theme="light"] .nav-tabs .nav-link.active,
html[data-bs-theme="light"] .btn-outline-warning,
html[data-bs-theme="light"] .card-header > .bi {
    color: var(--plex-orange-text);
}

.dashboard-list-scope {
    margin-left: 0.4rem;
    color: var(--bs-secondary-color);
    font-size: 0.72rem;
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
}
.server-edit-mobile-nav { display: none; }
@media (max-width: 991.98px) {
    .server-edit-mobile-nav {
        position: sticky;
        top: -1rem;
        z-index: 5;
        display: block;
        margin: -1rem -1rem 1rem;
        padding: 0.75rem 1rem;
        border-bottom: 1px solid var(--bs-border-color);
        background: var(--bs-body-bg);
    }
    .server-edit-mobile-nav .form-label { margin-bottom: 0.25rem; font-weight: 600; }
    #editServerModal .nav-tabs { display: none; }
}
@media (min-width: 992px) {
    .dashboard-system-card .system-card-body {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: 0.6rem 1.25rem;
    }
    .dashboard-system-card .system-card-body > :nth-child(1) { grid-column: 1; grid-row: 1 / span 2; }
    .dashboard-system-card .system-card-body > :nth-child(2) { grid-column: 1; grid-row: 3; }
    .dashboard-system-card .system-card-body > :nth-child(3) { grid-column: 2; grid-row: 1 / span 2; }
    .dashboard-system-card .system-card-body > :nth-child(4) { grid-column: 2; grid-row: 3; }
}
@media (max-width: 991.98px) {
    #dashboard-workers,
    #dashboard-all-jobs { scroll-margin-top: 8rem; }
}

/* ──────────────────────────────────────────────────────────────────────
 * Light mode badge overhaul — Bootstrap's `.bg-success` etc default
 * to solid saturated bg with white text. That reads "alert! warning!"
 * at every size. Modern dashboards (Linear, Stripe, Vercel) use
 * tinted-bg + dark-text pills which read as DATA, not state alarms.
 *
 * We override .bg-* badges and alerts in light mode only — dark mode
 * keeps the solid pills because they pop against the navy background.
 * Alerts get the same treatment so info / success banners feel
 * informative rather than urgent.
 * ────────────────────────────────────────────────────────────────────── */
html[data-bs-theme="light"] .badge.bg-primary,
html[data-bs-theme="light"] .alert-primary {
    background-color: var(--bs-primary-bg-subtle) !important;
    color: var(--bs-primary-text-emphasis) !important;
    border-color: transparent;
}
html[data-bs-theme="light"] .badge.bg-secondary,
html[data-bs-theme="light"] .alert-secondary {
    background-color: var(--bs-secondary-bg-subtle) !important;
    color: var(--bs-secondary-text-emphasis) !important;
    border-color: transparent;
}
html[data-bs-theme="light"] .badge.bg-success,
html[data-bs-theme="light"] .alert-success {
    background-color: var(--bs-success-bg-subtle) !important;
    color: var(--bs-success-text-emphasis) !important;
    border-color: transparent;
}
html[data-bs-theme="light"] .badge.bg-info,
html[data-bs-theme="light"] .alert-info {
    background-color: var(--bs-info-bg-subtle) !important;
    color: var(--bs-info-text-emphasis) !important;
    border-color: transparent;
}
html[data-bs-theme="light"] .badge.bg-warning,
html[data-bs-theme="light"] .alert-warning {
    background-color: var(--bs-warning-bg-subtle) !important;
    color: var(--bs-warning-text-emphasis) !important;
    border-color: transparent;
}
html[data-bs-theme="light"] .badge.bg-danger,
html[data-bs-theme="light"] .alert-danger {
    background-color: var(--bs-danger-bg-subtle) !important;
    color: var(--bs-danger-text-emphasis) !important;
    border-color: transparent;
}

/* `.text-dark` modifier (used by older callsites like `.bg-warning text-dark`)
   becomes a no-op in light mode — the tinted bg + emphasis text already
   gives us a dark-on-tint pill, so the .text-dark override would force
   pure-black text and lose the emphasis-colour nuance. */
html[data-bs-theme="light"] .badge.text-dark { color: inherit !important; }

/* Progress bar fills — the table's "Workers" column shows a 100% bar
 * coloured by status (.bg-success / .bg-warning). Inside .progress-bar
 * we want the SOLID colour (Bootstrap blue → tinted is too washed for
 * a fill), but the TEXT inside needs to stay dark for legibility on
 * the lighter solid. Hand-tuned so the fill is visibly green/amber/red
 * but the percentage label remains readable. */
html[data-bs-theme="light"] .progress-bar.bg-success { background-color: #10b981 !important; color: #064e3b; }
html[data-bs-theme="light"] .progress-bar.bg-warning { background-color: #f59e0b !important; color: #78350f; }
html[data-bs-theme="light"] .progress-bar.bg-danger  { background-color: #ef4444 !important; color: #7f1d1d; }
html[data-bs-theme="light"] .progress-bar.bg-secondary { background-color: #94a3b8 !important; color: #1e293b; }

/* ──────────────────────────────────────────────────────────────────────
 * Focus rings — applied to every interactive element. Bootstrap's
 * default focus indicator is so subtle on this dark theme that
 * keyboard users can't tell where they are; this lifts it to a
 * visible orange ring that matches the brand. Mouse clicks still get
 * the browser's :focus-visible suppression so the ring only appears
 * for keyboard navigation.
 * ────────────────────────────────────────────────────────────────────── */
.btn:focus-visible,
.form-control:focus-visible,
.form-select:focus-visible,
.form-check-input:focus-visible,
.dropdown-toggle:focus-visible,
.nav-link:focus-visible,
a:focus-visible {
    box-shadow: var(--focus-ring) !important;
    outline: none;
}

/* Page-level title block — used by every top-level page so the H1
 * + supporting subtitle has uniform spacing. The original templates
 * each rolled their own h1/h2 with different margins; this normalises
 * to the same leading + trailing space. */
.page-header {
    margin-bottom: 1.75rem;
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.page-header h1,
.page-header .page-title {
    font-size: 1.75rem;
    font-weight: 600;
    margin: 0;
    line-height: 1.2;
}

.page-header .page-subtitle {
    color: var(--bs-secondary-color);
    font-size: 0.95rem;
    margin-top: 0.25rem;
}

.page-header .page-actions {
    display: flex;
    gap: 0.5rem;
    align-items: center;
}

/* ──────────────────────────────────────────────────────────────────────
 * Layout — centre the main column at a comfortable reading width on
 * very wide displays (1920+). Bootstrap container-fluid is full-bleed
 * which makes the dashboard feel like a generic admin tool; capping
 * the content rail at ~1500px gives the app a designed feel without
 * shrinking on common laptop widths.
 * ────────────────────────────────────────────────────────────────────── */
/* .page-shell is the one horizontal frame: main, the navbar row and the config banner share the same gutter and
 * max width, so every page's first content edge lines up (it was 12/20/72/118px per page). Pages add no inset of
 * their own; a full-bleed page (logs) opts out explicitly. */
.page-shell {
    width: 100%;
    max-width: var(--page-max);
    margin-inline: auto;
    padding-inline: var(--page-gutter);
}
@media (max-width: 575.98px) {
    :root { --page-gutter: 16px; }
}

/* ──────────────────────────────────────────────────────────────────────
 * Brand accent strip — single 2px line under the nav using the brand
 * gradient. Anchors the top of the page and gives every screen an
 * unmistakable Plex-orange edge that the eye picks up immediately.
 * Plus frosted-glass effect on the navbar background — a modern dark
 * theme staple that reads as polish without being flashy.
 * ────────────────────────────────────────────────────────────────────── */
/* No `position` here: Bootstrap's .sticky-top already makes the nav positioned (sticky), which anchors the
 * ::after strip. Setting `relative` outranks .sticky-top and the navbar scrolls away with the page. */
nav.navbar.sticky-top {
    border-bottom: 1px solid var(--surface-border) !important;
    backdrop-filter: saturate(180%) blur(14px);
    -webkit-backdrop-filter: saturate(180%) blur(14px);
}
html[data-bs-theme="dark"] nav.navbar.sticky-top {
    background-color: rgba(15, 15, 26, 0.78) !important;
}
html[data-bs-theme="light"] nav.navbar.sticky-top {
    background-color: rgba(244, 246, 248, 0.86);
}
nav.navbar.sticky-top::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2px;
    pointer-events: none;
}
html[data-bs-theme="dark"] nav.navbar.sticky-top::after {
    background: linear-gradient(
        90deg,
        rgba(229, 160, 13, 0) 0%,
        rgba(229, 160, 13, 0.65) 18%,
        rgba(229, 160, 13, 0.65) 82%,
        rgba(229, 160, 13, 0) 100%
    );
}
/* Light mode: drop the strip to a single 1px slate divider — the
 * orange gradient on white was too candy-bright. Cleaner separation
 * via a flat hairline reads as proper "header / body" hierarchy. */
html[data-bs-theme="light"] nav.navbar.sticky-top::after {
    height: 1px;
    background: #cbd5e1;
}

/* Nav link hover: subtle highlight band, no jarring fill change. */
.navbar .nav-link {
    border-radius: var(--radius-sm);
    padding-left: 0.7rem !important;
    padding-right: 0.7rem !important;
    transition: background-color 0.15s ease, color 0.15s ease;
}
html[data-bs-theme="dark"] .navbar .nav-link:hover {
    background-color: rgba(255, 255, 255, 0.05);
}
html[data-bs-theme="light"] .navbar .nav-link:hover {
    background-color: rgba(0, 0, 0, 0.05);
}
.navbar .nav-link.active {
    color: var(--plex-orange-text) !important;
    background-color: var(--plex-orange-soft);
}
@media (min-width: 1200px) {
    .navbar .navbar-nav.me-auto > .nav-item > .nav-link:not(.active) {
        color: var(--muted);
    }
    .navbar .navbar-nav.me-auto > .nav-item > .nav-link:not(.active):hover {
        color: var(--bs-body-color);
    }
}

/* Automation, Settings and Tools hover menus (nav_menus.js). Bootstrap places a navbar dropdown's list under its toggle
 * only when its own JS opened it (it sets data-bs-popper then), so place these lists the same way. */
@media (min-width: 1200px) {
    .navbar .nav-hover-menu > .dropdown-menu {
        top: 100%;
        left: 0;
        margin-top: var(--bs-dropdown-spacer);
    }
}

/* ──────────────────────────────────────────────────────────────────────
 * Panel — modern replacement for Bootstrap's flat .card. Larger
 * radius, soft surface gradient, low-contrast border. Header has a
 * subtle top padding bump and the card-title gets uppercase tracking
 * so the visual hierarchy reads as "section / content" without
 * needing dividing rules everywhere.
 *
 * NB: the existing markup uses `.card`, so we redefine `.card` rather
 * than introducing a new class — the visual upgrade is uniform across
 * every page without hunting down individual templates. The whole
 * point of a design system is "change one place, propagate".
 * ────────────────────────────────────────────────────────────────────── */
html[data-bs-theme="dark"] .card {
    background: linear-gradient(180deg, rgba(255,255,255,0.045) 0%, rgba(255,255,255,0.02) 100%);
    border-radius: var(--radius-lg);
    box-shadow: none;
    transition: border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease;
}
html[data-bs-theme="light"] .card {
    /* Soft off-white (not pure #fff) — pure-white cards on a grey body
       read as "OLED at full brightness" rather than "paper". The 2-3%
       desaturation softens that without losing the elevation. */
    background: #fbfcfd;
    /* Visibly darker 1px slate border (slate-300 territory) so cards
       actually lift against the deeper body bg. The previous #e2e6eb
       was almost invisible against the prior body bg #eef0f3 — now
       that body is darker AND the border is a touch heavier the cards
       read as panels with edges, not floating washes. */
    border: 1px solid #cbd5e1;
    border-radius: var(--radius-lg);
    box-shadow: none;
    transition: border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease;
}

html[data-bs-theme="dark"] .card-header,
html[data-bs-theme="light"] .card-header {
    background: transparent;
    border-bottom: 1px solid var(--surface-border);
    padding: 0.85rem 1.1rem;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
    color: var(--bs-secondary-color);
}

.card-header .bi {
    color: var(--plex-orange-text);
    font-size: 0.95rem;
}

.card-body { padding: 1.1rem; }
.card-body.p-0 { padding: 0; } /* keep for tables */

/* Light theme status-dot colours: muted Tailwind hues, matching --bs-success / --bs-danger. */
html[data-bs-theme="light"] .status-dot.status-running   { color: #475569; }
html[data-bs-theme="light"] .status-dot.status-completed { color: #059669; }
html[data-bs-theme="light"] .status-dot.status-failed    { color: #b91c1c; }
html[data-bs-theme="light"] .status-dot.status-cancelled { color: #d97706; }
html[data-bs-theme="light"] .status-dot.status-warning   { color: #d97706; }

/* ──────────────────────────────────────────────────────────────────────
 * Status indicator dots — replaces full-fill status pills in dense
 * tables. A coloured ●  before a neutral label reads cleaner than
 * stacked pill badges and works at any column width.
 * ────────────────────────────────────────────────────────────────────── */
.status-dot {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-size: 0.82rem;
    font-weight: 500;
    color: var(--bs-body-color);
    line-height: 1;
}
.status-dot::before {
    content: "";
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 50%;
    background-color: currentColor;
    opacity: 0.85;
    flex-shrink: 0;
}
.status-dot.status-pending   { color: #adb5bd; }
.status-dot.status-running   { color: #4a9eff; }
.status-dot.status-completed { color: #2ecc71; }
.status-dot.status-failed    { color: #e74c3c; }
.status-dot.status-cancelled { color: var(--plex-orange); }
.status-dot.status-warning   { color: var(--plex-orange); }

/* Pulse the running dot so a live job is unmistakable at a glance. */
.status-dot.status-running::before {
    animation: status-dot-pulse 1.6s ease-in-out infinite;
}
@keyframes status-dot-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(74, 158, 255, 0.4); }
    50% { box-shadow: 0 0 0 4px rgba(74, 158, 255, 0); }
}

/* Smooth out the default Bootstrap btn transition feel — primary
 * actions are the same, but icon-only secondary buttons get a more
 * tactile feel.
 */
.btn { transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease; }

/* Primary button uses the brand orange in BOTH themes so the CTA
 * matches the orange logo + active nav tab. Without this, light mode
 * shows Bootstrap's stock blue which clashes with the orange brand. */
.btn-primary {
    background-color: var(--accent-fill);
    border-color: var(--accent-fill);
    color: #161616;
    font-weight: 600;
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active,
.btn-primary:focus-visible {
    background-color: #c08909;
    border-color: #c08909;
    color: #161616;
}

/* Outline-primary mirrors btn-primary — brand orange in both themes.
 * Without this it stays Bootstrap blue, which clashes with the orange
 * primary actions everywhere else (e.g., Manual Trigger button on
 * the dashboard, Edit on server cards, Refresh libraries, etc). */
.btn-outline-primary {
    color: var(--plex-orange-text);
    border-color: var(--plex-orange);
}
.btn-outline-primary:hover,
.btn-outline-primary:focus,
.btn-outline-primary:active,
.btn-check:checked + .btn-outline-primary {
    background-color: var(--plex-orange);
    border-color: var(--plex-orange);
    color: #161616;
}

/* `bg-primary` badges (worker count "2", "NVIDIA"/"INTEL" GPU type pill,
 * dashboard "Processing" status pill) — on dark theme, paint with brand
 * orange so they pop against navy. On light theme they inherit the
 * muted slate (--bs-primary override) so the data badges stay calm
 * instead of shouting alongside the orange CTAs. */
html[data-bs-theme="dark"] .bg-primary {
    background-color: var(--plex-orange) !important;
    color: #161616 !important;
}

/* ──────────────────────────────────────────────────────────────────────
 * Tables — borderless rows with subtle hover. The default Bootstrap
 * table-bordered look adds visual noise to the dense Job Queue.
 * ────────────────────────────────────────────────────────────────────── */
.table { --bs-table-border-color: var(--surface-border); }
.table > :not(caption) > * > * { border-bottom-width: 1px; }
html[data-bs-theme="dark"] .table > tbody > tr:last-child > td { border-bottom-color: transparent; }

/* ──────────────────────────────────────────────────────────────────────
 * Form controls — slightly taller default with cleaner placeholder
 * contrast. The original dark-theme form-control inherits Bootstrap's
 * tight 0.375rem padding which feels cramped in a long form.
 * ────────────────────────────────────────────────────────────────────── */
.form-control,
.form-select {
    border-radius: var(--radius-sm);
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
}

/* ──────────────────────────────────────────────────────────────────────
 * Tabs (nav-pills + nav-tabs) — branded active state. Bootstrap's
 * default uses primary blue which clashes with the new orange brand
 * primary on dark theme. Fill becomes the brand orange soft tint and
 * the active label picks up the brand colour.
 * ────────────────────────────────────────────────────────────────────── */
.nav-pills .nav-link {
    color: var(--bs-body-color);
    background-color: transparent;
    border-radius: var(--radius-sm);
    transition: background-color 0.15s ease, color 0.15s ease;
}
html[data-bs-theme="dark"] .nav-pills .nav-link:hover {
    background-color: rgba(255, 255, 255, 0.04);
}
.nav-pills .nav-link.active,
.nav-pills .show > .nav-link {
    background-color: var(--plex-orange-soft) !important;
    color: var(--plex-orange-text) !important;
    font-weight: 600;
}

/* Bootstrap's standard nav-tabs (used in modals) — matched look. */
.nav-tabs .nav-link.active {
    color: var(--plex-orange-text);
    border-bottom-color: var(--plex-orange);
    background-color: transparent;
}
.nav-tabs .nav-link {
    color: var(--bs-body-color);
    border-top: none;
    border-left: none;
    border-right: none;
    transition: color 0.15s ease, border-color 0.15s ease;
}

/* ──────────────────────────────────────────────────────────────────────
 * Modals — modern with larger radius + softer backdrop.
 * ────────────────────────────────────────────────────────────────────── */
.modal-content {
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-2);
    overflow: hidden;
}
.modal-header {
    padding: 1rem 1.5rem;
    /* Slightly heavier title weight + tighter line-height so the modal
       reads as "this is a focused task" rather than "another card". */
}
.modal-header .modal-title {
    font-weight: 600;
    font-size: 1.05rem;
    letter-spacing: -0.005em;
}
.modal-body { padding: 1.25rem 1.5rem 0.5rem; }
.modal-footer {
    padding: 0.75rem 1.5rem 1rem;
    /* No top border — the body's trailing whitespace + button styling
       provide enough separation. The hard line read as a divider was
       making compact modals feel "boxy". */
    border-top: 0;
    background: var(--surface-1, transparent);
}

/* Form-label inside modals: tracked uppercase makes long forms scan
 * faster (each section header reads as a label, not body copy). */
.modal-body .form-label {
    font-weight: 500;
    font-size: 0.85rem;
    margin-bottom: 0.35rem;
    color: var(--bs-body-color);
}
.modal-body .form-text {
    font-size: 0.78rem;
    margin-top: 0.3rem;
    line-height: 1.35;
}

/* Tighter `.mb-3` spacing inside modals — Bootstrap's 1rem default is
 * generous and makes 4-5 fields feel scrolling-required. 0.85rem still
 * separates fields cleanly without the airy gap. */
.modal-body .mb-3 {
    margin-bottom: 0.85rem !important;
}

/* Demote the Cancel/dismiss button to a ghost so the primary action
 * (Start Job, Save, etc.) gets the visual weight it deserves. Templates
 * already use `btn-secondary` for dismiss; this CSS override avoids
 * having to re-class every modal footer in every template. */
.modal-footer .btn-secondary[data-bs-dismiss="modal"] {
    background-color: transparent;
    border-color: transparent;
    color: var(--bs-secondary-color);
    font-weight: 500;
}
.modal-footer .btn-secondary[data-bs-dismiss="modal"]:hover,
.modal-footer .btn-secondary[data-bs-dismiss="modal"]:focus-visible {
    background-color: var(--surface-2, rgba(255, 255, 255, 0.06));
    border-color: transparent;
    color: var(--bs-body-color);
}

/* ──────────────────────────────────────────────────────────────────────
 * Toasts — bottom-right floating; tone down the loud border colour
 * the default uses. Add the elevation shadow so they read as floating.
 * ────────────────────────────────────────────────────────────────────── */
.toast {
    border-radius: var(--radius);
    box-shadow: var(--shadow-2);
}

body {
    min-height: 100vh;
}

/* The navbar flips with the page theme. Dark mode keeps the deep Plex
   background; light mode uses Bootstrap's tertiary body surface for a
   clean header bar. Brand mark stays Plex-orange in both themes. */
html[data-bs-theme="dark"] .navbar {
    background-color: var(--plex-dark) !important;
}

html[data-bs-theme="light"] .navbar {
    background-color: var(--bs-tertiary-bg);
}

.navbar-brand {
    color: var(--plex-orange) !important;
    font-weight: 600;
    font-size: 18px;
}
/* Amber on the light navbar is 2.1:1; the brand takes the dark amber text token (6.6:1) there. */
html[data-bs-theme="light"] .navbar-brand {
    color: var(--plex-orange-text) !important;
}

/* The logo mark in the nav brand: optically centred on the wordmark's x-height. */
.navbar-brand-mark {
    vertical-align: -6px;
}

/* Same mark, reused in the offcanvas menu header (h5.offcanvas-title, smaller image,
   different line-height) — tuned separately so it doesn't inherit the navbar's offset. */
.offcanvas-brand-mark {
    vertical-align: -4px;
}

/* Top-right utility icons (notification bell, sponsor heart + theme toggle) — give them a
 * unified circular hover affordance so they read as first-class controls
 * instead of bare text links. Matches the .icon-btn-group treatment used
 * inside tables.
 *
 * Desktop-only — below 1200px the same buttons collapse INTO
 * the hamburger menu and take full-row treatment with text labels (see
 * the ``.navbar-nav-utility`` block in the mobile media query below). The
 * 38×38 circle is the wrong shape for an accordion row.
 */
@media (min-width: 1200px) {
    /* One shared 34px icon button (bell, help, coffee, theme). Hover is a neutral tint: amber is reserved for the
     * primary action, the next schedule and job-ID links. */
    .navbar .nav-item > .nav-ibtn {
        width: 34px;
        height: 34px;
        display: inline-flex !important;
        align-items: center;
        justify-content: center;
        padding: 0 !important;
        border-radius: var(--radius-ctl);
        color: var(--muted) !important;
        transition: background-color 0.15s ease, color 0.15s ease;
    }
    .navbar .nav-item > .nav-ibtn:hover,
    .navbar .nav-item > .nav-ibtn:focus-visible {
        background-color: var(--inset);
        color: var(--bs-body-color) !important;
    }
    .navbar .nav-item > .nav-ibtn .bi {
        font-size: 1.05rem;
    }
    /* Hairline dividers: primary nav | {bell, help} | {coffee, Star, theme} | Logout. */
    .navbar-nav-utility > .nav-item:nth-child(2),
    .navbar-nav-utility > .nav-item:nth-child(4) {
        margin-left: 0.125rem;
        padding-left: 0.25rem;
        border-left: 1px solid var(--line);
    }
    .navbar-nav-utility {
        align-items: center;
    }

    /* Without a floor the Help menu's longest label sets the menu width and ends up only ~8px from the trailing
     * external-link glyph while every other row gets 20-49px. Pin a min-width so
     * the whole menu breathes instead of hugging its longest item.
     *
     * Desktop-only: below 1200px this same <ul> renders as a full-width accordion
     * row inside the offcanvas drawer (min(82vw, 360px)), where a forced 15rem
     * would overflow horizontally on a ~320px viewport. */
    .dropdown-menu[aria-labelledby="helpMenuBtn"] {
        min-width: 15rem;
    }
}

/* Ko-fi coffee cup — the docs site's pink, fixed in both themes because it's a
 * brand colour, not a semantic one. Declared on the icon itself so it survives
 * the utility-icon colour rule above, which sets the parent <a> colour with
 * !important; an own colour on the icon wins over the inherited one. */
.sponsor-cup {
    color: #db61a2;
}

a:hover > .sponsor-cup,
a:focus-visible > .sponsor-cup {
    color: #bf3989;
}

/* Star — gold, as on the docs site. */
.navbar-star {
    color: var(--plex-orange);
}

@media (prefers-reduced-motion: no-preference) {
    #sponsorLinkBtn .sponsor-cup,
    #navStarBtn .navbar-star {
        transition: transform 0.15s ease;
    }
    #sponsorLinkBtn:hover .sponsor-cup,
    #navStarBtn:hover .navbar-star {
        transform: scale(1.15);
    }
}

/* Desktop Star: a small outlined pill with its word, like the docs nav, so it doesn't read as
 * "favourite this page". In the phone menu it is a plain labelled row like its neighbours. */
@media (min-width: 1200px) {
    .navbar .nav-item > #navStarBtn {
        height: 34px;
        margin: 0 0.25rem;
        padding: 0 0.6rem !important;
        gap: 0.35rem;
        border: 1px solid var(--line);
        border-radius: var(--radius-ctl);
        font-size: 0.85rem;
        color: var(--bs-body-color) !important;
    }
    .navbar .nav-item > #navStarBtn:hover,
    .navbar .nav-item > #navStarBtn:focus-visible {
        background-color: var(--inset);
        border-color: var(--faint);
    }
    /* Logout is a ghost button that turns red on hover: it ends the session. */
    .navbar-nav-utility form > #navLogoutBtn {
        height: 34px;
        display: inline-flex;
        align-items: center;
        padding: 0 0.5rem !important;
        color: var(--muted) !important;
    }
    .navbar-nav-utility form > #navLogoutBtn:hover,
    .navbar-nav-utility form > #navLogoutBtn:focus-visible {
        background-color: var(--bad-soft);
        color: var(--bad) !important;
    }
}

/* Extras drop before anything structural as the bar narrows, as on the docs site. Below 1400px
 * the Star pill keeps its outline and loses its word (it still has an aria-label and a title),
 * and the main links lose their icons but keep their words. That leaves ~60px spare at 1200px
 * even in DejaVu Sans, a wide font Linux falls back to; below 1200px the bar is the phone menu. */
.navbar-star-word {
    display: none;
}
@media (min-width: 1400px) {
    .navbar-star-word {
        display: inline;
    }
}
@media (min-width: 1200px) and (max-width: 1399.98px) {
    .navbar .nav-item > #navStarBtn {
        width: 34px;
        justify-content: center;
        padding: 0 !important;
    }
    .navbar-nav.me-auto .nav-link > .bi:first-child {
        display: none;
    }
}

/* The running version, stacked under the wordmark (see base.html). The wordmark's line box is
 * tightened so wordmark + version fit the height the brand had alone, keeping the bar 57px. */
.navbar-brand-group {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    min-width: 0;
    max-width: 100%;
    margin-right: 1rem;
}
/* max-width + ellipsis: on a 320px phone with a wide font the wordmark alone is wider than the
 * row, and would otherwise scroll the page sideways. */
.navbar-brand-group .navbar-brand {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    line-height: 1.25;
}
/* A phone row leaves the wordmark ~240-330px beside the hamburger; at the desktop size it only
 * fits the widest phones, so it steps down (and with the width on the smallest) before the
 * ellipsis has to cut it. */
@media (max-width: 575.98px) {
    .navbar-brand-group {
        margin-right: 0.5rem;
    }
    .navbar-brand-group .navbar-brand {
        font-size: min(1.05rem, 4.9vw);
    }
}
.navbar-version {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    max-width: 100%;
    /* Under the wordmark, past the 26px mark and its .me-2 gap. */
    margin-left: calc(26px + 0.5rem);
    font-size: 0.72rem;
    line-height: 1.2;
    color: var(--bs-secondary-color);
    text-decoration: none;
    white-space: nowrap;
}
.navbar-version:hover,
.navbar-version:focus-visible {
    color: var(--plex-orange-text);
    text-decoration: underline;
}
#navVersionText {
    overflow: hidden;
    text-overflow: ellipsis;
}
.navbar-version-dot {
    flex: none;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--plex-orange);
}

/* Notification bell dropdown in the top-right navbar. */
.notification-bell-wrapper {
    position: relative;
}

.notification-bell-wrapper .dropdown-menu.notification-dropdown {
    width: 28rem;
    max-width: 95vw;
    max-height: 70vh;
    overflow-y: auto;
    border-radius: var(--radius);
    box-shadow: var(--shadow-2);
    margin-top: 0.4rem;
}

/* Polished header — uppercase tracked label, brand-orange "Reset" link
 * so it reads as a real action, not a faded utility. */
.notification-bell-wrapper .dropdown-menu.notification-dropdown > .d-flex {
    background: var(--surface-1, rgba(255, 255, 255, 0.04));
}
.notification-bell-wrapper .dropdown-menu.notification-dropdown strong {
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--bs-secondary-color);
}
.notification-bell-wrapper #notificationResetBtn {
    color: var(--plex-orange-text) !important;
    font-weight: 500;
    font-size: 0.8rem;
}
.notification-bell-wrapper #notificationResetBtn:hover {
    color: var(--plex-orange-text) !important;
    text-decoration: underline !important;
}

/* Empty state — lift the muted "No notifications." line so the dropdown
 * doesn't feel barren when there's nothing to show. */
.notification-bell-wrapper #notificationEmpty {
    padding: 2.25rem 1rem !important;
    color: var(--bs-secondary-color);
}

.notification-bell-wrapper .notification-entry {
    transition: background-color 0.15s ease;
}

.notification-bell-wrapper .notification-entry:hover {
    background-color: var(--bs-tertiary-bg);
}

.notification-bell-wrapper .notification-entry .notification-details code {
    word-break: break-word;
}

/* The notification card's "What changed" disclosure uses a <summary> tag.
   Inline style="cursor:pointer;" gets stripped by sanitizeNotificationHtml
   (no `style` attribute on the allow-list), so wire the cursor hint via a
   class that *does* survive sanitization. */
.notification-bell-wrapper .notification-details summary {
    cursor: pointer;
}

.notification-bell-wrapper .notification-list .notification-entry:last-child {
    border-bottom: 0 !important;
}

/* Dark theme — custom overrides apply only when data-bs-theme=dark. Light
   mode falls back to Bootstrap 5.3's built-in light theme, which handles
   cards/modals/forms/tables natively. */
html[data-bs-theme="dark"] body {
    background-color: var(--plex-gray);
}

html[data-bs-theme="dark"] .card {
    background-color: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
}

html[data-bs-theme="dark"] .card-header {
    background-color: rgba(0, 0, 0, 0.2);
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    font-weight: 500;
}

html[data-bs-theme="light"] .card-header {
    font-weight: 500;
}

/* Progress bars */
.progress {
    height: 1.5rem;
}

html[data-bs-theme="dark"] .progress {
    background-color: rgba(255, 255, 255, 0.1);
}

.progress-bar {
    transition: width 0.3s ease;
}
@media (prefers-reduced-motion: reduce) {
    .progress-bar { transition: none; }
}

/* Tables */
html[data-bs-theme="dark"] .table {
    color: var(--bs-body-color);
}

html[data-bs-theme="dark"] .table-hover tbody tr:hover {
    background-color: rgba(255, 255, 255, 0.05);
}

/* Buttons */
.btn-warning {
    background-color: var(--plex-orange);
    border-color: var(--plex-orange);
    color: #000;
}

.btn-warning:hover {
    background-color: #c98b0b;
    border-color: #c98b0b;
    color: #000;
}

/* Tooltip content: preserve newlines for multi-line outcome breakdowns */
.tooltip-inner {
    white-space: pre-line;
    text-align: left;
}

/* GPU cards — blue-tinted accent that fits both themes */
html[data-bs-theme="dark"] .gpu-card {
    background: linear-gradient(135deg, rgba(13, 110, 253, 0.2), rgba(13, 110, 253, 0.05));
    border: 1px solid rgba(13, 110, 253, 0.3);
}

html[data-bs-theme="light"] .gpu-card {
    border: 1px solid rgba(13, 110, 253, 0.2);
}

/* Library list */







/* Toast notifications */
html[data-bs-theme="dark"] .toast {
    background-color: var(--panel-2);
    border: 1px solid rgba(255, 255, 255, 0.1);
}

/* Modals */
html[data-bs-theme="dark"] .modal-content {
    background-color: var(--panel-2);
    border: 1px solid rgba(255, 255, 255, 0.1);
}

html[data-bs-theme="dark"] .modal-header {
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

html[data-bs-theme="dark"] .modal-footer {
    border-top: 1px solid rgba(255, 255, 255, 0.1);
}

/* Form controls */
html[data-bs-theme="dark"] .form-control,
html[data-bs-theme="dark"] .form-select {
    background-color: rgba(0, 0, 0, 0.3);
    border-color: rgba(255, 255, 255, 0.2);
    color: var(--bs-body-color);
}

html[data-bs-theme="dark"] .form-control:focus,
html[data-bs-theme="dark"] .form-select:focus {
    background-color: rgba(0, 0, 0, 0.4);
    border-color: var(--plex-orange);
    color: var(--bs-body-color);
    box-shadow: 0 0 0 0.25rem rgba(229, 160, 13, 0.25);
}

html[data-bs-theme="dark"] .form-control::placeholder {
    color: #6c757d;
}

html[data-bs-theme="light"] .form-control:focus,
html[data-bs-theme="light"] .form-select:focus {
    box-shadow: 0 0 0 0.25rem rgba(229, 160, 13, 0.25);
}

/* Alerts */
.alert {
    border: none;
}

/* Code blocks — brand-colored in both themes */
code {
    padding: 0.125rem 0.375rem;
    border-radius: 0.25rem;
    color: var(--plex-orange);
}

html[data-bs-theme="dark"] code {
    background-color: rgba(0, 0, 0, 0.3);
}

html[data-bs-theme="light"] code {
    /* Cool slate chip, not orange. Brand orange on a near-white bg
       reads as a warning state and clashes with the slate palette;
       slate-100 bg + slate-700 text matches the rest of the surface
       family. Dark mode keeps the orange because it pops on navy. */
    background-color: #e2e8f0;          /* slate-200 */
    color: #334155;                     /* slate-700 */
}

/* Scrollbar */
::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

html[data-bs-theme="dark"] ::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.2);
}

html[data-bs-theme="dark"] ::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.2);
    border-radius: 4px;
}

html[data-bs-theme="dark"] ::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.3);
}

html[data-bs-theme="light"] ::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.05);
}

html[data-bs-theme="light"] ::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.2);
    border-radius: 4px;
}

html[data-bs-theme="light"] ::-webkit-scrollbar-thumb:hover {
    background: rgba(0, 0, 0, 0.3);
}

/* Animations */
@keyframes pulse {
    0%, 100% {
        opacity: 1;
    }
    50% {
        opacity: 0.5;
    }
}

.pulse {
    animation: pulse 2s infinite;
}

/* Retry-waiting active-job card — subtle amber border + tinted bg
   so the card visibly differs from a normally-running job at a glance,
   without being so loud it competes with actually-failing jobs. */
.active-job-card.retry-waiting {
    border-color: rgba(255, 193, 7, 0.55) !important;
    background-color: rgba(255, 193, 7, 0.06);
}

[data-bs-theme="dark"] .active-job-card.retry-waiting {
    background-color: rgba(255, 193, 7, 0.08);
}

/* ──────────────────────────────────────────────────────────────────────
 * Mobile navbar polish — when the hamburger collapses the navbar into a
 * vertical stack, the right-side utility cluster (Notifications / Help /
 * Theme / Logout) used to render as bare icons with no labels. Pre-fix
 * the user opened the menu and saw 3 mystery glyphs in a row before
 * Logout. The template now ships a ``d-xl-none`` text label inside each
 * utility button; this CSS makes those rows align with the primary nav
 * (Dashboard / Servers / …) so everything reads as one consistent menu.
 *
 * The Bootstrap dropdown menus inside the collapsed navbar (Automation,
 * Tools, Help, Notifications) render flush to the left so they look
 * like an inline accordion instead of a popover detached from the row.
 * ────────────────────────────────────────────────────────────────────── */
@media (max-width: 1199.98px) {
    /* The navbar uses ``backdrop-filter: saturate(1.8) blur(14px)``
     * for the frosted-glass look on desktop, but ``backdrop-filter``
     * establishes a fixed-positioning containing block — meaning any
     * ``position: fixed`` child (like our offcanvas drawer) gets
     * clipped to the navbar's 56px height instead of spanning the
     * full viewport. Strip the filter on mobile so the offcanvas
     * slides over the whole screen as it should. The visual loss is
     * minimal because the drawer covers the navbar anyway. Without the
     * blur the page scrolling under the pinned navbar would show through
     * its translucent background, so it goes opaque here. */
    nav.navbar.sticky-top {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }
    html[data-bs-theme="dark"] nav.navbar.sticky-top {
        background-color: rgb(15, 15, 26) !important;
    }
    html[data-bs-theme="light"] nav.navbar.sticky-top {
        background-color: #f4f6f8;
    }

    /* One row: only the brand and the hamburger are in flow here (the menu is the fixed drawer),
     * so the brand shrinks and ellipsizes rather than pushing the hamburger onto a second row
     * of a pinned bar. */
    nav.navbar > .page-shell {
        flex-wrap: nowrap;
    }

    /* Hamburger toggle — opens the offcanvas drawer. Single ≡ glyph;
     * the ✕ close button lives inside the drawer header (where it has
     * room and reads as a proper dismiss). Tint the toggler in the
     * brand colour as a subtle press-state cue.
     *
     * Padding sized so the rendered button is ≥44px tall (Apple HIG
     * minimum touch target). Smaller and thumb taps land on the wrong
     * area on a moving phone.
     */
    .navbar-toggler {
        transition: background-color 0.15s ease, color 0.15s ease;
        line-height: 1;
        padding: 0.6rem 0.75rem;
        min-height: 44px;
        min-width: 44px;
    }
    .navbar-toggler .navbar-toggler-glyph {
        font-size: 1.4rem;
        display: inline-block;
        line-height: 1;
    }
    .navbar-toggler:hover,
    .navbar-toggler:focus-visible {
        background-color: var(--surface-2, rgba(255, 255, 255, 0.08));
        color: var(--plex-orange);
    }

    /* Bigger touch targets (~44px tall) — Apple HIG minimum. The
     * default Bootstrap nav-link padding rendered ~36px which is
     * tappable but cramped, especially with multi-row clusters. The
     * mobile menu lives inside an offcanvas drawer (#navbarNav with
     * .offcanvas class below lg), so all selectors target the
     * offcanvas surface. */
    #navbarNav.offcanvas .navbar-nav .nav-link {
        padding-top: 0.7rem;
        padding-bottom: 0.7rem;
    }
    #navbarNav.offcanvas .navbar-nav .nav-link .bi {
        width: 1.2rem;
        text-align: center;
        flex-shrink: 0;
    }
    /* Stretch every utility button to fill the menu row so the inline
     * label can lay out alongside the icon without wrapping. Without
     * ``width: 100%`` Bootstrap's ``btn`` styling keeps the button at
     * ``inline-block`` width — about 38px (just the icon) — and the
     * label gets squeezed to one character per line. */
    .navbar-nav-utility .nav-link {
        display: flex !important;
        align-items: center;
        width: 100%;
        padding: 0.7rem 0;
        text-align: left;
    }
    .navbar-nav-utility .nav-link .bi {
        width: 1.2rem;
        text-align: center;
        flex-shrink: 0;
    }
    /* Logout: visually demote so it doesn't compete with primary nav.
     * Smaller font, lighter weight, muted by default — still highly
     * tappable but reads as a footer / account-level action. */
    .navbar-nav-utility .nav-item:last-child .nav-link {
        font-size: 0.875rem;
        opacity: 1;
        color: var(--muted);
    }
    .navbar-nav-utility .nav-item:last-child .nav-link:hover,
    .navbar-nav-utility .nav-item:last-child .nav-link:focus-visible {
        opacity: 1;
        color: var(--bs-danger);
    }
    /* Notification badge sits AFTER the icon+label row on desktop (top-right
     * of the bell). On mobile we move it inline with the label instead of
     * floating absolutely so it sits next to "Notifications" cleanly. */
    .navbar-nav-utility .notification-bell-wrapper #notificationBellBadge {
        position: static !important;
        transform: none !important;
        margin-left: auto;
    }
    /* Dropdown menus inside the offcanvas drawer render full-width and
     * borderless so they read as nested rows of the accordion, not as a
     * floating popover. */
    #navbarNav.offcanvas .dropdown-menu {
        position: static;
        float: none;
        border: 0;
        box-shadow: none;
        background: transparent;
        padding-left: 1.25rem;
    }
    #navbarNav.offcanvas .dropdown-menu .dropdown-item {
        padding-left: 0.5rem;
        padding-top: 0.55rem;
        padding-bottom: 0.55rem;
    }
    /* Hide the inline ``<hr class="dropdown-divider">`` separators in
     * accordion-mode dropdowns — desktop uses them to chunk visually-
     * distinct groups (Schedules vs Webhooks vs catch-all) inside a
     * floating popover, but on mobile they make a single accordion
     * group look like three disjoint sections. The label text alone
     * carries the grouping; the dividers add only visual noise. */
    #navbarNav.offcanvas .dropdown-menu .dropdown-divider {
        display: none;
    }

    /* Offcanvas drawer surface — slide in from the right with a comfy
     * width that matches typical iOS app drawers (75-85% of viewport).
     * Pre-fix the default Bootstrap offcanvas was ~400px which on a
     * 390px iPhone equalled the entire screen. */
    #navbarNav.offcanvas-end {
        width: min(82vw, 360px);
        background-color: var(--bs-body-bg);
    }
    /* Drawer header — title + close button. Already styled by Bootstrap;
     * just make sure the close button is large enough to tap reliably. */
    #navbarNav .offcanvas-header .btn-close {
        padding: 0.85rem;
    }
    #navbarNav .offcanvas-body {
        padding-top: 0.5rem;
    }
}

/* Responsive adjustments */
@media (max-width: 768px) {
    .table-responsive { font-size: 0.875rem; }

    /* Tighter card padding on phones so the cards don't waste a third
       of the visible area on chrome. */
    .card-body { padding: 0.85rem; }
    .card-header { padding: 0.7rem 0.9rem; }

    /* Page header collapses to a single column when the action group
       wraps below the title. */
    .page-header { gap: 0.75rem; }
    .page-header h1, .page-header .page-title { font-size: 1.4rem; }
    .page-header .page-subtitle { font-size: 0.875rem; }
}

/* Log viewer */
.log-viewer {
    font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
    white-space: pre-wrap;
    scroll-behavior: smooth;
    word-break: break-all;
    /* Tighter line-height so consecutive ``[time] INFO -`` lines pack
       more densely — pre-fix the 1.5 default left a visible blank gap
       between every line that read as "log entries are far apart"
       rather than "scannable stream". */
    line-height: 1.35;
}

/* ``padding: 0`` on log-line so the new line-height controls density.
   The 1 px y-padding previously here partially defeated the line-height
   tightening above. */
.log-line { display: block; padding: 0; }
html[data-bs-theme="dark"] .log-line:hover { background-color: rgba(255, 255, 255, 0.05); }
html[data-bs-theme="light"] .log-line:hover { background-color: rgba(0, 0, 0, 0.05); }
.log-level-debug { color: #6c757d; }
.log-level-info { color: #20c997; }
.log-level-warning { color: #ffc107; }
.log-level-error { color: #dc3545; }
.log-level-critical { color: #dc3545; font-weight: bold; }
/* Light mode: bright neon green/yellow on white is fatiguing. Drop
 * to deeper hues so long log scans don't tire the eye. */
html[data-bs-theme="light"] .log-level-info { color: #0e8060; }
html[data-bs-theme="light"] .log-level-warning { color: #b88600; }
html[data-bs-theme="light"] .log-level-error { color: #b02a37; }
html[data-bs-theme="light"] .log-level-critical { color: #b02a37; }
.log-highlight { background-color: rgba(229, 160, 13, 0.3); border-radius: 2px; }
.log-line-hidden { display: none; }
.log-nav-btn { padding: 0.15rem 0.35rem; line-height: 1; font-size: 0.8rem; backdrop-filter: blur(4px); }
.log-nav-btn:hover { opacity: 1 !important; }

/* ──────────────────────────────────────────────────────────────────────
 * Retry-chain Attempts pill row (Job Details modal)
 * ────────────────────────────────────────────────────────────────────── */
.attempts-bar {
    /* Groups label + pills + chain-state chip into one tidy row.
       Subtle bg + border reads as "context block" without the
       alert-warning shoutiness the prior verbose banner had. */
    background: var(--bs-tertiary-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.375rem;
    padding: 0.5rem 0.75rem;
}
.attempts-state-chip {
    /* Right-aligned chip in the attempts bar — countdown / running /
       outcome. Sized to read as scannable context, not a primary CTA. */
    font-size: 0.75rem;
    padding: 0.35rem 0.55rem;
    font-weight: 500;
    align-self: center;
    white-space: nowrap;
}
.attempts-pill {
    /* Compact pill: just the attempt number + status glyph. Status colour
       comes from Bootstrap's btn-outline-{success,danger,...} classes
       which the JS swaps to solid btn-* on the active pill. */
    min-width: 2.5rem;
    padding: 0.25rem 0.55rem;
    font-size: 0.8rem;
    font-weight: 500;
    line-height: 1.2;
    transition: transform 0.08s ease-out, box-shadow 0.08s ease-out;
}
.attempts-pill:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.attempts-pill.active {
    /* Solid (filled) when active so the current selection is unmistakable
       even amongst many same-coloured "completed" pills. */
    font-weight: 600;
    box-shadow: 0 0 0 2px rgba(var(--bs-primary-rgb, 13, 110, 253), 0.25);
}
/* The "Original" pill — distinct width + a divider after it so the eye
   reads "first run | then retries" instead of a flat sequence. */
.attempts-pills > .attempts-pill[data-is-originating="1"] {
    min-width: 5rem;
}
.attempts-pills > .attempts-pill[data-is-originating="1"]::after {
    content: "";
    border-left: 1px solid var(--bs-border-color);
    margin-left: 0.5rem;
    padding-left: 0;
    height: 1.2em;
}

/* ──────────────────────────────────────────────────────────────────────
 * Mobile-responsive job-modal layout (< 768 px)
 * Survivor of the Tier 2.10 work after the simplification pass dropped
 * the Overview tab + timeline + log-level filter blocks above.
 * ────────────────────────────────────────────────────────────────────── */
@media (max-width: 767.98px) {
    /* The 5-column Files table doesn't fit a phone viewport without
       horizontal scroll. Bootstrap's table-responsive idiom is fine
       here — Details + Worker columns are secondary. */
    #fileResultsTable {
        min-width: 540px;
    }
    /* The 500 px-tall logs/files panes are too much vertical real
       estate on a phone where the modal is full-screen. Drop to a
       proportional cap. */
    #logsContent,
    #fileResultsTable + div {
        max-height: 60vh !important;
    }
    /* Modal footer with the operator-action buttons (Retry now / Cancel
       chain / Open BIF) + Copy / Download / Refresh / Close needs to
       stack. The footer is flex-wrap already; reduce padding so
       stacked buttons fit without pushing content off-screen. */
    .modal-footer.flex-wrap > .btn,
    .modal-footer.flex-wrap > a {
        flex: 1 1 calc(50% - 0.5rem);
        min-width: 0;
    }
    .modal-footer.flex-wrap > .btn-secondary[data-bs-dismiss="modal"] {
        flex: 1 1 100%;  /* Close gets its own row at the bottom */
    }
    /* Pill row pills shrink slightly so 5+ pills still fit one row. */
    .attempts-pill {
        min-width: 2rem;
        padding: 0.2rem 0.4rem;
        font-size: 0.75rem;
    }
}

/* Dropdown menus (dark theme only — light mode uses Bootstrap defaults) */
html[data-bs-theme="dark"] .dropdown-menu {
    background-color: var(--panel-2);
    border: 1px solid rgba(255, 255, 255, 0.1);
}

/* Dropdown items — Bootstrap defaults the .active state to bright blue
 * (#0d6efd) with white text, which spikes against the calm light theme
 * and clashes with brand orange. Use the same orange-tint pattern as
 * .nav-link.active for consistency: soft orange bg + brand orange text.
 * Dark mode keeps the same pattern (same vars). */
.dropdown-item.active,
.dropdown-item:active {
    background-color: var(--plex-orange-soft) !important;
    color: var(--plex-orange-text) !important;
    font-weight: 600;
}
.dropdown-item:hover,
.dropdown-item:focus {
    background-color: var(--surface-2);
    color: var(--bs-body-color);
}

html[data-bs-theme="dark"] .dropdown-menu .form-check-label {
    color: var(--bs-body-color);
}

/* ──────────────────────────────────────────────────────────────────────
 * Icon button groups — used for table-row action clusters (View logs /
 * Re-run / Delete on the Job Queue, Edit / Refresh / Delete on the
 * Servers + Schedules pages). The default Bootstrap btn-sm is too tall
 * for a dense row; we lock the height + width to a 32px square so the
 * rows feel uniform. The btn-group also strips inter-button margin so
 * each cluster sits as one cohesive control instead of three loose
 * icons.
 * ────────────────────────────────────────────────────────────────────── */
.icon-btn-group .btn {
    width: 2rem;
    height: 2rem;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

.icon-btn-group .btn .bi {
    font-size: 0.95rem;
    line-height: 1;
}

/* ──────────────────────────────────────────────────────────────────────
 * Jobs table — compact row layout with breathing space at the
 * uppercase column headers + a subtle hover. Header gets the same
 * tracking treatment used on the Schedules teaser badge so the page
 * has one consistent micro-typography rhythm.
 * ────────────────────────────────────────────────────────────────────── */
/* Brief highlight flash applied when the pending-webhooks chip
 * scrolls the user to the relevant row. Class is added in JS
 * (loadPendingWebhooks click handler) and removed after 1.5s so the
 * row settles back to normal — long enough to land the eye, short
 * enough to not pulse permanently. */
.queue-row-flash {
    animation: queueRowFlash 1.4s ease-out;
}
@keyframes queueRowFlash {
    0% { background-color: rgba(255, 193, 7, 0.35); }
    100% { background-color: transparent; }
}

.jobs-table thead th {
    border-top: 0;
    letter-spacing: 0.04em;
    font-weight: 600;
    font-size: 0.72rem;
    padding-top: 0.65rem;
    padding-bottom: 0.65rem;
    background-color: transparent;
}

.jobs-table tbody td {
    padding-top: 0.55rem;
    padding-bottom: 0.55rem;
}

/* Sticky-feel left padding on first/last columns so action icons don't
   touch the card edge. */
.jobs-table thead th:first-child,
.jobs-table tbody td:first-child { padding-left: 1rem; }
.jobs-table thead th:last-child,
.jobs-table tbody td:last-child { padding-right: 1rem; }

html[data-bs-theme="dark"] .jobs-table tbody tr:hover { background-color: rgba(255,255,255,0.04); }
html[data-bs-theme="light"] .jobs-table tbody tr:hover { background-color: rgba(0,0,0,0.03); }

/* ──────────────────────────────────────────────────────────────────────
 * Mobile job-queue layout: convert each table row into a stacked
 * card-style block so the dashboard isn't a 600px-wide horizontal
 * scroller on a phone. The table header collapses to ARIA-only and
 * each cell renders as a labelled key/value strip. Detail row
 * (publishers, file lists) expands inline below.
 *
 * Triggers below the .table-responsive scroll's normal threshold so
 * tablets that comfortably fit the table keep the table view; phones
 * (sm and down) get the stacked layout.
 * ────────────────────────────────────────────────────────────────────── */
@media (max-width: 575.98px) {
    .jobs-table thead { display: none; }
    .jobs-table,
    .jobs-table tbody {
        display: block;
        width: 100%;
    }
    /* Each row becomes a card. CSS-grid gives us a deliberate two-row
     * layout instead of 4-5 stacked flex rows:
     *
     *   ┌─────────────────────────┬──────────┐
     *   │ TITLE  + Origin pill    │ chevron  │   row 1  (.title)
     *   │ Status · Created        │ Actions  │   row 2  (.meta / .actions)
     *   │ ███████ progress bar  100%         │   row 3  (.progress, full)
     *   └─────────────────────────┴──────────┘
     *
     * The title cell already contains the origin pill + chevron on
     * the desktop layout — we just let it span the full grid row on
     * mobile. Status and Created are merged on a single horizontal row
     * via inline-flex inside the status td (see below). Actions sits
     * to the right of meta. Progress takes the full bottom row.
     */
    /* Mobile rows render as flat list items inside the parent JOB QUEUE
     * card. Pre-fix each row had its OWN ``var(--surface-1)`` bg + 1px
     * border + radius — stacked inside the parent card's identical
     * surface, this produced the "card-in-card" + double-shading look
     * the user flagged. We drop the per-row bg/border/radius and use a
     * single thin divider between rows so the parent card defines the
     * surface and rows read as flat list items. */
    .jobs-table tbody tr {
        display: grid;
        grid-template-columns: auto 1fr auto;
        grid-template-areas:
            "title    title    title"
            "status   created  actions"
            "progress progress progress";
        gap: 0.45rem 0.55rem;
        align-items: center;
        margin: 0;
        background: transparent;
        border: 0;
        border-bottom: 1px solid var(--surface-border);
        border-radius: 0;
        padding: 0.85rem 0.25rem;
        position: relative;
    }
    .jobs-table tbody tr:last-child {
        border-bottom: 0;
    }
    .jobs-table tbody tr:hover {
        background-color: transparent;
    }
    .jobs-table tbody tr > td {
        display: block;
        border: none !important;
        padding: 0;
        line-height: 1.3;
    }

    /* Cell → grid-area assignment by DOM position (hidden cells still
     * occupy nth-child slots). Status (3) and Created (6) sit on the
     * middle row in their own columns so they read as a single
     * inline meta line. Actions cluster pins to the right. */
    .jobs-table tbody tr > td:nth-child(2) {
        grid-area: title;
        font-weight: 500;
        /* Long release-style titles like
         * "Peacemaker (2022) - S01E02 - Best Friends for Never [HMAX
         * MULTi WEBDL-2160p][DV HDR10][EAC3 5.1][FR+EN][h265]-BONOBO.mkv"
         * used to break mid-word and steal 6+ rows of vertical space.
         * Clamp to 2 lines + word-wrap so they ellipsize cleanly and
         * the badge row (Sonarr / Retry N/M) gets breathing room. */
        overflow: hidden;
        overflow-wrap: anywhere;
        word-break: normal;
    }
    .jobs-table tbody tr > td:nth-child(2) > a:first-child {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
        line-height: 1.3;
    }
    .jobs-table tbody tr > td:nth-child(3) { grid-area: status; }
    .jobs-table tbody tr > td:nth-child(5) { grid-area: progress; }
    .jobs-table tbody tr > td:nth-child(7) {
        grid-area: actions;
        justify-self: end;
        align-self: center;
    }

    /* Hide the progress bar on mobile for terminal job states — the
     * coloured status dot ("● Completed", "● Cancelled", "● Failed")
     * already conveys the outcome, so a 100% bar adds nothing but
     * vertical noise. Keep it for running/pending where the live %
     * is the point. */
    .jobs-table tbody tr > td:nth-child(5):has(.progress[data-status="completed"]),
    .jobs-table tbody tr > td:nth-child(5):has(.progress[data-status="cancelled"]),
    .jobs-table tbody tr > td:nth-child(5):has(.progress[data-status="failed"]) {
        display: none;
    }

    /* Force-show "Created" on mobile (it's d-none d-lg-table-cell on
     * desktop) so users get the row metadata they were missing. */
    .jobs-table tbody tr > td:nth-child(6) {
        display: block !important;
        grid-area: created;
        font-size: 0.78rem;
        color: var(--bs-secondary-color);
    }

    /* Detail (expanded files / publishers) row: full-width block
     * spanning the whole grid. */
    .jobs-table tbody tr.job-files-detail {
        display: block;
        margin-top: -0.4rem;
        padding-top: 0.4rem;
    }
    .jobs-table tbody tr.job-files-detail > td {
        display: block;
        padding: 0;
    }

    /* Action cluster aligned to the right of the meta row. */
    .jobs-table tbody tr > td.text-end {
        text-align: right;
    }

    /* Progress bar fills the bottom row. */
    .jobs-table tbody tr > td > .progress {
        width: 100%;
    }


    /* Trigger Activity Log table on /automation. Use display:inline on
     * the meta cells (Time, Source, Server) so they flow as a single
     * line below the Title; Title gets display:block on top, Status
     * pill floats to the right of the title row. */
    #historyTable thead { display: none; }
    #historyTable,
    #historyTable tbody { display: block; width: 100%; }
    #historyTable tbody tr {
        display: block;
        margin-bottom: 0.55rem;
        background: var(--surface-1);
        border: 1px solid var(--surface-border);
        border-radius: var(--radius);
        padding: 0.6rem 0.8rem;
        position: relative;
    }
    #historyTable tbody tr > td {
        display: inline;
        border: none !important;
        padding: 0;
        font-size: 0.78rem;
        color: var(--bs-secondary-color);
    }
    /* Title: own row, prominent. */
    #historyTable tbody tr > td:nth-child(4) {
        display: block;
        font-weight: 500;
        font-size: 0.9rem;
        color: var(--bs-body-color);
        margin-bottom: 0.3rem;
        padding-right: 5rem;  /* leave room for the absolutely-positioned status pill */
    }
    /* Status pill: pinned top-right. */
    #historyTable tbody tr > td:nth-child(5) {
        position: absolute;
        top: 0.6rem;
        right: 0.8rem;
    }
    /* Meta cells (Time, Source, Server) flow inline with " · " separators. */
    #historyTable tbody tr > td:nth-child(2)::after,
    #historyTable tbody tr > td:nth-child(3)::after {
        content: " · ";
        margin: 0 0.3rem;
        opacity: 0.5;
    }
}

/* Numeric inputs that pair a stepper-wrapped input with a unit addon
 * (e.g. Settings → Logging → "Rotation Size [10] MB") wrap onto two
 * lines on mobile because the inner stepper-input-group claims the
 * full row, pushing the "MB" / "files" addon below. Keep the outer
 * input-group on one row so the unit addon stays inline. */
.input-group:has(> .input-group),
.input-group:has(> .has-stepper) {
    flex-wrap: nowrap;
}

/* ──────────────────────────────────────────────────────────────────────
 * Workers panel — idle slot rows. The processing layout (title,
 * progress bar, footer numbers) drops into a quieter palette so an
 * 8-worker deployment doesn't look like a wall of fake metrics on a
 * mostly-idle queue. Card height stays constant either way (the
 * structure is identical — only colours change), which is the
 * D34 anti-flash contract.
 * ────────────────────────────────────────────────────────────────────── */
.workers-panel-card { transition: border-color 0.15s ease; }

html[data-bs-theme="dark"] .workers-panel-card[data-status="idle"] {
    background-color: rgba(255,255,255,0.025);
    border-color: rgba(255,255,255,0.06);
}

html[data-bs-theme="dark"] .workers-panel-card[data-status="processing"] {
    border-color: rgba(13,110,253,0.35);
}

/* ──────────────────────────────────────────────────────────────────────
 * System & Workers card sections — replaces the old <hr>-separated
 * blob with named sub-sections (Media Servers / Worker Pool / Version).
 * Each .system-section gets a small uppercase title + comfortable
 * vertical rhythm. The whole card reads as an outline rather than a
 * scroll of disconnected widgets.
 * ────────────────────────────────────────────────────────────────────── */
.system-card-body { padding-bottom: 0.85rem; }
.system-card-body .system-section { margin-bottom: 1.1rem; }
.system-card-body .system-section:last-of-type { margin-bottom: 0.4rem; }


html[data-bs-theme="dark"] .system-version-row { border-color: rgba(255, 255, 255, 0.08); }
html[data-bs-theme="light"] .system-version-row { border-color: rgba(0, 0, 0, 0.08); }
.system-version-row { padding-top: 0.7rem !important; margin-top: 0.4rem; }

/* ──────────────────────────────────────────────────────────────────────
 * Day-of-week picker (Quiet Hours) — checked days get the brand
 * orange so the user sees at a glance which days the window applies
 * to. Bootstrap's default btn-check + outline-secondary leaves
 * checked days in the same washed-out grey as unchecked.
 * ────────────────────────────────────────────────────────────────────── */
.qh-window-days .btn-check:checked + .btn,
.qh-window-days .btn-check:checked + .btn:focus {
    background-color: var(--plex-orange) !important;
    border-color: var(--plex-orange) !important;
    color: #161616 !important;
    font-weight: 600;
}
.qh-window-days .btn-check + .btn { color: var(--bs-body-color); }
.qh-window-days .btn-check + .btn:hover { background-color: var(--surface-1); }

/* Priority buttons & badges — fixed width so High/Normal/Low don't shift layout */
.priority-btn,
.priority-badge {
    min-width: 76px;
    text-align: center;
}

/* Make the dropdown toggle match badge sizing so rows don't shift height */
.priority-btn {
    padding: 0.25em 0.5em;
    font-size: 0.75em;
    font-weight: 700;
    line-height: 1;
    vertical-align: middle;
}

/* Color dot in priority dropdown items */

/* ──────────────────────────────────────────────────────────────
 * Unified info-icon (ⓘ) affordance
 * Every ⓘ across the app uses `.info-icon` so users see
 * consistent hover/click behaviour wherever it appears:
 *   - hover → Bootstrap tooltip with one short sentence
 *   - click → shared `#globalInfoModal`, only on `.info-icon-more`
 * `_applyInfoIconAffordance` (app.js) sets `.info-icon-more` exactly
 * when a detail is attached, so only those get the pointer; a
 * hover-only ⓘ keeps the default cursor because a click does nothing.
 * ────────────────────────────────────────────────────────────── */
.info-icon {
    color: var(--bs-secondary-color, var(--bs-secondary));
    background: none;
    border: none;
    padding: 0;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    gap: 0.15rem;
}
/* `:not(:disabled)` matches the specificity of Bootstrap's reboot rule that gives every enabled button a pointer. */
.info-icon:not(:disabled) {
    cursor: default;
}
.info-icon:hover,
.info-icon:focus-visible {
    color: var(--accent-text);
}
.info-icon:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-radius: 2px;
}
/* Affordance: ⓘ icons that open a modal get a pointer and a chevron to
 * signal "there's more" vs. bare ⓘs that only have a hover tooltip. */
.info-icon-more:not(:disabled) {
    cursor: pointer;
}
/* The ⓘ alone is the affordance; a trailing chevron made it two controls for one action. */

.scan-filters > summary {
    list-style: none;
    cursor: pointer;
}
.scan-filters > summary::-webkit-details-marker { display: none; }
.scan-filters > summary:hover { background: var(--bs-tertiary-bg); }
.scan-filters > summary:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}
.scan-filters .scan-filters-hide,
.scan-filters[open] .scan-filters-edit { display: none; }
.scan-filters[open] .scan-filters-hide { display: inline; }
.scan-filters[open] .scan-filters-chevron { transform: rotate(180deg); }

/* Setup Health redesign — bucketed checks (Must fix / Recommended /
 * All good) plus side-by-side current/recommended diffs. The aim is
 * "one glance answers: do I need to act, and what value should it be?"
 * — pre-redesign every failing check rendered identically and the
 * single "Currently <X>" line left users guessing whether <X> was good
 * or bad. */
.readiness-bucket > summary {
    list-style: none;
    background: var(--bs-tertiary-bg);
    border: 1px solid var(--bs-border-color);
    transition: background-color 120ms ease;
}
.readiness-bucket > summary::-webkit-details-marker { display: none; }
.readiness-bucket > summary:hover { background: var(--bs-secondary-bg); }
.readiness-bucket[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.readiness-bucket-body {
    border: 1px solid var(--bs-border-color);
    border-top: none;
    border-bottom-left-radius: 0.375rem;
    border-bottom-right-radius: 0.375rem;
    padding-bottom: 0.5rem;
}

/* Side-by-side current vs recommended pill pair. Mismatch is tinted
 * red on the left and green on the right — colour does the heavy
 * lifting so the user doesn't have to read the values to spot a
 * problem. */
.readiness-values .readiness-current {
    background: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
    border: 1px solid var(--bs-danger-border-subtle);
}
.readiness-values .readiness-recommended {
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
    border: 1px solid var(--bs-success-border-subtle);
}
.readiness-values .readiness-label {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    opacity: 0.75;
}
.readiness-values code {
    background: transparent;
    color: inherit;
    font-weight: 600;
}
.readiness-arrow { font-weight: 700; }

html[data-bs-theme="light"] .modal-footer .btn-secondary[data-bs-dismiss="modal"]:hover {
    background-color: #e2e8f0;
}

html[data-bs-theme="light"] .nav-item .btn-link:hover,
html[data-bs-theme="light"] .nav-item .btn-link:focus,
html[data-bs-theme="light"] .nav-item .nav-link:hover {
    background-color: rgba(15, 23, 42, 0.06);
    border-radius: 6px;
}

html[data-bs-theme="light"] .status-dot.status-running::before {
    /* The dark-mode neon-blue pulse halo (rgba(74,158,255,…) in the
     * status-dot-pulse keyframe) sits awkwardly on the slate-grey
     * light-mode dot. Drop the animation in light — the dot's filled
     * state is enough state indication on a light canvas. */
    animation: none;
}

html[data-bs-theme="light"] .attempts-pill:hover {
    box-shadow: 0 2px 6px rgba(15, 23, 42, 0.06);
}

html[data-bs-theme="light"] .attempts-pill.active {
    /* Brand-orange focus ring matches every other "active selection"
     * affordance in light mode (nav-active, dropdown-active). */
    box-shadow: 0 0 0 2px rgba(229, 160, 13, 0.35);
}

html[data-bs-theme="light"] .attempts-bar {
    background: #f1f5f9;
    border-color: #cbd5e1;
}

html[data-bs-theme="light"] .attempts-pills > .attempts-pill[data-is-originating="1"]::after {
    /* Originator/retry divider — needs slightly darker than the bar's
     * border to be visible. */
    border-left-color: #cbd5e1;
}

html[data-bs-theme="light"] .gpu-card {
    /* The default .gpu-card light variant used Bootstrap-blue rgba()s
     * that don't exist anywhere else in the palette. Re-skin to slate
     * so the card reads as part of the system, not a third-party widget. */
    background: linear-gradient(135deg, #f1f5f9, #fbfcfd);
    border-color: #cbd5e1;
}

/* Priority dots — drop saturation so they read as labels, not alarms. */

/* Log search-result highlight. The 30%-opacity orange reads as candy
 * over near-white logs; amber-100 matches the rest of the
 * warning-subtle pattern. */
html[data-bs-theme="light"] .log-highlight {
    background: #fef3c7;
    color: #92400e;
}

/* Info-icon focus ring must use the brand orange like every other
 * focusable in the app, not Bootstrap-blue. */
html[data-bs-theme="light"] .info-icon:focus-visible {
    box-shadow: var(--focus-ring);
}

/* Form-control / form-select border tightening. Bootstrap's default
 * light border (#dee2e6) is slightly too soft on slate-200 cards. */
html[data-bs-theme="light"] .form-control,
html[data-bs-theme="light"] .form-select {
    border-color: #cbd5e1;
}
html[data-bs-theme="light"] .form-control:focus,
html[data-bs-theme="light"] .form-select:focus {
    border-color: #94a3b8;
}

/* Light theme: the slider thumb takes the brand accent (Bootstrap's default is the slate primary). */
html[data-bs-theme="light"] .form-range::-webkit-slider-thumb {
    background-color: var(--plex-orange);
}
html[data-bs-theme="light"] .form-range::-moz-range-thumb {
    background-color: var(--plex-orange);
}
html[data-bs-theme="light"] .form-range::-webkit-slider-thumb:active {
    background-color: #c98a0b;
}
html[data-bs-theme="light"] .form-range::-moz-range-thumb:active {
    background-color: #c98a0b;
}

/* ──────────────────────────────────────────────────────────────────────
 * Senior-design polish pass — round 3 long-tail.
 * Final cleanup after the 28-screenshot audit. Each rule below was
 * either confirmed real via Playwright measurement or visible in the
 * captured screenshots. All scoped to avoid dark/light regressions.
 * ────────────────────────────────────────────────────────────────────── */

/* Pagination disabled state. Default Bootstrap disabled pagination
 * shows the chevrons at near-full opacity in light mode — looks
 * like enabled-but-unhovered. Bump to 0.4 so they read as disabled. */
.page-item.disabled .page-link {
    opacity: 0.4;
}

/* Log viewer floating jump-to-top / jump-to-bottom buttons. The
 * arrows float bottom-right with no chrome — on light bg they were
 * disembodied glyphs. Give them a circular surface so they read as
 * controls. The container is forced dark in light mode now (see
 * pages/logs.css light-mode terminal block) so we tune the floating
 * controls against that dark surface. */
.log-nav-btns .btn {
    background-color: rgba(255, 255, 255, 0.08);
    border-color: rgba(255, 255, 255, 0.15);
    color: #e2e8f0;
    border-radius: 50%;
    width: 32px;
    height: 32px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.log-nav-btns .btn:hover {
    background-color: rgba(255, 255, 255, 0.15);
    color: #fff;
}

/* Servers cards — visual separator between primary actions (Edit /
 * Refresh) and the destructive Delete button. Pre-fix the trash
 * button floated right of the secondary buttons with no semantic
 * gap, making accidental clicks more likely. */
.server-card .btn-outline-danger,
#serverList .btn-outline-danger {
    margin-left: 0.5rem;
    border-left-width: 1px;
}

/* Setup wizard step connector thickness. The 1px line between
 * progress dots was barely visible against the page bg in light mode.
 * Bump to 2px and use the border token so it picks up the slate
 * value in light, slate-translucent in dark. */
.setup-progress .step-connector {
    height: 2px;
    background-color: var(--bs-border-color);
}
.setup-progress .step-connector.completed {
    background-color: var(--plex-orange);
}

/* ──────────────────────────────────────────────────────────────────────
 * Job queue table — bg-color inheritance fix. Bootstrap's .table sets
 * --bs-table-bg: var(--bs-body-bg), and every cell paints that body bg.
 * Inside the JOB QUEUE card (which is white-ish via --bs-secondary-bg),
 * cells render slate-200 (body bg) instead of the card surface — every
 * row reads as if it's sitting on a different background than the card
 * it's inside. Force the table-bg variable to transparent so cells let
 * the card bg show through. Affects desktop table view + the mobile
 * card-stack derived from the same table. The user flagged this as
 * banded title-row-vs-body-row appearance.
 *
 * Also: tighten the mobile card-stack row so the title-row /
 * status-row / progress-row group reads as ONE card instead of three
 * loose bands. Add slight vertical separator between consecutive job
 * cards (previously a 1px border on the tr which painted under the
 * inherited cell bg and disappeared).
 * ────────────────────────────────────────────────────────────────────── */

.jobs-table > tbody > tr,
.jobs-table > tbody > tr > td {
    background-color: transparent;
}

@media (max-width: 575.98px) {
    /* Stronger card separation between consecutive job rows; the
     * 1px hairline got lost over the inherited body-bg. */
    .jobs-table tbody tr {
        border-bottom: 1px solid var(--bs-border-color);
        padding: 0.9rem 0.5rem;
    }
    /* Action-button group on the status row — give it a slight surface
     * so the 2-3 small buttons read as a contained cluster, not as three
     * floating chips sitting next to the status text. */
    .jobs-table tbody tr > td:nth-child(7) .btn-group {
        background-color: var(--bs-tertiary-bg);
        border-radius: 0.375rem;
        padding: 2px;
    }
    .jobs-table tbody tr > td:nth-child(7) .btn-group .btn {
        border-color: transparent;
        background-color: transparent;
    }
    .jobs-table tbody tr > td:nth-child(7) .btn-group .btn:hover {
        background-color: var(--bs-secondary-bg);
    }
    /* Faint "Pending" status text — bump from text-muted to body-color
     * so the operator can scan job state without leaning in. */
    .jobs-table tbody tr .status-dot.status-pending {
        color: var(--bs-body-color);
    }
}

/* Path-mapping + exclude-path tables on mobile. At < 576px the three
 * narrow input fields (Path on Server / Local path / Path in webhook)
 * + remove button squeeze to ~70px each, making the input visibly
 * truncated and the +/- buttons feel cramped. Force a minimum table
 * width and let the parent scroll horizontally so the user can swipe
 * to reveal each column at a comfortable width.
 * Affects both Setup wizard Step 3 (#setupPathMappingsTable +
 * #setupExcludePathsTable) and Server-edit modal Path-mappings +
 * Exclude-paths tabs (#editPathMappingsTable + #editExcludePathsTable). */
@media (max-width: 575.98px) {
    #editPathMappingsTable,
    #editExcludePathsTable,
    #setupPathMappingsTable,
    #setupExcludePathsTable {
        min-width: 480px;
    }
    /* The Setup wizard's path-mapping container has no .table-responsive
     * wrapper — fall back to overflow-x on the section card so the table
     * can scroll independently of the page. */
    #setupPathMappingsContainer,
    #setupExcludePathsContainer {
        overflow-x: auto;
    }
    /* Server-edit modal — the tab-pane already has padding; let the
     * table scroll within it. */
    #edit-tab-paths,
    #edit-tab-excludes {
        overflow-x: auto;
    }
}

/* Two final fixes from the user's gap-closing review.
 *
 * 1. Job-row hover in light mode — table-hover regression. My earlier
 *    --bs-table-bg: transparent override only neutralised the BASE bg.
 *    Bootstrap's .table-hover sets --bs-table-bg-state to a different
 *    var on hover, which paints cells with --bs-table-hover-bg
 *    (= slate-200) over the white parent card — same banded look the
 *    user flagged in s1/s2 returns on hover. Neutralise the hover
 *    state too: transparent base + a subtle row-level tint so the
 *    "this is the row I'm pointing at" feedback survives without
 *    repainting cells with body-bg.
 *
 * 2. Job Details modal log viewer — same WCAG fix as the /logs page
 *    needs applying to #logsContent inside #logsModal. The modal log
 *    panel uses bg-body-tertiary which is slate-100 in light, and
 *    the neon log colors (debug grey, info teal, warning amber,
 *    error red) were tuned for a dark canvas. Force the same
 *    slate-900 dark terminal in light mode for the modal's log
 *    panel so the colors stay legible.
 */

.jobs-table {
    --bs-table-bg: transparent;
    --bs-table-bg-type: transparent;
    --bs-table-hover-bg: transparent;
    --bs-table-color-state: inherit;
}
.jobs-table > tbody > tr:hover {
    background-color: rgba(15, 23, 42, 0.04);
}
html[data-bs-theme="dark"] .jobs-table > tbody > tr:hover {
    background-color: rgba(255, 255, 255, 0.04);
}
.jobs-table > tbody > tr:hover > * {
    background-color: transparent !important;
}

html[data-bs-theme="light"] #logsContent {
    background-color: #0f172a !important;
    color: #e2e8f0;
}

/* Servers → Edit → Intro & Credits tab (markers_server_tab.js). Label left, control right, as in the
 * approved design; the status block is a two-column label/value grid. */
.markers-opt {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}
.markers-opt > :first-child {
    min-width: 0;
}
/* A switch stays beside its label on a phone; segmented controls may wrap under theirs. */
.markers-opt:has(> .markers-switch) {
    flex-wrap: nowrap;
}
.markers-switch {
    margin: 0;
    padding-left: 0;
}
.markers-switch .form-check-input {
    margin-left: 0;
    width: 2.5em;
    height: 1.35em;
}
.markers-kv {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0.5rem 1rem;
    align-items: center;
}
.markers-kv > .markers-kv-label {
    color: var(--bs-secondary-color);
}
@media (max-width: 575.98px) {
    .markers-kv {
        grid-template-columns: minmax(0, 1fr);
        gap: 0 1rem;
    }
    .markers-kv > .markers-kv-label:not(:first-child) {
        margin-top: 0.5rem;
    }
}
/* Servers → Edit → Libraries: one switch per column, centred under its header with no label beside it (the row
 * names the library). Switch columns stay as narrow as their one-line header; the library name takes the rest. */
.edit-lib-switch-col {
    width: 1%;
    white-space: nowrap;
}
.edit-lib-switch {
    display: inline-block;
    min-height: 0;
    margin: 0;
    padding-left: 0;
}
.edit-lib-switch .form-check-input {
    float: none;
    margin-left: 0;
}
/* Jobs queue: the ↳ of a follow-up explains itself on hover. */
.job-follow-arrow {
    cursor: help;
}

/* Notification bell badge: a 16px count pill ringed in the navbar colour (set per theme below). */
#notificationBellBadge {
    position: absolute;
    top: 2px;
    right: 2px;
    left: auto;
    transform: none;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    font-size: 10px;
    font-weight: 700;
    line-height: 16px;
    background: var(--bad);
    color: #fff;
    box-shadow: 0 0 0 2px var(--plex-dark);
}
/* White on the dark theme's coral --bad is 2.6:1; dark ink clears 6:1. */
html[data-bs-theme="dark"] #notificationBellBadge {
    color: #1a0508;
}
html[data-bs-theme="light"] #notificationBellBadge {
    box-shadow: 0 0 0 2px #fff;
}

/* Shared dropdown surface: Tools, Help and Notifications menus match. */
.navbar .dropdown-menu {
    border: 1px solid var(--line);
    border-radius: var(--radius-ctl);
}
.navbar .dropdown-item:hover,
.navbar .dropdown-item:focus-visible {
    background-color: var(--inset);
}
.navbar .dropdown-item.active {
    background-color: var(--accent-soft);
    color: var(--plex-orange-text);
}

main {
    padding-bottom: 2rem;
}

/* Phone menu: the Account & help rows, notifications and the pinned Logout / version foot are in overlays.css. */
@media (max-width: 1199.98px) {
    #navbarNav.offcanvas .offcanvas-body {
        display: flex;
        flex-direction: column;
    }
    #navbarNav.offcanvas .nav-hover-menu > .dropdown-toggle.show::after {
        transform: rotate(180deg);
    }
}

/* 44px touch targets for icon buttons on every page; the visual size is unchanged on pointer devices. */
@media (pointer: coarse), (max-width: 575.98px) {
    .ibtn, .wh-ibtn, .log-ibtn, .pm-remove, .ep-remove, .stepper-minus, .stepper-plus, .btn-icon { min-width: 44px; min-height: 44px; }
    .dash-btn, .dash-btn-sm { min-height: 44px; min-width: 44px; }
    .btn, .nav-link, .log-level-btn, .log-fbtn, .log-nav-btn { min-height: 44px; }
    .log-nav-btn, .log-level-btn { min-width: 44px; }
    .form-control, .form-select { min-height: 44px; }
    summary { min-height: 44px; display: flex; align-items: center; }
    /* Hit area only: the glyph keeps its size, a transparent 44px square centred on it takes the tap. */
    .info-icon { position: relative; }
    .info-icon::before { content: ''; position: absolute; top: 50%; left: 50%; width: 44px; height: 44px; transform: translate(-50%, -50%); }
    .form-check-input[type="checkbox"] { position: relative; }
    .form-check-input[type="checkbox"]::before { content: ''; position: absolute; top: 50%; left: 50%; width: 44px; height: 44px; transform: translate(-50%, -50%); }
    .form-range { height: 44px; }
}

/* ──────────────────────────────────────────────────────────────────────
 * Polish layer: tokens and Bootstrap overrides that make the app one calm family. Kept last so it wins over the legacy rules above at equal specificity.
 * ────────────────────────────────────────────────────────────────────── */

/* The navbar row lost Bootstrap's .container-fluid flex rules when it took .page-shell. */
nav.navbar > .page-shell {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: space-between;
}
.navbar .nav-link { font-size: 14px; }

/* Typography scale 11 / 12 / 13 / 14 / 16 / 20 / 26: Bootstrap's fractional sizes (11.5, 12.5, 13.6 px) are retired. */
small, .small, .form-text { font-size: 12px; }
.badge { font-size: 11px; font-weight: 600; }
.table { font-size: 13px; --bs-table-color: var(--bs-body-color); }
.table thead th { font-weight: 600; }

/* Secondary outline buttons: Bootstrap grey was 3.6:1 on the page and 2.8:1 on a card. */
.btn-outline-secondary {
    --bs-btn-color: var(--bs-body-color);
    --bs-btn-border-color: var(--line-strong);
    --bs-btn-hover-bg: var(--inset);
    --bs-btn-hover-color: var(--bs-body-color);
    --bs-btn-hover-border-color: var(--faint);
    --bs-btn-active-bg: var(--inset);
    --bs-btn-active-color: var(--bs-body-color);
    --bs-btn-active-border-color: var(--line-strong);
    --bs-btn-disabled-color: var(--muted);
    --bs-btn-disabled-border-color: var(--line-strong);
}
/* Info and success outline buttons (vendor tiles): token text colour, neutral edge. */
.btn-outline-info, .btn-outline-success {
    --bs-btn-border-color: var(--line-strong);
    --bs-btn-hover-bg: var(--inset);
    --bs-btn-active-bg: var(--inset);
    --bs-btn-hover-border-color: var(--line-strong);
    --bs-btn-active-border-color: var(--line-strong);
}
.btn-outline-info { --bs-btn-color: var(--run); --bs-btn-hover-color: var(--run); --bs-btn-active-color: var(--run); }
.btn-outline-success { --bs-btn-color: var(--ok); --bs-btn-hover-color: var(--ok); --bs-btn-active-color: var(--ok); }
html[data-bs-theme="light"] .btn-outline-info { --bs-btn-color: var(--bs-info-text-emphasis); --bs-btn-hover-color: var(--bs-info-text-emphasis); --bs-btn-active-color: var(--bs-info-text-emphasis); }
html[data-bs-theme="light"] .btn-outline-warning { --bs-btn-hover-color: var(--plex-orange-text); }

/* Solid status badges become the tinted pill used everywhere else (white on cyan was 1.96:1, on green 3.8:1). */
html[data-bs-theme="dark"] .badge.bg-info,
html[data-bs-theme="dark"] .badge.bg-info.text-dark { background-color: var(--run-soft) !important; color: var(--run) !important; }
html[data-bs-theme="dark"] .badge.bg-success { background-color: var(--ok-soft) !important; color: var(--ok) !important; }
html[data-bs-theme="dark"] .badge.bg-warning,
html[data-bs-theme="dark"] .badge.bg-warning.text-dark { background-color: var(--warn-soft) !important; color: var(--warn) !important; }
html[data-bs-theme="dark"] .badge.bg-danger { background-color: var(--bad-soft) !important; color: var(--bad) !important; }
html[data-bs-theme="dark"] .badge.bg-secondary { background-color: var(--inset) !important; color: var(--muted) !important; }

/* Menus, offcanvas, modals and tooltips are one navy family one step above the card. */
.dropdown-menu { --bs-dropdown-bg: var(--panel-2); --bs-dropdown-link-hover-bg: var(--inset); }
.offcanvas { --bs-offcanvas-bg: var(--panel-2); }
html[data-bs-theme="light"] .modal-content { background-color: var(--bs-secondary-bg); }
html[data-bs-theme="dark"] .table > thead > tr > th,
html[data-bs-theme="dark"] .table thead th { background-color: var(--inset); }
.tooltip {
    --bs-tooltip-bg: var(--panel-2);
    --bs-tooltip-color: var(--bs-body-color);
    --bs-tooltip-max-width: 280px;
    --bs-tooltip-opacity: 1;
}
.tooltip-inner { border: 1px solid var(--line-strong); box-shadow: var(--shadow-2); }
html[data-bs-theme="light"] .tooltip { --bs-tooltip-bg: #1e293b; --bs-tooltip-color: #f1f5f9; }
html[data-bs-theme="light"] .tooltip-inner { border-color: transparent; }

/* One accent for every switch, checkbox and radio. The mark inside is dark: white on amber is 2.3:1. */
.form-check-input:checked { background-color: var(--accent-fill); border-color: var(--accent-fill); }
.form-check-input:focus { border-color: var(--accent); box-shadow: var(--focus-ring); }
.form-check-input:checked[type="checkbox"] {
    --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%231a1203' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}
.form-check-input:checked[type="radio"] {
    --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%231a1203'/%3e%3c/svg%3e");
}
.form-switch .form-check-input:checked {
    --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%231a1203'/%3e%3c/svg%3e");
}

/* Light theme glare and hairlines */
html[data-bs-theme="light"] .table { --bs-table-border-color: var(--surface-border); }
html[data-bs-theme="light"] .card-header { border-bottom-color: var(--surface-border); }

/* Dashboard bars draw no stripes, so the stripe keyframe would only burn an infinite animation. The indeterminate
 * worker bar keeps its own slide. */
.dashboard-layout .progress-bar-animated:not(.worker-indeterminate) { animation: none; }

/* Icon controls share one size: 32px with a pointer, 44px on a phone. */
.ibtn, .btn-icon, .stepper-minus, .stepper-plus { min-width: var(--ctl-icon); min-height: var(--ctl-icon); }
@media (max-width: 575.98px) {
    :root { --ctl-icon: 44px; }
    .info-icon { position: relative; }
    .info-icon::before { content: ''; position: absolute; top: 50%; left: 50%; width: 44px; height: 44px; transform: translate(-50%, -50%); }
}

/* Numbers that change are tabular so a ticking count does not shimmer. */
.stat, .queue-progress, .queue-created, .wh-time, .occ-chip, input[type="number"] { font-variant-numeric: tabular-nums; }
@media (pointer: coarse) {
    .navbar-brand, .navbar-version { display: inline-flex; align-items: center; min-height: 44px; }
}
/* The bar is a fixed 57px whatever the wordmark size: the logo mark, not the text, sets its line box. */
nav.navbar { min-height: 57px; }

/* Status pill shared by the dashboard and the Automation page (other pages restate their own variant). */
.dashboard-layout .pill,
.automation-page .pill { display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 10px; border-radius: 999px; font-size: 12px; font-weight: 600; background: var(--inset); color: var(--muted); white-space: nowrap; letter-spacing: normal; text-transform: none; }
.dashboard-layout .pill.ok,
.automation-page .pill.ok { background: var(--ok-soft); color: var(--ok); }
.dashboard-layout .pill.warn,
.automation-page .pill.warn { background: var(--warn-soft); color: var(--warn); }
