/* NexoVirt app styles (loaded after Tabler). Keep small and additive.
 * NOTE: all brand/colour theming, including the baked-in component overrides
 * (pagination, nav-pills, list-group active, file input), lives in
 * theme-overwrite.css. This file is app-specific, non-colour component CSS only. */

/*
 * The `hidden` attribute always wins. Tabler's display utilities (`.d-flex`, `.d-inline`, ...) are
 * `!important` and come after the reboot's `[hidden]` rule, so `el.hidden = true` on an element with
 * such a class left it visible (a loading block that never went away, a Retry form that never hid).
 * This file loads after Tabler, so the same-specificity rule here takes precedence.
 */
[hidden] { display: none !important; }

/*
 * Icon-only action buttons.
 * This Tabler build's `.btn-icon .icon { margin: calc(-1 * padding-x) }` trick
 * cancels out the icon's width, collapsing the button to ~2px so the icons
 * overlap. Restore symmetric padding and a zero icon margin so the button
 * renders as a proper square sized to its icon (scales with .btn-sm via the
 * padding-y variable).
 */
.btn-icon {
  padding-left: var(--tblr-btn-padding-y);
  padding-right: var(--tblr-btn-padding-y);
}
.btn-icon .icon {
  margin: 0;
}

/*
 * Vertical sidebar background.
 * Tabler's navbar-vertical ships a fixed dark surface (its demo forces
 * data-bs-theme="dark" on the <aside>). We dropped that attribute so the sidebar
 * follows the active theme, and pin its background to the normal page bg var so it
 * matches the body in BOTH light and dark mode, separated only by a hairline border.
 */
.navbar-vertical {
  background-color: var(--tblr-body-bg);
  border-right: var(--tblr-border-width) solid var(--tblr-border-color);
}

/* ---- Toasts (nxAlert, owner-approved "Style B"): flat solid status block + frosted animated mark ----
   Contrast: white text on the raw status tokens fails WCAG AA for small text (warning #d97706 ~3.2:1,
   success #16a34a ~3.3:1, info #0891b2 ~3.7:1; the lighter dark-mode tokens are worse). So the block
   is the status token deepened with black (a flat colour-mix, no gradient): 80 % token in light mode,
   60 % in dark mode. White text then passes AA normal text (>= 4.5:1) for all four types. */
#nx-toasts { width: min(22rem, calc(100vw - 2rem)); }
.nx-toast {
  --nx-toast-base: var(--tblr-info);
  --nx-toast-bg: color-mix(in srgb, var(--nx-toast-base) 80%, #000);
  display: flex; align-items: center; gap: .75rem;
  padding: .75rem .625rem .75rem .75rem; border-radius: .875rem;
  background: var(--nx-toast-bg); color: #fff;
  box-shadow: 0 .75rem 1.75rem -.875rem rgba(9, 9, 11, .45), 0 1px 3px rgba(9, 9, 11, .12);
  opacity: 1; transition: opacity .2s ease, transform .2s ease;
}
[data-bs-theme="dark"] .nx-toast { --nx-toast-bg: color-mix(in srgb, var(--nx-toast-base) 60%, #000); }
.nx-toast-success { --nx-toast-base: var(--tblr-success); }
.nx-toast-error   { --nx-toast-base: var(--tblr-danger); }
.nx-toast-warning { --nx-toast-base: var(--tblr-warning); }
.nx-toast.nx-toast-out { opacity: 0; transform: translateX(1.5rem); }
.nx-toast-body { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.nx-toast-title { font-weight: 700; font-size: .875rem; line-height: 1.3; }
.nx-toast-msg { font-size: .8125rem; line-height: 1.4; overflow-wrap: anywhere; }
.nx-toast a { color: inherit; }
.nx-toast-close {
  flex: 0 0 auto; align-self: flex-start; display: inline-grid; place-items: center;
  width: 1.75rem; height: 1.75rem; padding: 0; border: 0; border-radius: .375rem;
  background: transparent; color: inherit; opacity: .85; cursor: pointer;
}
.nx-toast-close:hover { background: rgba(255, 255, 255, .18); opacity: 1; }
.nx-toast-close:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
.nx-toast-close .icon { width: 1rem; height: 1rem; }

/* Animated status mark (shared by toasts and nxSpotlight). Final state is the static state, so
   reduced motion simply shows the finished icon. The ring svg fills the badge box exactly (inset 0,
   100 %), its circle (r 19 in a 40 box) sits on the badge edge at any size. */
.nx-mark { position: relative; flex: 0 0 auto; display: inline-grid; place-items: center; border-radius: 50%; width: 2.5rem; height: 2.5rem; }
.nx-mark > svg { width: 55%; height: 55%; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.nx-mark > svg.nx-mark-ring { position: absolute; inset: 0; width: 100%; height: 100%; stroke-width: 2; opacity: .6; transform: rotate(-90deg); }
.nx-toast .nx-mark { background: rgba(255, 255, 255, .2); color: #fff; }
@media (prefers-reduced-motion: no-preference) {
  #nx-toasts .nx-toast { animation: nx-toast-in .38s cubic-bezier(.22, 1, .36, 1); }
  .nx-mark .nx-draw { stroke-dasharray: 1; stroke-dashoffset: 1; animation: nx-draw .45s .18s cubic-bezier(.65, 0, .35, 1) forwards; }
  .nx-mark .nx-draw-late { animation-delay: .5s; animation-duration: .3s; }
  .nx-mark-ring circle { stroke-dasharray: 1; stroke-dashoffset: 1; animation: nx-draw .6s cubic-bezier(.65, 0, .35, 1) forwards; }
  .nx-mark .nx-pop { transform-box: fill-box; transform-origin: center; animation: nx-pop .35s .35s backwards cubic-bezier(.34, 1.56, .64, 1); }
  .nx-mark-success { animation: nx-pop .4s cubic-bezier(.34, 1.56, .64, 1); }
  .nx-mark-error { animation: nx-shake .45s .6s; }
  .nx-mark-warning { animation: nx-pulse 1.4s .4s 2; }
}
@keyframes nx-toast-in { from { transform: translateX(1.5rem) scale(.96); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes nx-draw { to { stroke-dashoffset: 0; } }
@keyframes nx-pop { from { transform: scale(.4); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes nx-shake { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-4px); } 40% { transform: translateX(4px); } 60% { transform: translateX(-3px); } 80% { transform: translateX(2px); } }
@keyframes nx-pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, .45); } 50% { box-shadow: 0 0 0 .5rem rgba(255, 255, 255, 0); } }

/* ---- nxSpotlight (owner-approved "Style C"): centred card for big moments, native <dialog> ---- */
.nx-spotlight {
  width: min(24rem, calc(100vw - 2rem)); max-width: none; padding: 1.75rem 1.5rem 1.5rem; margin: auto;
  border: var(--tblr-border-width) solid var(--tblr-border-color); border-radius: 1rem;
  background: var(--tblr-bg-surface); color: var(--tblr-body-color);
  box-shadow: 0 1.25rem 3rem -1rem rgba(9, 9, 11, .45);
  text-align: center;
}
.nx-spotlight::backdrop { background: rgba(10, 10, 10, .45); }
.nx-spotlight-inner { display: grid; gap: .5rem; justify-items: center; }
.nx-spotlight .nx-mark { width: 72px; height: 72px; margin-bottom: .25rem; color: var(--nx-spot-c); background: color-mix(in srgb, var(--nx-spot-c) 14%, transparent); }
.nx-spotlight .nx-mark > svg:not(.nx-mark-ring) { width: 40px; height: 40px; stroke-width: 2.6; }
.nx-spotlight .nx-mark > svg.nx-mark-ring { opacity: .45; stroke-width: 1.25; }
.nx-spotlight { --nx-spot-c: var(--tblr-info); }
.nx-spotlight-success { --nx-spot-c: var(--tblr-success); }
.nx-spotlight-error   { --nx-spot-c: var(--tblr-danger); }
.nx-spotlight-warning { --nx-spot-c: var(--tblr-warning); }
.nx-spotlight-title { font-size: 1.125rem; font-weight: 700; margin: 0; }
.nx-spotlight-msg { color: var(--tblr-secondary); max-width: 34ch; margin: 0; overflow-wrap: anywhere; }
.nx-spotlight-facts { display: flex; flex-wrap: wrap; justify-content: center; gap: .375rem; margin-top: .25rem; }
.nx-spotlight-fact { font-family: var(--tblr-font-monospace); font-size: .75rem; line-height: 1; padding: .375rem .625rem; border-radius: 999px; background: var(--tblr-bg-surface-secondary); color: var(--tblr-body-color); }
.nx-spotlight-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: .5rem; margin-top: .75rem; }
.nx-spotlight-auto { font-size: .75rem; color: var(--tblr-secondary); }
@media (prefers-reduced-motion: no-preference) {
  .nx-spotlight[open] { animation: nx-spot-in .4s cubic-bezier(.22, 1, .36, 1); }
}
@keyframes nx-spot-in { from { transform: translateY(.75rem) scale(.96); opacity: 0; } to { transform: none; opacity: 1; } }

/* Guest Network topology connectors: animated marching dashes (theme-var colours).
   flex:1 so the connectors grow and spread the Server/NIC/IP cards across the full card width. */
.gd-conn { flex:1 1 auto; min-width:40px; height:3px; align-self:center; border-radius:2px;
  background-image: repeating-linear-gradient(90deg, var(--gd-c) 0 7px, transparent 7px 15px);
  background-size: 15px 100%; animation: gd-march .6s linear infinite; }
.gd-conn-nic { --gd-c: var(--tblr-warning); }
.gd-conn-ip  { --gd-c: var(--tblr-success); }
@keyframes gd-march { to { background-position: 15px 0; } }
@media (prefers-reduced-motion: reduce) { .gd-conn { animation: none; } }

/* Hide the main page (document) scrollbar: scrolling itself still works (wheel / keys / touch /
   trackpad). Inner scroll areas (dropdowns, the xterm viewport, etc.) keep their own scrollbars. */
html { scrollbar-width: none; -ms-overflow-style: none; }
html::-webkit-scrollbar { display: none; }
/* Hide the sidebar's own scrollbar too (scroll still works): its gutter was offsetting the brand
   from centre, and it matches the clean no-scrollbar look. */
.navbar-vertical { scrollbar-width: none; }
.navbar-vertical::-webkit-scrollbar { display: none; }

/* Sidebar nav-link count badges (Guests "n/n", Alerts count, etc.).
   Tabler core positions ANY `.navbar .navbar-nav .nav-link .badge` ABSOLUTELY at the
   nav-link's top-right with `transform: translate(50%,-50%)`: that's for a notification
   DOT on a horizontal top-navbar icon. In our VERTICAL sidebar it mis-targets the inline
   `ms-auto` count badges: they get shoved half their width past the fixed 15rem edge and
   clipped (the hidden scrollbar means no reveal). Make them normal inline items again so
   `ms-auto` right-aligns them fully inside the sidebar. */
.navbar-vertical .navbar-nav .nav-link .badge {
  position: static;
  transform: none;
  top: auto;
  right: auto;
}

/* Split.js gutters: resizable panes (Settings → Email → Templates). Flex-basis mode. */
.gutter { background-color: var(--tblr-border-color); background-clip: padding-box; flex-shrink: 0; }
.gutter.gutter-horizontal { cursor: col-resize; }

/* Resizable HTML body editor (Settings → Email → Templates): drag the bottom-right grip. */
#et-pane-editor .CodeMirror { resize: vertical; overflow: hidden; min-height: 12rem; }

/* 2FA OTP single-digit boxes. */
.tfa-digit { width: 3rem; padding-inline: 0; text-align: center; font-size: 1.5rem; font-weight: 600; }

/* NexoVirt logo (partials/logo.php): the whole mark follows the theme body colour (monochrome,
   as on the marketing site). A white-label custom logo is object-fit:contain so it
   never stretches to fill its box, capped at max-width:100%/max-height so a tall or square upload
   can't bleed past the sidebar/navbar/login-card edges (the .navbar-brand py-3 ps-2 padding on the
   sidebar/navbar wrapper gives it breathing room). */
.nx-logo { color: var(--tblr-body-color); display: block; object-fit: contain; max-width: 100%; max-height: 100%; }

/* OS group picker: logo-only borderless tiles (no card box). The logos are transparent
   SVGs, contain-fit so they aren't cropped; selection is a thin primary border + hover tint. */
.os-logo { height: 64px; width: auto; max-width: 100%; object-fit: contain; }
.os-logo-lg { height: 88px; }
.os-logo-sm { height: 36px; }
.js-os-card { display: block; padding: .75rem; border: 2px solid transparent;
  border-radius: var(--tblr-border-radius); cursor: pointer; }
.js-os-card:hover { background: var(--tblr-active-bg, rgba(0, 0, 0, .03)); }
/* Selected tile: a theme-aware tint + ring so it reads in BOTH light and dark (the brand --tblr-primary
   is near-white, so a border alone is invisible on light backgrounds). */
.js-os-card.active { border-color: var(--tblr-primary); background: var(--tblr-active-bg, rgba(0, 0, 0, .06)); box-shadow: inset 0 0 0 1px var(--tblr-border-color); }

/* Error-detail viewer (#nx-detail): readable, wrapping, scrollable message block. */
.nx-detail-pre { white-space: pre-wrap; word-break: break-word; max-height: 60vh; overflow: auto; }

/* Empty-state illustrations: constrain size. The bundled Tabler illustrations carry an intrinsic
   800×600 and Tabler's `.empty-img img { height:auto }` ignores the height attr, so they render
   huge; cap them (documented illustration-sizing quirk, same family as the .auth-illustration
   constraint). Applies to both the inlined SVG (.empty blocks) and the <img> (NexoTable empties). */
.empty-img svg, .empty-img img { max-height: 8rem; width: auto; height: auto; }

/* Alert icons: GLOBAL spacing fix. This Tabler build's `.alert-icon` only sets size/colour, no
   inline-end margin, so every alert-with-icon had to remember `me-2` and several were missed → the
   icon touched the heading (e.g. "Datacenter firewall is disabled"). Space them once, globally.
   Idempotent with an explicit `me-2` (both resolve to .5rem, so no double gap); works whether the
   icon is a direct flex child or wrapped in a <div>. Documented quirk-fix, same family as .btn-icon. */
.alert-icon { margin-inline-end: .5rem; }

/* ApexCharts dark-mode legibility: GLOBAL. ApexCharts bakes a hard-coded dark-grey foreColor into
   legend text and renders a light tooltip that don't follow the Tabler theme, so in dark mode the
   legend (e.g. "Down / Up") is near-black-on-black and the crosshair tooltip is light-on-dark. The
   window.Apex global default is unreliable once a chart sets its own legend/tooltip object, so we pin
   it in CSS (theme-reactive via --tblr-* vars, beats ApexCharts' inline styles, survives live theme
   toggle). Legend text follows the body colour in BOTH modes; the tooltip gets theme-surface colours
   in dark mode. Documented quirk-fix (same family as the .alert-icon / .btn-icon fixes above). */
.apexcharts-legend-text { color: var(--tblr-body-color) !important; }
/* radialBar centre value (node gauges): its SVG fill is set once at render from the theme, but a
   live theme toggle does NOT re-render ApexCharts, so a chart first drawn in dark keeps its light
   fill and vanishes in light mode. Force the fill from the live --tblr-* var so it re-themes with
   the page in both directions. Same quirk-fix family as .apexcharts-legend-text above. */
.apexcharts-datalabel-value { fill: var(--tblr-body-color) !important; }
[data-bs-theme="dark"] .apexcharts-tooltip {
  background: var(--tblr-bg-surface, #1a2234) !important;
  color: var(--tblr-body-color) !important;
  border-color: var(--tblr-border-color) !important;
  box-shadow: none !important;
}
[data-bs-theme="dark"] .apexcharts-tooltip-title {
  background: var(--tblr-bg-surface-secondary, #16202f) !important;
  border-bottom-color: var(--tblr-border-color) !important;
  color: var(--tblr-body-color) !important;
}
[data-bs-theme="dark"] .apexcharts-xaxistooltip,
[data-bs-theme="dark"] .apexcharts-yaxistooltip {
  background: var(--tblr-bg-surface, #1a2234) !important;
  color: var(--tblr-body-color) !important;
  border-color: var(--tblr-border-color) !important;
}
[data-bs-theme="dark"] .apexcharts-xaxistooltip-bottom:before { border-bottom-color: var(--tblr-border-color) !important; }
[data-bs-theme="dark"] .apexcharts-xaxistooltip-bottom:after  { border-bottom-color: var(--tblr-bg-surface, #1a2234) !important; }

/* OS / CT group picker cards (create + reinstall). Selectable bordered cards; the active one gets a
   primary-coloured border + ring. Requested explicitly; lives here with the other --tblr-* styling. */
.gd-os-card { cursor: pointer; transition: border-color .12s ease, box-shadow .12s ease; }
.gd-os-card:hover { border-color: var(--tblr-primary); }
.gd-os-card.active { border-color: var(--tblr-primary); box-shadow: 0 0 0 1px var(--tblr-primary); }

/* Compact OS tiles (create/reinstall picker): small logo + tight body; a multi-version group's
   version dropdown only appears on the ACTIVE tile, so idle tiles stay small (logo + name). */
.os-logo-tile { height: 34px; }
.gd-os-tile .card-body { min-height: 82px; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.gd-os-sublabel { font-size: .72rem; line-height: 1.2; }

/* Steps indicator: drop the leading margin/padding/border so the stepper sits flush.
   Requested explicitly. */
.steps {
  margin-left: 0 !important;
  padding-left: 0 !important;
  border-left: 0 !important;
}

/* Settings → Branding → Colors: mini-panel live preview (sidebar mock). Requested explicitly;
   the content area reads --tblr-* vars directly via inline style, only the sidebar chrome
   (no Tabler equivalent for a mini nav rail) needs a rule here. */
.brand-preview-sidebar {
  width: 110px;
  flex: 0 0 110px;
  background: var(--tblr-bg-surface);
  border-right: 1px solid var(--tblr-border-color);
}
.brand-preview-nav-link {
  display: block;
  padding: 4px 8px;
  margin-bottom: 2px;
  border-radius: 4px;
  font-size: .7rem;
  color: var(--tblr-body-color);
  text-decoration: none;
  opacity: .7;
}
.brand-preview-nav-link.active {
  background: var(--tblr-primary);
  color: var(--tblr-primary-fg, #fff);
  opacity: 1;
}

.brand-thumb { max-height: 36px; width: auto; }

/* Dashboard KPI cards: label, big number, sublabel, and a plain outline icon top-right. */
.nx-kpi .card-body { position: relative; padding-right: 4.5rem; }
a.nx-kpi { color: var(--tblr-body-color); text-decoration: none; }
a.nx-kpi:hover .nx-kpi-label { color: var(--tblr-primary); }
.nx-kpi-label { font-size: .875rem; font-weight: 500; color: var(--tblr-secondary); }
.nx-kpi-value { font-size: 2rem; font-weight: 700; line-height: 1.15; letter-spacing: -.02em; font-variant-numeric: tabular-nums; margin-top: .25rem; }
.nx-kpi-of { font-size: 1.25rem; font-weight: 600; color: var(--tblr-secondary); }
.nx-kpi-sub { font-size: .8125rem; color: var(--tblr-secondary); margin-top: .125rem; }
.nx-kpi-sub.text-danger { color: var(--tblr-danger); }
.nx-kpi-icon { position: absolute; top: 1.25rem; right: 1.25rem; width: 2rem; height: 2rem; color: var(--tblr-secondary); opacity: .55; }

/* Dashboard host cards: nodes as compact link chips (status dot + name). */
.nx-chip { display: inline-flex; align-items: center; gap: .375rem; padding: .125rem .625rem; font-size: .8125rem;
  border: var(--tblr-border-width, 1px) solid var(--tblr-border-color); border-radius: 999px; color: var(--tblr-body-color); text-decoration: none; }
.nx-chip:hover { border-color: var(--tblr-primary); color: var(--tblr-primary); text-decoration: none; }
.nx-chip-more { color: var(--tblr-secondary); }
/* Host cards share the row: 1 = full width, 2 = halves, 3 = thirds, never more than 3 per row; a
   shorter last row stretches to fill it. Exactly 4 hosts go 2 + 2 instead of 3 + 1. Mobile: 1 per row. */
.nx-hosts { --nx-hosts-gap: 1rem; display: flex; flex-wrap: wrap; gap: var(--nx-hosts-gap); }
.nx-host { flex: 1 1 100%; min-width: 0; }
@media (min-width: 992px) {
  .nx-host { flex-basis: calc((100% - 2 * var(--nx-hosts-gap)) / 3); }
  .nx-hosts:has(> .nx-host:nth-child(4):last-child) > .nx-host { flex-basis: calc((100% - var(--nx-hosts-gap)) / 2); }
}
/* Host network line: the summed node up/down rates. */
.nx-net { display: flex; flex-wrap: wrap; align-items: center; column-gap: 1rem; font-size: .8125rem; font-variant-numeric: tabular-nums; }
.nx-net .icon { width: 1rem; height: 1rem; margin-right: .125rem; vertical-align: -.125em; }

/* Dashboard usage bars (host aggregates): label, slim bar, percentage. */
.nx-usage { display: flex; align-items: center; gap: .5rem; flex: 1 1 9rem; min-width: 8rem; }
.nx-usage-label { width: 2.25rem; font-size: .8125rem; color: var(--tblr-secondary); }
.nx-usage-bar { flex: 1 1 auto; min-width: 3rem; }
.nx-usage-pct { width: 2.5rem; text-align: right; font-size: .8125rem; font-variant-numeric: tabular-nums; }

/* Nodes list: compact usage bar inside a table cell. */
.nx-usage.nx-usage-sm { flex: none; min-width: 0; }
.nx-usage-sm .nx-usage-bar { flex: none; width: 3.5rem; min-width: 0; }

/* Node page Info KPI: network up/down on one row under the uptime; the status badge sits top-right. */
.nx-node-net { display: flex; flex-wrap: wrap; column-gap: 1rem; row-gap: .125rem; margin-top: .5rem; white-space: nowrap; }
.nx-node-net > span { display: inline-flex; align-items: center; gap: .25rem; }
.nx-node-net .icon { width: 1rem; height: 1rem; }
.nx-node-net-val { font-weight: 600; font-variant-numeric: tabular-nums; }
.nx-kpi-badge { position: absolute; top: 1.25rem; right: 1.25rem; }
/* A vendor logo in the KPI icon slot: wordmarks are wide, so size by width. */
.nx-kpi-icon.nx-kpi-logo { width: 3rem; height: 2rem; opacity: 1; }

/* Node page tabs: a section title inside a tab (instead of a ruled hr-text divider). */
.nx-tab-section { margin: 1.5rem 0 .75rem; font-size: .75rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--tblr-secondary); }
/* A tab whose only content is a NexoTable sits flush in the tab card (no inset table frame). */
.card > .card-body:has(> .tab-content > .tab-pane.active > [data-nexo-table]:only-child) { padding: 0; }
/* KPI card progress bar spans the full card width, under the icon column too. */
.nx-kpi .progress { width: auto; margin-right: -3.25rem; }
.nx-kpi-value { white-space: nowrap; }
.nx-asof { font-size: .8125rem; letter-spacing: 0; }

/* CPU vendor logos (Simple Icons, monochrome via currentColor). */
.nx-cpu-logo-sm { width: 1.25rem; height: 1.25rem; flex-shrink: 0; color: var(--tblr-body-color); }
.nx-cpu-logo-xs { width: 1.375rem; height: 1.375rem; flex-shrink: 0; vertical-align: -.125em; margin-right: .375rem; color: var(--tblr-secondary); }

/* Overview Status card: CPU row, load meter, platform fact tiles (grouped by proximity). */
.nx-status-cpu { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem; }
.nx-status-cpu-logo { flex-shrink: 0; width: 4.5rem; display: flex; justify-content: center; }
.nx-cpu-logo-lg { width: 4.5rem; height: 2.5rem; }
.nx-status-label { font-size: .75rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--tblr-secondary); }
.nx-status-load { margin-bottom: 1.5rem; }
.nx-load-row { display: flex; align-items: center; gap: .75rem; margin-bottom: .375rem; }
.nx-load-lbl { width: 3.5rem; white-space: nowrap; font-size: .8125rem; color: var(--tblr-secondary); }
.nx-load-val { width: 3rem; text-align: right; font-size: .8125rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.nx-status-facts { display: flex; flex-wrap: wrap; gap: .75rem; }
/* Tiles grow to share the full row, whatever their count (wraps when narrow). */
.nx-status-facts > .nx-fact { flex: 1 1 9.5rem; }
.nx-fact { display: flex; align-items: flex-start; gap: .625rem; padding: .625rem .75rem; border: var(--tblr-border-width, 1px) solid var(--tblr-border-color); border-radius: var(--tblr-border-radius); }
.nx-fact-icon .icon { width: 1.25rem; height: 1.25rem; margin-top: .125rem; }
.nx-fact-value { font-weight: 600; }
.min-w-0 { min-width: 0; }
/* Copyable fact tile (DynDNS name): a real button, reset to look like the other tiles. */
button.nx-fact { text-align: left; background: transparent; color: inherit; font: inherit; cursor: pointer; }
.nx-fact-copy:hover { border-color: var(--tblr-primary); }
.nx-fact-copy:focus-visible { outline: 2px solid var(--tblr-primary); outline-offset: 2px; }

/* Node page Network services tiles: a big plain icon (no plate), title, state, one line, action. */
.nx-svc-icon { width: 2rem; height: 2rem; flex-shrink: 0; color: var(--tblr-body-color); }
/* Six tiles: 3 + 3 on desktops, 2 per row on tablets, 1 on phones. Equal heights per row. */
.nx-svc-row { --nx-svc-gap: .75rem; display: flex; flex-wrap: wrap; gap: var(--nx-svc-gap); }
.nx-svc-col { flex: 1 1 100%; min-width: 0; }
@media (min-width: 576px)  { .nx-svc-col { flex-basis: calc((100% - var(--nx-svc-gap)) / 2); } }
@media (min-width: 992px)  { .nx-svc-col { flex-basis: calc((100% - 2 * var(--nx-svc-gap)) / 3); } }
.nx-svc .card-body { padding: 1rem; }
.nx-svc-title { font-size: .9375rem; }
.nx-svc-state { font-size: .8125rem; font-weight: 600; }

/* Top bar: when the page actions need the room, they wrap under the title instead of squeezing it. */
.page-wrapper > .page-header .row > .col { min-width: 14rem; }

/* Tom Select (vendor/tom-select, Bootstrap 5 theme) reads Bootstrap's --bs-* variables, which Tabler
   does not define (Tabler uses --tblr-*): without these aliases the IP picker dropdown is transparent
   and has no hover highlight. Variable aliases only, so light/dark follow Tabler automatically. */
:root {
  --bs-body-bg: var(--tblr-bg-surface);
  --bs-body-color: var(--tblr-body-color);
  --bs-secondary-bg: var(--tblr-bg-surface-secondary);
  --bs-tertiary-bg: var(--tblr-bg-surface-tertiary);
  --bs-border-color: var(--tblr-border-color);
  --bs-border-color-translucent: var(--tblr-border-color-translucent);
  --bs-border-width: var(--tblr-border-width);
  --bs-border-radius: var(--tblr-border-radius);
  --bs-border-radius-sm: var(--tblr-border-radius-sm);
  --bs-border-radius-lg: var(--tblr-border-radius-lg);
  --bs-box-shadow-inset: var(--tblr-box-shadow-inset);
  --bs-form-valid-color: var(--tblr-green);
  --bs-form-invalid-color: var(--tblr-red);
}
