/* ============================================================
   NexoVirt theme override for Tabler (dist CSS)
   Load AFTER tabler.min.css. No SCSS build needed. Dark mode switches via data-bs-theme="dark".
   Zinc black/white base with ONE blue accent, matching the marketing site (NexoVirtLanding
   site.css): #2563EB on white (5.17:1, white label on the fill 5.17:1) and #60A5FA on black
   (8.26:1, dark label on the fill). Primary buttons, links, focus rings, checked controls,
   active nav/tabs/pagination and progress all follow --tblr-primary. Status colours stay as is.
   ============================================================ */

/* ---------- Neutral zinc greys (both modes) ---------- */
:root {
  --tblr-gray-50:  #fafafa;
  --tblr-gray-100: #f4f4f5;
  --tblr-gray-200: #e4e4e7;
  --tblr-gray-300: #d4d4d8;
  --tblr-gray-400: #a1a1aa;
  --tblr-gray-500: #71717a;
  --tblr-gray-600: #52525b;
  --tblr-gray-700: #3f3f46;
  --tblr-gray-800: #27272a;
  --tblr-gray-900: #18181b;
  --tblr-gray-950: #09090b;
}

/* ---------- LIGHT MODE -------------------------------------- */
:root {
  /* Brand / Primary: blue-600 on white */
  --tblr-primary: #2563eb;
  --tblr-primary-rgb: 37, 99, 235;
  --tblr-primary-fg: #ffffff;
  --tblr-primary-text-emphasis: #1e40af;
  --tblr-primary-bg-subtle: #eff6ff;
  --tblr-primary-border-subtle: #bfdbfe;

  /* Links (#2563eb on the #fafafa body 4.95:1, on white cards 5.17:1) */
  --tblr-link-color: #2563eb;
  --tblr-link-color-rgb: 37, 99, 235;
  --tblr-link-hover-color: #1d4ed8;

  /* Body */
  --tblr-body-bg: #fafafa;
  --tblr-body-bg-rgb: 250, 250, 250;
  --tblr-body-color: #0a0a0a;
  --tblr-body-color-rgb: 10, 10, 10;
  --tblr-emphasis-color: #000000;

  /* Surfaces */
  --tblr-bg-surface: #ffffff;
  --tblr-bg-surface-secondary: #f4f4f5;
  --tblr-bg-surface-tertiary: #fafafa;
  --tblr-bg-surface-dark: #18181b;
  --tblr-bg-forms: #ffffff;

  /* Borders */
  --tblr-border-color: #e4e4e7;
  --tblr-border-color-translucent: rgba(9, 9, 11, 0.08);

  /* Secondary / muted text */
  --tblr-secondary: #71717a;
  --tblr-secondary-rgb: 113, 113, 122;
  --tblr-secondary-color: #71717a;
  --tblr-muted: #71717a;
  --tblr-tertiary-color: #a1a1aa;

  /* Dark/light theme colour aliases */
  --tblr-dark: #18181b;
  --tblr-dark-rgb: 24, 24, 27;
  --tblr-light: #fafafa;

  /* Status colours (functional, unchanged) */
  --tblr-success: #16a34a;
  --tblr-success-rgb: 22, 163, 74;
  --tblr-warning: #d97706;
  --tblr-warning-rgb: 217, 119, 6;
  --tblr-danger: #dc2626;
  --tblr-danger-rgb: 220, 38, 38;
  --tblr-info: #0891b2;
  --tblr-info-rgb: 8, 145, 178;
}

/* Buttons: the dist bakes some btn vars, so re-point them to the brand vars (var-based, so a
   white-label --tblr-primary flows through too). Hover/active darken the fill a step. */
.btn-primary {
  --tblr-btn-color: var(--tblr-primary-fg);
  --tblr-btn-bg: var(--tblr-primary);
  --tblr-btn-border-color: var(--tblr-primary);
  --tblr-btn-hover-color: var(--tblr-primary-fg);
  --tblr-btn-hover-bg: color-mix(in srgb, var(--tblr-primary), #000 14%);
  --tblr-btn-hover-border-color: color-mix(in srgb, var(--tblr-primary), #000 14%);
  --tblr-btn-active-color: var(--tblr-primary-fg);
  --tblr-btn-active-bg: color-mix(in srgb, var(--tblr-primary), #000 24%);
  --tblr-btn-active-border-color: color-mix(in srgb, var(--tblr-primary), #000 24%);
  --tblr-btn-disabled-color: var(--tblr-primary-fg);
  --tblr-btn-disabled-bg: var(--tblr-primary);
  --tblr-btn-disabled-border-color: var(--tblr-primary);
  --tblr-btn-focus-shadow-rgb: var(--tblr-primary-rgb);
}

.btn-outline-primary {
  --tblr-btn-color: var(--tblr-primary);
  --tblr-btn-border-color: var(--tblr-primary);
  --tblr-btn-hover-color: var(--tblr-primary-fg);
  --tblr-btn-hover-bg: var(--tblr-primary);
  --tblr-btn-hover-border-color: var(--tblr-primary);
  --tblr-btn-active-color: var(--tblr-primary-fg);
  --tblr-btn-active-bg: var(--tblr-primary);
  --tblr-btn-active-border-color: var(--tblr-primary);
  --tblr-btn-focus-shadow-rgb: var(--tblr-primary-rgb);
}

/* ---------- DARK MODE --------------------------------------- */
[data-bs-theme="dark"] {
  /* Brand / Primary: blue-400 on black, with a DARK label on filled buttons
     (#0a0a0a on #60a5fa 7.79:1; #60a5fa on the #0a0a0a body 7.79:1, on the #161616 surface 7.12:1) */
  --tblr-primary: #60a5fa;
  --tblr-primary-rgb: 96, 165, 250;
  --tblr-primary-fg: #0a0a0a;
  --tblr-primary-text-emphasis: #93c5fd;
  --tblr-primary-bg-subtle: #0f1d33;
  --tblr-primary-border-subtle: #1e3a5f;

  /* Links */
  --tblr-link-color: #60a5fa;
  --tblr-link-color-rgb: 96, 165, 250;
  --tblr-link-hover-color: #93c5fd;

  /* Body */
  --tblr-body-bg: #0a0a0a;
  --tblr-body-bg-rgb: 10, 10, 10;
  --tblr-body-color: #fafafa;
  --tblr-body-color-rgb: 250, 250, 250;
  --tblr-emphasis-color: #ffffff;

  /* Surfaces */
  --tblr-bg-surface: #161616;
  --tblr-bg-surface-secondary: #1f1f22;
  --tblr-bg-surface-tertiary: #121212;
  --tblr-bg-surface-dark: #000000;
  --tblr-bg-forms: #161616;

  /* Borders */
  --tblr-border-color: #262628;
  --tblr-border-color-translucent: rgba(250, 250, 250, 0.12);

  /* Secondary / muted text */
  --tblr-secondary: #a1a1aa;
  --tblr-secondary-rgb: 161, 161, 170;
  --tblr-secondary-color: #a1a1aa;
  --tblr-muted: #a1a1aa;
  --tblr-tertiary-color: #71717a;

  /* Status colours (a touch lighter in dark mode) */
  --tblr-success: #22c55e;
  --tblr-success-rgb: 34, 197, 94;
  --tblr-warning: #f59e0b;
  --tblr-warning-rgb: 245, 158, 11;
  --tblr-danger: #ef4444;
  --tblr-danger-rgb: 239, 68, 68;
  --tblr-info: #22d3ee;
  --tblr-info-rgb: 34, 211, 238;
}

/* Dark mode: hover/active LIGHTEN the blue (the label is dark). */
[data-bs-theme="dark"] .btn-primary {
  --tblr-btn-hover-bg: color-mix(in srgb, var(--tblr-primary), #fff 18%);
  --tblr-btn-hover-border-color: color-mix(in srgb, var(--tblr-primary), #fff 18%);
  --tblr-btn-active-bg: color-mix(in srgb, var(--tblr-primary), #fff 32%);
  --tblr-btn-active-border-color: color-mix(in srgb, var(--tblr-primary), #fff 32%);
}

/* Button weight as on the marketing site (semibold); the radius stays the panel's own scale. */
.btn {
  --tblr-btn-font-weight: 600;
}

/* Headings: the tighter tracking of the marketing type scale. */
.page-title,
h1, .h1, h2, .h2, h3, .h3 {
  letter-spacing: -.02em;
}

/* ============================================================
   Baked-in component overrides
   These Tabler dist classes hardcode the default blue (they do NOT read
   --tblr-primary), so the variable cluster above can't reach them. Re-point each
   to the brand / theme vars here (the skill's quick test).
   ============================================================ */

/* Pagination active page: the blue fill + its contrast label (the dist hardcodes a white label,
   which fails on the light dark-mode blue). */
.pagination {
  --tblr-pagination-active-bg: var(--tblr-primary);
  --tblr-pagination-active-border-color: var(--tblr-primary);
  --tblr-pagination-active-color: var(--tblr-primary-fg);
}

/* Nav pills active (the app-wide tab style) → the brand colour + its contrast fg. */
.nav-pills .nav-link.active,
.nav-pills .show > .nav-link {
  background-color: var(--tblr-primary);
  color: var(--tblr-primary-fg);
}

/* List-group active row: a neutral surface with a blue left accent (not a loud full blue fill). */
.list-group-item.active {
  background-color: var(--tblr-bg-surface-secondary);
  border-color: var(--tblr-border-color);
  color: var(--tblr-body-color);
  box-shadow: inset 2px 0 0 0 var(--tblr-primary);
}

/* Active tab (nav-tabs, incl. card-header-tabs): a blue underline as the indicator. */
.nav-tabs .nav-link.active,
.nav-tabs .nav-item.show .nav-link {
  box-shadow: inset 0 -2px 0 0 var(--tblr-primary);
}

/* Steps counter: Tabler hardcodes the number to white, which fails on the light dark-mode blue. */
.steps-counter .step-item::before {
  color: var(--tblr-primary-fg);
}

/* NexoTable selection: selected rows get the soft blue tint, the bulk bar a blue left accent. */
.nx-body tr:has(.nx-check:checked) > td {
  background-color: var(--tblr-primary-bg-subtle);
}
.nx-bulkbar {
  background-color: var(--tblr-primary-bg-subtle);
  box-shadow: inset 3px 0 0 0 var(--tblr-primary);
}

/* Native file-input "Choose file" button: neutral themed surface (was the OS/dist default). */
.form-control[type="file"]::file-selector-button {
  background-color: var(--tblr-bg-surface-secondary);
  color: var(--tblr-body-color);
  border: 0;
  border-inline-end: var(--tblr-border-width, 1px) solid var(--tblr-border-color);
  margin-inline-end: .75rem;
}
.form-control[type="file"]:hover::file-selector-button {
  background-color: var(--tblr-bg-surface-tertiary);
}

/* ============================================================
   SCSS-baked derived vars + form focus  (per the updated Tabler skill)
   Tabler pre-computes these from $primary at build time as hardcoded values,
   so overriding --tblr-primary alone doesn't reach them. Re-derive from the
   brand at runtime with color-mix. (--tblr-info kept functional on purpose.)
   ============================================================ */

/* ---------- LIGHT MODE ---------- */
:root {
  --tblr-primary-lt:      color-mix(in oklab, var(--tblr-primary) 10%, transparent);
  --tblr-primary-lt-rgb:  var(--tblr-primary-rgb);
  --tblr-primary-darken:  color-mix(in oklab, var(--tblr-primary), transparent 20%);
  --tblr-active-bg:       rgba(var(--tblr-primary-rgb), 0.04);
  --tblr-link-hover-color-rgb: var(--tblr-secondary-rgb);
  --tblr-blue:            var(--tblr-primary);   /* raw-blue anchor follows the (white-label) brand */
  --tblr-blue-rgb:        var(--tblr-primary-rgb);
  --tblr-form-focus-border-color: color-mix(in oklab, var(--tblr-primary) 50%, white);
}

/* Form focus borders → brand tint (Bootstrap ships these with !important, so we match). */
.form-control:focus,
.form-select:focus,
.form-check-input:focus,
.input-group-flat:focus-within .form-control,
.input-group-flat:focus-within .input-group-text {
  border-color: var(--tblr-form-focus-border-color) !important;
}

/* Switch toggle dot colour on focus (encoded in an SVG data URL). */
.form-switch .form-check-input:focus {
  --tblr-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='%238C8C8E'/%3e%3c/svg%3e");
}

/* ---------- DARK MODE ---------- */
[data-bs-theme="dark"] {
  --tblr-active-bg:       rgba(var(--tblr-primary-rgb), 0.08);  /* the dark dropdown-active leak */
  --tblr-primary-lt:      color-mix(in oklab, var(--tblr-primary) 10%, transparent);
  --tblr-primary-lt-rgb:  var(--tblr-primary-rgb);
  --tblr-primary-darken:  color-mix(in oklab, var(--tblr-primary), transparent 20%);
  --tblr-link-hover-color-rgb: var(--tblr-secondary-rgb);
  --tblr-blue:            var(--tblr-primary);
  --tblr-blue-rgb:        var(--tblr-primary-rgb);
  --tblr-form-focus-border-color: color-mix(in oklab, var(--tblr-primary) 50%, black);
}
[data-bs-theme="dark"] .form-switch .form-check-input:focus {
  --tblr-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='%23727272'/%3e%3c/svg%3e");
}

/* Checked switch in dark mode. The dark-mode blue (#60a5fa) is light, so Tabler's white knob
   only reaches 2.5:1 on it. Use a DARK knob instead (#18181b on #60a5fa, about 7:1). */
[data-bs-theme="dark"] .form-switch .form-check-input:checked {
  background-color: var(--tblr-primary);
  border-color: var(--tblr-primary);
  --tblr-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='%2318181b'/%3e%3c/svg%3e");
}

/* Checked checkbox + radio in dark mode: same reason as the switch above. Tabler draws the
   tick/dot WHITE via --tblr-form-check-bg-image; force a DARK mark on the light blue box. */
[data-bs-theme="dark"] .form-check-input:checked[type="checkbox"] {
  --tblr-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3e%3cpath fill='none' stroke='%2318181b' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/%3e%3c/svg%3e");
}
[data-bs-theme="dark"] .form-check-input:checked[type="radio"] {
  --tblr-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3ccircle r='3' fill='%2318181b' cx='8' cy='8' /%3e%3c/svg%3e");
}

/* ============================================================
   Modern shell (rounded + soft): var-only refresh
   Tabler derives every radius from a single scale var and reads --tblr-box-shadow*
   on cards/dropdowns/modals, so bumping these globally rounds and lifts the whole
   panel without touching any component markup. Reversible: delete this block.
   ============================================================ */
:root {
  /* One knob scales ALL radii (sm 4 / md 6 / lg 8 px * scale). 1.5 => 6/9/12px. */
  --tblr-border-radius-scale: 1.5;

  /* Shared height for the sidebar logo card + the content top bar, so they always match and
     resize together. Bump this one value to make both taller. */
  --nx-topbar-h: 4rem;

  /* Softer, layered shadows (default Tabler card shadow is nearly flat). */
  --tblr-box-shadow-sm: 0 1px 2px rgba(9, 9, 11, .05);
  --tblr-box-shadow:    0 1px 3px rgba(9, 9, 11, .06), 0 1px 2px -1px rgba(9, 9, 11, .05);
  --tblr-box-shadow-lg: 0 12px 28px -8px rgba(9, 9, 11, .14), 0 6px 12px -6px rgba(9, 9, 11, .08);
  --tblr-card-box-shadow: var(--tblr-box-shadow);
}

/* Cards: give every card the soft shadow + keep the hairline border a touch lighter,
   so surfaces read as lifted panels rather than flat boxes. */
.card {
  box-shadow: var(--tblr-box-shadow);
}

/* Dark mode: shadows need real black (an rgba of the body colour would be near-white) and a bit more depth. */
[data-bs-theme="dark"] {
  --tblr-box-shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --tblr-box-shadow:    0 1px 3px rgba(0, 0, 0, .5), 0 1px 2px -1px rgba(0, 0, 0, .4);
  --tblr-box-shadow-lg: 0 14px 32px -8px rgba(0, 0, 0, .6), 0 6px 14px -6px rgba(0, 0, 0, .45);
  --tblr-card-box-shadow: var(--tblr-box-shadow);
}

/* ============================================================
   Attached sidebar (classic shell, lg+)
   Full height, flush to the left edge, its own surface, one 1px border on the right. Logo on top
   (same height as the top bar, so both bottom borders line up), search, the scrolling nav, and the
   version as plain text at the bottom. The nx-side-card wrappers stay in the markup but carry no
   card styling. Mobile keeps the off-canvas drawer below.
   ============================================================ */
@media (min-width: 992px) {
  .nx-sidebar.navbar-vertical {
    width: 17.5rem;
    background: var(--tblr-bg-surface) !important;
    border: 0 !important;
    border-right: var(--tblr-border-width, 1px) solid var(--tblr-border-color) !important;
    box-shadow: none !important;
    padding: 0;
    overflow: hidden;
  }
  /* Tabler hardcodes 15rem on the aside + the content offset; keep both at the wider width. */
  .nx-sidebar.navbar-vertical ~ .page-wrapper { margin-left: 17.5rem; }
  .nx-sidebar.navbar-vertical ~ .page { padding-left: 17.5rem; }
  /* Tabler zeroes padding on the vertical navbar's direct container; match its specificity. */
  .nx-sidebar.navbar-vertical > .nx-side-stack {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    height: 100%;
    gap: 0;
    padding: 0 !important;
  }
  .nx-side-collapse {
    display: flex !important;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    padding: 0;
    overflow: visible;
  }
  .nx-side-card {
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  .nx-side-brand {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    min-height: var(--nx-topbar-h);
    padding: 0 1rem;
    border-bottom: var(--tblr-border-width, 1px) solid var(--tblr-border-color);
  }
  .nx-side-search { flex-shrink: 0; padding: .75rem .75rem 0; }
  .nx-side-nav {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: .5rem .75rem;
  }
  .nx-side-nav-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
  }
  .nx-side-nav .navbar-nav { padding: 0; }
  .nx-side-user { flex-shrink: 0; margin: .25rem .75rem .75rem; }
}

/* Sidebar foot: the edition as the main line, the version below it. A link to Settings > Edition
   & license (admin shell only), with the same soft hover as the nav items. */
.nx-side-edition-link {
  display: block;
  padding: .5rem .75rem;
  border-radius: var(--tblr-border-radius);
  color: var(--tblr-body-color);
  text-decoration: none;
  transition: background-color .12s ease;
}
.nx-side-edition-link:hover { background-color: var(--tblr-active-bg); color: var(--tblr-body-color); text-decoration: none; }
.nx-side-edition { display: block; font-size: .9375rem; font-weight: 600; line-height: 1.3; }
.nx-side-version { display: block; font-size: .8125rem; color: var(--tblr-secondary); }
.nx-side-update { align-self: flex-start; margin: .5rem 1.5rem 0; }

/* ============================================================
   Sidebar nav active/hover: soft blue tint pill
   Active entry = rounded pill with a soft brand tint + brand-coloured icon/text; hover = fainter
   tint. Scoped to the sidebar nav card so it never touches Tabler tabs/pills elsewhere.
   ============================================================ */
.nx-side-nav .navbar-nav .nav-link {
  border-radius: var(--tblr-border-radius);
  transition: background-color .12s ease, color .12s ease;
}
.nx-side-nav .navbar-nav .nav-link:hover {
  background-color: var(--tblr-active-bg);
}
.nx-side-nav .navbar-nav > .nav-item.active > .nav-link,
.nx-side-nav .navbar-nav .nav-link.active,
.nx-side-nav .navbar-nav .nav-link.show {
  background-color: var(--tblr-primary-lt);
  color: var(--tblr-primary);
  font-weight: 500;
  box-shadow: none;
}
.nx-side-nav .navbar-nav > .nav-item.active > .nav-link .nav-link-icon,
.nx-side-nav .navbar-nav .nav-link.active .nav-link-icon {
  color: var(--tblr-primary);
}
/* Sub-items (Nodes / Provisioning dropdowns): rounded inset pills, subtle active (no full-bleed bar). */
.nx-side-nav .dropdown-menu .dropdown-item {
  border-radius: var(--tblr-border-radius);
  width: auto;
  margin: 1px .25rem;
  padding-inline: .5rem;
}
/* Nodes menu: a small cluster label above the nodes of a multi-node connection, nodes indented. */
.nx-side-nav .dropdown-menu .nx-nav-cluster,
.navbar .dropdown-menu .nx-nav-cluster {
  padding: .5rem .75rem .125rem;
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--tblr-secondary);
}
.nx-side-nav .dropdown-menu .dropdown-item.nx-nav-cluster-node { padding-left: 1.25rem; }
/* Tabler paints the active sub-item with --tblr-navbar-active-bg (a grey); use the blue tint. */
.nx-sidebar { --tblr-navbar-active-bg: var(--tblr-primary-lt); }
.nx-side-nav .dropdown-menu .dropdown-item.active,
.nx-side-nav .dropdown-menu .dropdown-item:active {
  background-color: var(--tblr-primary-lt);
  color: var(--tblr-primary);
  font-weight: 500;
}
.nx-side-nav .dropdown-menu .dropdown-item:hover {
  background-color: var(--tblr-active-bg);
  color: var(--tblr-body-color);
}

/* ============================================================
   Top bar (lg+): a full-width bar attached to the top of the content area, same height as the
   sidebar logo row, one 1px bottom border, sticky while the page scrolls.
   ============================================================ */
@media (min-width: 992px) {
  .page-wrapper > .page-header {
    position: sticky;
    top: 0;
    z-index: 1020;
    display: flex;
    align-items: center;
    min-height: var(--nx-topbar-h);
    margin: 0;
    padding: 0 1.5rem;
    background: var(--tblr-bg-surface);
    border: 0;
    border-bottom: var(--tblr-border-width, 1px) solid var(--tblr-border-color);
    border-radius: 0;
    box-shadow: none;
  }
  .page-wrapper > .page-header > .container-fluid {
    width: 100%;
    padding-left: 0;
    padding-right: 0;
  }
  /* Content: the same 1.5rem gutter on both sides as the top bar. */
  .page-wrapper > .page-body > .container-fluid,
  .page-wrapper > .footer > .container-fluid {
    padding-left: 1.5rem;
    padding-right: 1.5rem;
  }
}


/* ============================================================
   Mobile sidebar drawer (slides from the LEFT). Self-contained (js-nav-drawer in app.js), so the
   desktop static sidebar (lg+) is untouched. Only active below lg.
   ============================================================ */
@media (max-width: 991.98px) {
  .nx-side-collapse {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 17rem;
    max-width: 85vw;
    z-index: 1045;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    padding: 1rem;
    background: var(--tblr-bg-surface);
    border-right: var(--tblr-border-width, 1px) solid var(--tblr-border-color);
    transform: translateX(-100%);
    transition: transform .25s ease;
    overflow-y: auto;
  }
  .nx-side-collapse { padding: .75rem; }
  .nx-side-collapse.show { transform: translateX(0); }
  /* Drawer children span its full width (the collapsed navbar would centre and shrink them). */
  .nx-side-collapse > * { align-self: stretch; width: 100%; }
  .nx-side-user { text-align: left; }
  /* Left-align + flush the nav in the drawer (Tabler centres it in a collapsed navbar; the
     flex-start rule is lg+-only, so force it here with id specificity + !important). */
  #sidebar-menu .navbar-nav { align-items: stretch !important; padding-left: 0; margin-left: 0; }
  #sidebar-menu .nav-item { width: 100%; text-align: left; }
  #sidebar-menu .navbar-nav .nav-link {
    display: flex !important;
    justify-content: flex-start !important;
    text-align: left !important;
    padding-left: .5rem;
    padding-right: .5rem;
  }
  #sidebar-menu .dropdown-menu { padding-left: 0; }
  #sidebar-menu .dropdown-item { justify-content: flex-start !important; text-align: left !important; padding-left: .75rem; }
  .nx-nav-backdrop { position: fixed; inset: 0; z-index: 1044; background: rgba(0, 0, 0, .45); }
  body.nx-nav-open { overflow: hidden; }
}

/* ============================================================
   Auth pages (layouts/auth: login + 2FA) in the marketing-site look: a plain white page in light
   mode, the near-black body in dark mode, and a flat bordered card (no lift shadow).
   ============================================================ */
.nx-auth { background-color: var(--tblr-bg-surface); }
[data-bs-theme="dark"] .nx-auth { background-color: var(--tblr-body-bg); }
.nx-auth .card { box-shadow: none; }

/* NexoTable stacks its own card-body blocks (hidden bulk bar, toolbar). Tabler's
   `.card-body + .card-body { border-top }` drew a thin line above the toolbar even while the
   bulk bar is hidden, so drop that rule inside tables. */
[data-nexo-table] .card-body + .card-body { border-top: 0; }
/* Same for the bottom: no full-width rule between the last row and the pagination footer
   (Tabler's .card-footer border-top plus the last row's own border-bottom). */
[data-nexo-table] .card-footer { border-top: 0; }
[data-nexo-table] .table > tbody > tr:last-child > * { border-bottom-width: 0; }

/* Guests list: OS logo next to the name, and the compact CPU/RAM/disk usage block. */
.nx-guest-os { width: 2.25rem; height: 2.25rem; flex: 0 0 auto; object-fit: contain; }
.nx-guest-os-empty { display: inline-flex; align-items: center; justify-content: center; color: var(--tblr-secondary); }
.nx-usage { display: grid; gap: .25rem; min-width: 13rem; }
.nx-usage-line { display: grid; grid-template-columns: 2.25rem 5rem auto; align-items: center; gap: .5rem; font-size: .75rem; }
.nx-usage-label { color: var(--tblr-secondary); }
.nx-usage-val { white-space: nowrap; font-variant-numeric: tabular-nums; }
/* KPI card traffic line: may run under the status badge like the node page's usage bar does,
   so upload + download fit on one line. */
.nx-kpi .nx-node-net { margin-right: -3.25rem; }

/* Migrate dialog (partials/migrate_modal.php): sections without dividers, source -> target rows. */
.nx-mig-section { margin-top: 1.5rem; }
.nx-mig-heading { font-size: .75rem; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--tblr-secondary); margin-bottom: .75rem; }
.nx-mig-empty { text-align: center; padding: 2.5rem 1rem 1rem; }
.nx-mig-empty .icon-lg { width: 2.5rem; height: 2.5rem; opacity: .5; }
.nx-mig-wait { margin-top: 1.5rem; }
.nx-mig-map-row { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: .75rem; margin-bottom: .75rem; }
.nx-mig-map-arrow { font-size: 1.25rem; line-height: 1; }
.nx-mig-passed > summary { cursor: pointer; color: var(--tblr-secondary); }
.nx-mig-passed[open] > summary { margin-bottom: .25rem; }

/* Manage groups dialog (partials/guest_group_manage.php): group list left, editor right. */
.nx-gm-side { background: var(--tblr-bg-surface-secondary); border-right: 1px solid var(--tblr-border-color); }
@media (max-width: 767.98px) { .nx-gm-side { border-right: 0; border-bottom: 1px solid var(--tblr-border-color); } }
.nx-gm-heading { font-size: .75rem; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--tblr-secondary); }
.nx-gm-list { max-height: 24rem; overflow-y: auto; padding: 0 .5rem .75rem; }
.nx-gm-row { display: flex; align-items: center; gap: .75rem; padding: .5rem .5rem; border-radius: var(--tblr-border-radius); cursor: pointer; }
.nx-gm-row:hover { background: var(--tblr-bg-surface); }
.nx-gm-row.active { background: var(--tblr-bg-surface); box-shadow: inset 2px 0 0 var(--tblr-primary); }
.nx-gm-row .js-gm-delete { opacity: 0; }
.nx-gm-row:hover .js-gm-delete, .nx-gm-row.active .js-gm-delete, .nx-gm-row .js-gm-delete:focus { opacity: 1; }
.nx-gm-empty { text-align: center; padding: 2rem 1rem; }
.nx-gm-empty .icon { width: 2rem; height: 2rem; opacity: .5; }
.nx-gm-colors { gap: .375rem; }
.nx-gm-icons .form-selectgroup-label { padding: .5rem .625rem; }

/* Guests "Group view" tree (guest-groups.js): collapsible group rows, guests as child rows. */
.nx-tree tr.nx-tree-group { cursor: pointer; background: var(--tblr-bg-surface-secondary); }
.nx-tree tr.nx-tree-group:hover { background: var(--tblr-active-bg); }
.nx-tree tr.nx-tree-group:focus-visible { outline: 2px solid var(--tblr-primary); outline-offset: -2px; }
.nx-tree-chevron {
  width: .5rem; height: .5rem; flex: 0 0 auto; margin: 0 .25rem;
  border-right: 2px solid var(--tblr-secondary); border-bottom: 2px solid var(--tblr-secondary);
  transform: rotate(-45deg); transition: transform .15s ease;
}
.nx-tree tr.nx-tree-group.open .nx-tree-chevron { transform: rotate(45deg); }
.nx-tree tr.nx-tree-child > td:first-child { padding-left: 3.25rem; }
@media (prefers-reduced-motion: reduce) { .nx-tree-chevron { transition: none; } }

/* Live provisioning / migration page (guests/provisioning.php + provisioning-view.js). */
.nx-prov-icon {
  width: 3.25rem; height: 3.25rem; flex: 0 0 auto; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--tblr-primary-lt); color: var(--tblr-primary);
}
.nx-prov-icon .icon { width: 1.75rem; height: 1.75rem; }
.nx-prov-icon .spinner-border { width: 1.75rem; height: 1.75rem; border-width: 3px; }
[data-state="done"] .nx-prov-icon { background: var(--tblr-green-lt); color: var(--tblr-green); }
[data-state="failed"] .nx-prov-icon { background: var(--tblr-red-lt); color: var(--tblr-red); }
.nx-prov-elapsed { font-size: 1.75rem; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.1; }

/* Phase stepper: dots joined by a line, the line fills up as phases finish. */
.nx-prov-phases { display: flex; list-style: none; margin: 1.75rem 0 .25rem; padding: 0; }
.nx-prov-phase { flex: 1 1 0; position: relative; display: flex; flex-direction: column; align-items: center; gap: .5rem; text-align: center; min-width: 0; }
.nx-prov-phase + .nx-prov-phase::before {
  content: ''; position: absolute; top: 1rem; right: calc(50% + 1.25rem); left: calc(-50% + 1.25rem);
  height: 2px; background: var(--tblr-border-color); transition: background-color .3s ease;
}
/* The line after a finished phase turns green (it leads to the next one). */
.nx-prov-phase.is-done + .nx-prov-phase::before { background: var(--tblr-green); }
.nx-prov-dot {
  width: 2rem; height: 2rem; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  font-size: .8125rem; font-weight: 600; color: var(--tblr-secondary);
  background: var(--tblr-bg-surface); border: 2px solid var(--tblr-border-color); transition: all .3s ease;
}
.nx-prov-dot .icon { width: 1rem; height: 1rem; }
.nx-prov-phase.is-active .nx-prov-dot { border-color: var(--tblr-primary); color: var(--tblr-primary); box-shadow: 0 0 0 4px var(--tblr-primary-lt); }
/* Running = blue (ring above), finished = green, failed = red. */
.nx-prov-phase.is-done .nx-prov-dot { background: var(--tblr-green); border-color: var(--tblr-green); color: #fff; }
.nx-prov-phase.is-failed .nx-prov-dot { background: var(--tblr-red); border-color: var(--tblr-red); color: #fff; }
.nx-prov-phase-label { font-size: .8125rem; color: var(--tblr-secondary); }
.nx-prov-phase.is-active .nx-prov-phase-label, .nx-prov-phase.is-done .nx-prov-phase-label { color: var(--tblr-body-color); font-weight: 500; }
.nx-prov-phase.is-failed .nx-prov-phase-label { color: var(--tblr-red); font-weight: 500; }


/* Log: the card itself is the terminal (dark in both themes), so the corners match the card. */
.nx-prov-log { background: #0b0d12; border-color: #0b0d12; overflow: hidden; color: #c9d1d9; }
.nx-prov-log-bar { display: flex; align-items: center; gap: .75rem; padding: .625rem 1rem; border-bottom: 1px solid rgba(255, 255, 255, .08); font-size: .8125rem; }
.nx-prov-log-title { font-weight: 600; color: #e6edf3; }
.nx-prov-log-live { color: #7ee2a8; display: inline-flex; align-items: center; }
.nx-prov-log .form-check-label { color: #8b949e; }
.nx-prov-log-body {
  max-height: 60vh; min-height: 14rem; overflow: auto; padding: .75rem 0;
  font-family: var(--tblr-font-monospace); font-size: .8125rem; line-height: 1.6;
}
.nx-log-line { display: grid; grid-template-columns: 5.5rem 6.5rem minmax(0, 1fr); gap: .75rem; padding: 0 1rem; }
.nx-log-line:hover { background: rgba(255, 255, 255, .04); }
.nx-log-time { color: #6e7681; }
.nx-log-step { color: #79c0ff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nx-log-msg { white-space: pre-wrap; word-break: break-word; }
.nx-log-warning .nx-log-msg { color: #e3b341; }
.nx-log-error { background: rgba(248, 81, 73, .08); }
.nx-log-error .nx-log-msg { color: #ff7b72; }
.nx-log-debug .nx-log-msg { color: #8b949e; }
@media (max-width: 575.98px) {
  .nx-log-line { grid-template-columns: 4.5rem minmax(0, 1fr); }
  .nx-log-step { display: none; }
  .nx-prov-phase-label { font-size: .6875rem; }
}
@media (prefers-reduced-motion: reduce) { .nx-prov-dot, .nx-prov-phase::before { transition: none; } }
.nx-log-success .nx-log-msg { color: #7ee2a8; }

/* Create wizard: the two guest-type boxes side by side at equal width (stacked on phones). */
.nx-cg-types { display: flex; gap: 1rem; width: 100%; }
.nx-cg-types > .form-selectgroup-item { flex: 1 1 0; min-width: 0; margin: 0; }
.nx-cg-types .form-selectgroup-label { height: 100%; }
.nx-cg-type-icon { width: 1.75rem; height: 1.75rem; flex: 0 0 auto; }
@media (max-width: 767.98px) { .nx-cg-types { flex-direction: column; } }
