/* =========================================================================
   Apex Tech Labs — Shared Brand Layer
   Injected into shell hosts via sub_filter, ahead of shell.js.

   SCOPE, DELIBERATELY NARROW.
   This loads into a dozen unrelated codebases, so it only holds rules that
   are safe on ANY app: selection, focus, one brand hairline, and per-app
   layout fixes for bar mode.

   NOTE: scrollbar styling was deliberately REMOVED. It did not apply
   consistently across apps and looked worse where it did. Each app styles
   its own scrollbars now — do not reintroduce it here.
   ========================================================================= */

:root {
  --atl-blue:     #2563eb;
  --atl-blue-lit: #3b82f6;
  --atl-cyan:     #38bdf8;
}

/* -------------------------------------------------------------------------
   1. BRAND HAIRLINE
   2px rule at the top of every shell host — the one visual claim the shared
   layer makes. Sits above the bar so the gradient reads as one edge.
   ------------------------------------------------------------------------- */
html::before {
  content: "";
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: linear-gradient(90deg,
    #1e3a8a 0%, var(--atl-blue) 25%, var(--atl-cyan) 50%, var(--atl-blue) 75%, #1e3a8a 100%);
  z-index: 2147482000;
  pointer-events: none;
}

/* -------------------------------------------------------------------------
   2. SELECTION + FOCUS
   The focus ring is a real accessibility win on the arr apps, several of
   which ship almost no visible focus state.
   ------------------------------------------------------------------------- */
::selection { background: rgba(37, 99, 235, .42); color: #fff; }
:focus-visible { outline: 2px solid var(--atl-cyan); outline-offset: 1px; }

/* -------------------------------------------------------------------------
   3. PER-APP BAR-MODE FIXES

   shell.js sets two attributes on <html>:
       data-apex-app     current app's subdomain slug ("bazarr")
       data-apex-layout  "bar" or "fab"

   Always scope with BOTH. shell.css is served to every app, so an unscoped
   rule leaks everywhere, and a bar-mode fix left active in fab mode leaves
   a phantom gap.

   THE TWO SHAPES OF THIS PROBLEM — get this right before adding a rule.

   (a) NORMAL-FLOW APP.  shell.js puts padding-top on <html>, so flow
       content already moves down by the bar height and needs nothing. Only
       the app's own position:fixed/sticky chrome is out of flow and stays
       at top:0, under the bar. Push JUST that down by --apex-bar-h.

       Do NOT also add --apex-bar-h to the app's internal content offset.
       The html padding already accounted for it, and adding it twice opens
       a blank strip exactly one bar-height tall between the app's header
       and its first content. That bug was live on Bazarr and Homarr.

   (b) VIEWPORT-SIZED APP.  The app pins itself to 100vh (or absolutely
       positions its root at top:0). The html padding then pushes it past
       the bottom of the viewport and the document grows a second scrollbar.
       Cancel the padding, or subtract --apex-bar-h from the height, and
       offset the root instead.

   Prefer framework-stable selectors (.navbar-fixed-top, CSS custom
   properties, ids) over build-hashed class names, which change on upgrade.
   ------------------------------------------------------------------------- */

/* --- Bazarr (Mantine 7 AppShell) — shape (a) ---------------------------
   Mantine hashes class names per build (.m_9814e45f) — never target those.
   Header is position:fixed at top:0; main takes its padding from
   --app-shell-header-offset.

   MEASURED 2026-09-03: at bar 42px the old --app-shell-header-offset
   override produced header 42..106 but content at 148 — a 42px void. The
   offset must stay at the app's own header height; only the header moves.
   Do not reinstate that override.                                        */
[data-apex-app="bazarr"][data-apex-layout="bar"] header {
  top: var(--apex-bar-h, 0px) !important;
}

/* --- Homarr / dash (Mantine 7 AppShell) — shape (a) --------------------
   Identical structure to Bazarr: header fixed at top:0, height 60, and
   main padded by --app-shell-header-offset. Verified 2026-09-03 that the
   header alone needs moving; html padding covers the rest.               */
[data-apex-app="dash"][data-apex-layout="bar"] header {
  top: var(--apex-bar-h, 0px) !important;
}

/* --- Tautulli (Bootstrap 3) — shape (a) --------------------------------
   .navbar-fixed-top is stock Bootstrap 3 — stable across Tautulli
   upgrades. Bootstrap pins it at top:0.                                  */
[data-apex-app="tautulli"][data-apex-layout="bar"] .navbar-fixed-top {
  top: var(--apex-bar-h, 0px) !important;
}

/* --- Seerr (Next.js / Tailwind) — shape (a) ----------------------------
   Same app as sethsflix.com on a different hostname. Sidebar and search
   bar are both fixed at top:0. VERIFIED 2026-09-03: sidebar and searchbar
   both land at the bar height and document overflow is 0.                */
[data-apex-app="seerr"][data-apex-layout="bar"] .sidebar,
[data-apex-app="seerr"][data-apex-layout="bar"] div[class*="fixed"][class*="top-0"],
[data-apex-app="seerr"][data-apex-layout="bar"] div[class*="sticky"][class*="top-0"] {
  top: var(--apex-bar-h, 0px) !important;
}

/* --- Cleanuparr (Angular) — shape (b) ----------------------------------
   MEASURED 2026-09-03. The previous rule targeted .layout-topbar and
   .layout-sidebar, which NO LONGER EXIST in this build — that is why the
   left nav sat under the bar while the right side looked correct.

   Real structure: body and div.shell are both 100vh, and nav.sidebar is
   position:fixed top:0 height:100vh. The 100vh body pushed past the
   viewport by the html padding was the second scrollbar.

   Verified after: document overflow 0, sidebar starts at the bar height.  */
html[data-apex-app="clean"][data-apex-layout="bar"] body,
html[data-apex-app="clean"][data-apex-layout="bar"] .shell {
  height: calc(100vh - var(--apex-bar-h, 0px)) !important;
  min-height: 0 !important;
}
html[data-apex-app="clean"][data-apex-layout="bar"] nav.sidebar {
  top: var(--apex-bar-h, 0px) !important;
  height: calc(100vh - var(--apex-bar-h, 0px)) !important;
}
/* Cleanuparr's own sticky content header sits below the fixed nav. */
[data-apex-app="clean"][data-apex-layout="bar"] header {
  top: var(--apex-bar-h, 0px) !important;
}

/* --- Kener status pages (SvelteKit / Tailwind) — shape (a) -------------
   Covers BOTH status.apextechlabs.com and status.sethsflix.com: slugOf()
   takes the first label, so both report data-apex-app="status".

   MEASURED 2026-09-03: an unclassed div.fixed.inset-x-0.top-0 header 62px
   tall, plus a .theme-plus-bar sticky at top:72px (Tailwind top-18) that
   has to clear the header once it moves.                                 */
[data-apex-app="status"][data-apex-layout="bar"] div[class*="fixed"][class*="inset-x-0"][class*="top-0"] {
  top: var(--apex-bar-h, 0px) !important;
}
[data-apex-app="status"][data-apex-layout="bar"] .theme-plus-bar {
  top: calc(72px + var(--apex-bar-h, 0px)) !important;
}

/* --- Kodbox / data (jQuery desktop) — shape (b) ------------------------
   MEASURED 2026-09-03: body height is 0 — the entire UI is one absolutely
   positioned .app-main at top:0, height:100vh, so the html padding did
   nothing at all and the whole desktop sat under the bar.

   .frame-left / .frame-right are absolute children with JS-set heights;
   forcing them to 100% makes them track the shortened parent.            */
html[data-apex-app="data"][data-apex-layout="bar"] {
  padding-top: 0 !important;
}
html[data-apex-app="data"][data-apex-layout="bar"] .app-main {
  top: var(--apex-bar-h, 0px) !important;
  height: calc(100vh - var(--apex-bar-h, 0px)) !important;
}
html[data-apex-app="data"][data-apex-layout="bar"] .app-main > .frame-left,
html[data-apex-app="data"][data-apex-layout="bar"] .app-main > .frame-right {
  height: 100% !important;
}

/* --- Plex Web — shape (b) ----------------------------------------------
   MEASURED 2026-09-03: #plex.application is absolute, top:0, height:100vh.
   Every other class on the page is build-hashed (FullPage-container-yJVGyN)
   and must not be targeted; the id and .application are authored.

   Public host — shell.js only loads here for WireGuard/LAN clients, so
   these rules never reach an outside viewer.                             */
html[data-apex-app="plex"][data-apex-layout="bar"] {
  padding-top: 0 !important;
}
html[data-apex-app="plex"][data-apex-layout="bar"] #plex.application {
  top: var(--apex-bar-h, 0px) !important;
  height: calc(100vh - var(--apex-bar-h, 0px)) !important;
}

/* --- LLM Council (Vite PWA) — shape (b) --------------------------------
   Plain 100vh body, no fixed chrome. MEASURED 2026-09-03: 42px document
   overflow at bar size S, gone once the body is capped.

   NOTE: this app and qui both precache index.html with a workbox service
   worker. A browser that cached the page before injection existed keeps
   serving the old HTML forever and shows no menu at all. Unregister the
   worker and clear its cache once per browser; after that the precache is
   refetched through NPM and includes the injection.                      */
html[data-apex-app="llm"][data-apex-layout="bar"] body {
  height: calc(100vh - var(--apex-bar-h, 0px)) !important;
  min-height: 0 !important;
}

/* --- qBittorrent / VueTorrent (Vue 3 + Vuetify 3) ----------------------
   VueTorrent's files live in a git clone (/mnt/plex/docker/vuetorrent),
   so anything edited there is lost on the next `git pull`. All of its
   customisation lives here instead.

   Values below come from the rendered DOM and the theme definition in the
   JS bundle — not assumption:
     drawer  top:0; bottom:0; height:calc(100% + 0px); width:256px; z-index:1008
     header  top:0; left:256px; width:calc(100% - 256px); z-index:1006
     toolbar content height 64px
   All are INLINE styles from Vuetify's layout composable, so overrides
   need !important.

   Every rule below uses BOTH attribute selectors. A rule with only
   [data-apex-app] loses to one with [data-apex-app][data-apex-layout],
   which is what previously pinned the drawer under the header.
   ------------------------------------------------------------------------- */

/* -- Theme tokens -------------------------------------------------------
   VueTorrent has no accent-colour setting in its UI; the palette is baked
   into the bundle as a Vuetify theme:
     primary #35495E · secondary #415c75 · navbar #273845
     download #5BB974 · accent #64CEAA
   Vuetify emits these as --v-theme-* RGB triplets in a runtime stylesheet,
   so overriding the token recolours every element that uses it — far more
   reliable than chasing individual selectors.                            */
[data-apex-app="qbit"] .v-theme--dark-legacy {
  --v-theme-accent:    59, 130, 246 !important;   /* #3b82f6 blue  (was #64CEAA green) */
  --v-theme-secondary: 18, 18, 18   !important;   /* #121212 cards (was #415c75) */
  --v-theme-navbar:    33, 33, 33   !important;   /* #212121 nav   (was #273845) */
}

/* -- Header: full width, above the drawer, with a shadow ---------------- */
[data-apex-app="qbit"][data-apex-layout="bar"] .v-app-bar {
  top: var(--apex-bar-h, 0px) !important;
  left: 0 !important;
  width: 100% !important;
  z-index: 1009 !important;   /* drawer is 1008; without this the header hides behind it */
  box-shadow: 0 2px 10px rgba(0, 0, 0, .55) !important;
}

/* -- Drawer: starts below the header, stretches to the bottom -----------
   Its inline height:calc(100% + 0px) must be cleared; top + bottom +
   height:auto is what makes it fill the remaining space correctly.      */
[data-apex-app="qbit"][data-apex-layout="bar"] .v-navigation-drawer {
  top: calc(var(--apex-bar-h, 0px) + 64px) !important;
  bottom: 0 !important;
  height: auto !important;
}

/* -- Page height ---------------------------------------------------------
   The document was 64px too tall because TWO offsets stacked:
     shell.js puts padding-top:64px on <html>   (space for the Apex bar)
     .v-main reserves --v-layout-top:64px       (space for VueTorrent header)
   = 128px consumed while the app still sizes itself to a full viewport.

   Capping .v-application does NOT fix this: its children have
   overflow:visible, so they still extend the scrollable area. That is why
   the earlier height/min-height override left the scrollbar in place.

   Correct fix: stop padding the DOCUMENT, and fold the bar height into the
   app's own content offset instead. --v-layout-top is Vuetify's variable
   for exactly that, so the layout stays internally consistent.
   ------------------------------------------------------------------------- */
html[data-apex-app="qbit"][data-apex-layout="bar"] {
  padding-top: 0 !important;
}
[data-apex-app="qbit"][data-apex-layout="bar"] .v-main {
  --v-layout-top: calc(var(--apex-bar-h, 0px) + 64px) !important;
}

/* -- Torrent table height ------------------------------------------------
   MEASURED IN THE BROWSER, not inferred.

   VueTorrent sizes .vt-resizable-table from the viewport in JavaScript —
   there is no inline style and no CSS rule to find. Its calculation knows
   about VueTorrent's own 64px header but nothing about the Apex bar, so
   the table came out ~56px too tall and the whole document overflowed:
       viewport 1221 / document 1276.

   The table is what overflows, NOT .v-application, .v-main or the drawer.
   Earlier attempts sized those and changed nothing, because the table's
   height is set independently of all of them.

   !important beats the JS-applied value and survives its resize handler.
   The 128px is v-main padding + toolbar. The pagination height is NOT
   subtracted: the table runs full height behind the footer. --apex-bar-h is
   kept separate so this tracks the size steps automatically.

   Verified: document overflow 0 at bar heights 42 / 52 / 64.
   ------------------------------------------------------------------------- */
[data-apex-app="qbit"][data-apex-layout="bar"] .vt-resizable-table {
  height: calc(100vh - var(--apex-bar-h, 0px) - 128px) !important;
}

/* -- Pagination: floating footer ----------------------------------------
   Pagination acts as a footer: torrent rows scroll BEHIND it rather than
   stopping above it.

   Three parts, and all three are required:
     1. the table runs full height (see 128px above) so rows extend under
        the footer instead of ending at its top edge;
     2. the pagination's wrapper is pulled back over that space with a
        negative margin and raised on z-index, so it paints on top;
     3. .v-table__wrapper gets matching bottom padding, or the last rows
        would sit permanently under the footer and be unreachable.

   The wrapper is an unclassed <div>, so :has(> .v-pagination) targets it
   structurally rather than by position.

   --vt-footer-h must match the pagination's real height (58px measured).
   Verified: page overflow 0, and at full scroll the last row bottoms out
   at 1162 with the footer starting at 1163 — nothing trapped underneath.
   ------------------------------------------------------------------------- */
[data-apex-app="qbit"] {
  --vt-footer-h: 58px;
}
[data-apex-app="qbit"] div:has(> .v-pagination) {
  position: relative;
  z-index: 5;
  margin-top: calc(-1 * var(--vt-footer-h));
}
[data-apex-app="qbit"] .v-pagination {
  background: rgba(18, 18, 18, .92);
}
[data-apex-app="qbit"] .v-table__wrapper {
  padding-bottom: var(--vt-footer-h) !important;
}

/* -- Table row breathing room -------------------------------------------
   Vuetify's compact density ships 8px top/bottom on cells. --vt-row-pad
   raises it; the table height is fixed by the calc above and scrolls
   internally, so taller rows just show fewer at once and cannot bring the
   page scrollbar back.
   ------------------------------------------------------------------------- */
[data-apex-app="qbit"] {
  --vt-row-pad: 14px;
}
[data-apex-app="qbit"] .v-table tbody td {
  padding-top: var(--vt-row-pad) !important;
  padding-bottom: var(--vt-row-pad) !important;
}

/* -- Left nav: hide its vertical scrollbar -------------------------------
   Trimming the content height (5px, then 10px) did not help, so the bar is
   not caused by a few stray pixels. Hide it visually instead and keep the
   region scrollable — wheel, trackpad and keyboard still work, so nothing
   becomes unreachable if the nav ever does overflow.

   This is app-scoped on purpose. The global scrollbar styling was removed
   from this file deliberately; do not reintroduce it there.
   ------------------------------------------------------------------------- */
[data-apex-app="qbit"] .v-navigation-drawer__content {
  scrollbar-width: none !important;              /* Firefox */
  -ms-overflow-style: none !important;           /* legacy Edge */
}
[data-apex-app="qbit"] .v-navigation-drawer__content::-webkit-scrollbar {
  width: 0 !important;
  height: 0 !important;
  display: none !important;
}

/* -- Main content font size ---------------------------------------------
   .v-main was the wrong target: .v-table declares font-size:.875rem on
   ITSELF, so a value inherited from an ancestor never applies. The rule
   has to land on .v-table (and its cells, which can restate it).        */
[data-apex-app="qbit"] .v-table,
[data-apex-app="qbit"] .v-table > .v-table__wrapper > table > thead > tr > th,
[data-apex-app="qbit"] .v-table > .v-table__wrapper > table > tbody > tr > td {
  font-size: 1rem !important;
}

/* -- Progress bar 50% wider ---------------------------------------------
   The bar does NOT fill its cell: VueTorrent writes an inline width:10em
   onto the .v-progress-linear element itself, so widening the <td> had no
   effect. Real markup:
     <td class="torrent-progress">
       <div class="v-progress-linear" style="... width: 10em;">
   Inline width means !important is required. 10em + 50% = 15em.
   --vt-progress-w is the tunable.                                        */
[data-apex-app="qbit"] {
  --vt-progress-w: 15em;   /* 10em default + 50% */
}
[data-apex-app="qbit"] td.torrent-progress .v-progress-linear {
  width: var(--vt-progress-w) !important;
}
[data-apex-app="qbit"] td.torrent-progress {
  min-width: var(--vt-progress-w) !important;
}

/* -- Toolbar title: qBittorrent icon + name -----------------------------
   VueTorrent does NOT use Vuetify's .v-toolbar-title__placeholder. Real
   markup, from the rendered page:
     <div class="title-wrapper"><span class="text-accent">Vue</span><span>Torrent</span></div>
   The data-v-* attribute is build-hashed; .title-wrapper is authored and
   stable. width:min-content must be cleared or the text is clipped.     */
[data-apex-app="qbit"] .title-wrapper {
  font-size: 0 !important;
  width: auto !important;
  white-space: nowrap;
}
[data-apex-app="qbit"] .title-wrapper::before {
  content: "";
  display: inline-block;
  width: 1.5rem; height: 1.5rem;
  margin-right: .45rem;
  flex: none;
  background: url("/apex/icons/qbit.webp") center / contain no-repeat;
}
[data-apex-app="qbit"] .title-wrapper::after {
  content: "qBittorrent";
  font-size: 1.25rem;
  font-weight: 500;
  line-height: 1;
}

/* --- Radarr / Sonarr / Prowlarr (React) --------------------------------
   Confirmed working with the default html padding-top. No rule needed. */

/* --- qui (React PWA) ---------------------------------------------------
   Verified 2026-09-03: document overflow 0 with the default html padding,
   no fixed chrome to move. Nothing needed here. Its menu was missing only
   because of the stale workbox precache described in the LLM block above. */

/* --- apextechlabs.com (marketing site) - shape (a) ---------------------
   MEASURED 2026-09-04: an unclassed <nav>, position:fixed top:0, height
   79px, z-index 100 - it sat under the Apex bar.

   The slug here is "apextechlabs", not a subdomain. This host is NOT in
   shell.js's menu (its link moved to the Trackers page), so CURRENT is
   null and the old code left data-apex-app empty, leaving nothing to
   scope against. shell.js now falls back to the hostname's first label so
   every injected host has a hook.

   The hero is full-bleed behind the nav, so there is no reserved header
   space to double-count - moving the nav alone is the whole fix.

   Public host, WireGuard-gated: outside viewers never load any of this.  */
[data-apex-app="apextechlabs"][data-apex-layout="bar"] nav {
  top: var(--apex-bar-h, 0px) !important;
}

/* --- Add new apps here as you find them --------------------------------
   Pattern for shape (a) — an app in normal flow with fixed chrome:
     [data-apex-app="<slug>"][data-apex-layout="bar"] <stable-selector> {
       top: var(--apex-bar-h, 0px) !important;
     }
   Pattern for shape (b) — an app pinned to the viewport:
     html[data-apex-app="<slug>"][data-apex-layout="bar"] { padding-top: 0 !important; }
     html[data-apex-app="<slug>"][data-apex-layout="bar"] <root> {
       top: var(--apex-bar-h, 0px) !important;
       height: calc(100vh - var(--apex-bar-h, 0px)) !important;
     }
   Measure in the browser first. document.documentElement.scrollHeight
   minus clientHeight must come out 0 when you are done.
   ------------------------------------------------------------------------- */

/* -------------------------------------------------------------------------
   4. PRINT — hide the hairline so it doesn't render as a stray mark.
   ------------------------------------------------------------------------- */
@media print {
  html::before { display: none; }
}
