/* ═══════════════════════════════════════════════════════════════════════════ */
/* AnomLab — Sneat theme overrides                                            */
/* ═══════════════════════════════════════════════════════════════════════════ */
/* Rebrand primary color from Sneat default purple to AnomLab orange +        */
/* small polish tweaks that keep the Sneat feel but match our identity.      */
/* ═══════════════════════════════════════════════════════════════════════════ */

:root {
  --bs-primary:        #ff7a2f;
  --bs-primary-rgb:    255, 122, 47;
  --ir-brand:          #ff7a2f;
  --ir-brand-2:        #ff4d1f;
}

/* Sneat's primary buttons */
.btn-primary,
.bg-primary { background-color: var(--ir-brand) !important; border-color: var(--ir-brand) !important; }
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active { background-color: var(--ir-brand-2) !important; border-color: var(--ir-brand-2) !important; }
.btn-outline-primary { color: var(--ir-brand) !important; border-color: var(--ir-brand) !important; }
.btn-outline-primary:hover { background-color: var(--ir-brand) !important; color: #fff !important; border-color: var(--ir-brand) !important; }
.text-primary { color: var(--ir-brand) !important; }
.link-primary { color: var(--ir-brand); }
.bg-label-primary { background-color: rgba(255, 122, 47, 0.16) !important; color: var(--ir-brand) !important; }

/* Menu active item — brand accent */
.menu-vertical .menu-item.active > .menu-link:not(.menu-toggle),
.menu-vertical .menu-item.active > .menu-link:not(.menu-toggle):hover {
  background: linear-gradient(90deg, var(--ir-brand), var(--ir-brand-2)) !important;
  color: #fff !important;
  box-shadow: 0 4px 12px -4px rgba(255, 122, 47, 0.45);
}

/* Sneat KPI stat cards get a subtle top-border accent */
.card {
  transition: transform 200ms ease-out, box-shadow 240ms ease-out;
}
.card:hover {
  box-shadow: 0 6px 24px -8px rgba(15, 20, 34, 0.10);
}

/* Live clock badge tweak */
[data-server-clock] { min-width: 172px; text-align: center; }

/* Monospace helper */
.mono, .font-mono { font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace !important; }

/* Section titles (h1 in each view) */
.page-title {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 24px;
}
.page-title h4 {
  margin: 0;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.page-title p {
  margin: 0;
  color: var(--bs-secondary-color);
  font-size: 14px;
}

/* KPI value emphasized */
.kpi-value {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 700;
  font-size: 28px;
  letter-spacing: -0.02em;
  color: var(--bs-heading-color);
  line-height: 1;
}
.kpi-hero .kpi-value {
  font-size: 44px;
  color: #71dd37;
  text-shadow: 0 0 30px rgba(113, 221, 55, 0.25);
}

/* Empty state helper */
.empty-state {
  padding: 40px 20px;
  text-align: center;
  color: var(--bs-secondary-color);
}
.empty-state i {
  font-size: 40px;
  opacity: 0.45;
  margin-bottom: 12px;
}
.empty-state h5 {
  margin-bottom: 6px;
  color: var(--bs-heading-color);
  font-size: 15px;
}
.empty-state p {
  margin: 0;
  font-size: 13px;
}

/* Table hover cursor for clickable rows */
.table tbody tr[data-clickable] { cursor: pointer; }

/* Sub-tabs (nav-pills inside toolserver) */
.nav-pills .nav-link.active {
  background-color: var(--ir-brand) !important;
  color: #fff !important;
}

/* Dark theme */
[data-theme="dark"] {
  --bs-body-bg: #232333;
  --bs-body-color: #d4d4dc;
}

/* Force layout to work when JS menu hasn't loaded yet */
@media (min-width: 1200px) {
  .layout-menu-fixed .layout-menu { position: fixed; }
}

/* ═══════════════════════════════════════════════════════════════════════════ */
/* Interior polish — page titles, cards, KPIs, empty states, sub-nav          */
/* ═══════════════════════════════════════════════════════════════════════════ */

/* Page title — uniform section header across every view */
.page-title {
  margin-bottom: 28px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--bs-border-color);
}
.page-title h4 {
  font-size: 24px;
  line-height: 1.15;
  color: var(--bs-heading-color);
  margin: 0 0 4px;
}
.page-title p {
  font-size: 13.5px;
  color: var(--bs-secondary-color);
  margin: 0;
}

/* Card header uniform spacing + subtle divider */
.card > .card-header {
  padding: 18px 22px;
  min-height: 60px;
  display: flex;
  align-items: center;
  gap: 12px;
}
.card > .card-header .card-title {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.005em;
  color: var(--bs-heading-color);
  margin: 0;
  display: flex;
  align-items: center;
  gap: 10px;
}
.card > .card-header .card-title i {
  color: var(--ir-brand);
  font-size: 18px;
}
.card > .card-body {
  padding: 22px;
}

/* KPI hero card centering + gradient wash */
.card:has(.kpi-hero) {
  background:
    radial-gradient(600px 300px at 100% 0%, rgba(113, 221, 55, 0.06), transparent 60%),
    radial-gradient(500px 260px at 0% 100%, rgba(255, 122, 47, 0.05), transparent 65%),
    var(--bs-card-bg);
}
.kpi-hero-wrap {
  display: flex;
  align-items: baseline;
  gap: 6px;
}
.kpi-hero-wrap .unit {
  font-size: 22px;
  color: var(--bs-secondary-color);
  font-weight: 500;
}

/* Stat cards — colored top strip on hover */
.card.stat-card {
  position: relative;
  overflow: hidden;
  transition: transform 200ms ease-out, box-shadow 240ms ease-out;
}
.card.stat-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: linear-gradient(90deg, var(--ir-brand), var(--ir-brand-2));
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 260ms cubic-bezier(0.16, 1, 0.3, 1);
}
.card.stat-card:hover::before { transform: scaleX(1); }
.card.stat-card:hover { transform: translateY(-2px); }

/* Empty state polish */
.empty-state {
  padding: 48px 24px;
}
.empty-state i {
  font-size: 40px;
  color: var(--bs-secondary-color);
  opacity: 0.35;
  margin-bottom: 14px;
  display: block;
}
.empty-state h5 {
  font-size: 15px;
  font-weight: 600;
  margin-bottom: 6px;
}
.empty-state p {
  font-size: 13px;
  color: var(--bs-secondary-color);
  margin: 0;
}

/* Sub-nav (pills used in Tool Server) */
.nav-pills {
  gap: 4px;
  margin-bottom: 24px;
  padding: 4px;
  background: rgba(0, 0, 0, 0.025);
  border: 1px solid var(--bs-border-color);
  border-radius: 8px;
  display: inline-flex;
}
.nav-pills .nav-link {
  padding: 8px 18px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--bs-secondary-color);
  border-radius: 6px;
  transition: color 180ms, background 200ms;
}
.nav-pills .nav-link:hover:not(.active) {
  color: var(--bs-heading-color);
  background: rgba(0, 0, 0, 0.03);
}
.nav-pills .nav-link.active {
  color: #fff !important;
  background: linear-gradient(180deg, var(--ir-brand), var(--ir-brand-2)) !important;
  box-shadow: 0 4px 12px -4px rgba(255, 122, 47, 0.4);
}

/* Tables — better spacing + header contrast */
.table {
  --bs-table-hover-bg: rgba(255, 122, 47, 0.04);
}
.table > thead > tr > th {
  padding: 14px 18px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  background: rgba(0, 0, 0, 0.02);
  border-bottom: 1px solid var(--bs-border-color);
}
.table > tbody > tr > td {
  padding: 14px 18px;
  vertical-align: middle;
  font-size: 13.5px;
}

/* Badges — softer background variants + weight */
.badge {
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 5px 10px;
}
.bg-label-success { background-color: rgba(113, 221, 55, 0.14) !important; color: #71dd37 !important; }
.bg-label-danger  { background-color: rgba(244, 82, 107, 0.14) !important; color: #f4526b !important; }
.bg-label-warning { background-color: rgba(255, 171, 0, 0.14)  !important; color: #ffab00 !important; }
.bg-label-info    { background-color: rgba(2, 195, 234, 0.14)  !important; color: #03c3ec !important; }
.bg-label-primary { background-color: rgba(255, 122, 47, 0.14) !important; color: var(--ir-brand) !important; }

/* Progress bars — subtle rounding + brand fill */
.progress {
  height: 8px;
  border-radius: 999px;
  overflow: hidden;
}
.progress-bar {
  background: linear-gradient(90deg, var(--ir-brand), var(--ir-brand-2));
  transition: width 300ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* Form controls — brand focus */
.form-control:focus,
.form-select:focus {
  border-color: var(--ir-brand);
  box-shadow: 0 0 0 0.15rem rgba(255, 122, 47, 0.15);
}
.form-check-input:checked {
  background-color: var(--ir-brand);
  border-color: var(--ir-brand);
}
.form-check-input:focus {
  border-color: var(--ir-brand);
  box-shadow: 0 0 0 0.15rem rgba(255, 122, 47, 0.15);
}

/* Sidebar brand — remove the ugly text-primary color on the mark background */
.app-brand-link .app-brand-logo > span {
  color: #fff !important;
}
.app-brand-text {
  color: var(--bs-heading-color) !important;
  font-size: 18px !important;
}

/* Hide the broken orange collapse-toggle circle — sidebar stays expanded.
   (Sneat's toggle renders as an empty orange circle because the FA icon
   doesn't pick up its block-size/inline-size sizing.) */
.menu .app-brand .layout-menu-toggle { display: none !important; }

/* Sidebar menu items — subtle icon color, sharper active */
.menu-vertical .menu-item .menu-link {
  padding: 10px 14px;
  border-radius: 8px;
  margin: 2px 6px;
  font-size: 14px;
}
.menu-vertical .menu-item .menu-link .menu-icon {
  font-size: 18px;
  margin-right: 10px;
  color: var(--bs-secondary-color);
}
.menu-vertical .menu-item.active .menu-link .menu-icon { color: #fff; }

/* Topbar clock + logout — tighten spacing */
[data-server-clock] {
  min-width: 172px;
  text-align: center;
  padding: 7px 14px !important;
}

/* Alerts (flash messages) */
.alert {
  border-radius: 8px;
  padding: 12px 16px;
  font-size: 13.5px;
}
.alert i.icon-base { font-size: 18px; flex-shrink: 0; }

/* Toast improvements (Bootstrap 5 toast component we emit from JS) */
.toast {
  min-width: 320px;
  border: 1px solid var(--bs-border-color);
  box-shadow: 0 12px 32px -12px rgba(0, 0, 0, 0.25);
  border-radius: 10px;
}
.toast-body { padding: 14px 16px; font-size: 13.5px; }
.toast-header {
  padding: 10px 14px;
  font-weight: 600;
  background: transparent;
  border-bottom: 1px solid var(--bs-border-color);
}
.toast.bg-danger  .toast-body,
.toast.bg-danger  .toast-header,
.toast.bg-success .toast-body,
.toast.bg-success .toast-header,
.toast.bg-primary .toast-body,
.toast.bg-primary .toast-header,
.toast.bg-warning .toast-body,
.toast.bg-warning .toast-header { color: #fff; }
.toast.bg-warning .toast-body,
.toast.bg-warning .toast-header { color: #1a1500; }

/* Buttons — better sizing rhythm */
.btn { font-weight: 500; }
.btn-primary,
.btn-danger,
.btn-success,
.btn-info,
.btn-warning { font-weight: 600; }
.btn-lg { padding: 12px 20px; font-size: 15px; }
.btn-icon {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.btn-icon.btn-sm { width: 32px; height: 32px; }
.btn-group > .btn + .btn { margin-left: 4px; }

/* Container padding rhythm */
.content-wrapper > .container-xxl { padding-top: 28px; }

/* ═══════════════════════════════════════════════════════════════════════════ */
/* SMOOTH MOTION — modals, page transitions, hovers                           */
/* ═══════════════════════════════════════════════════════════════════════════ */

/* ─── MODAL: gentle rise + fade instead of the abrupt -100px/scale(0.8) jump ─── */
/* Bootstrap appends the backdrop to <body>. Keep these layers explicit so a
   layout stacking context can never cover the active dialog. */
.modal { z-index: 1095; }
.modal-backdrop { z-index: 1090; }

.modal.fade .modal-dialog {
  transition: transform 0.32s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.28s ease-out;
  transform: translateY(18px) scale(0.97);
  opacity: 0;
}
.modal.show .modal-dialog {
  transform: translateY(0) scale(1);
  opacity: 1;
}
.modal-backdrop.fade      { transition: opacity 0.28s ease-out; }
.modal-backdrop.show      { opacity: 0.5; }
.modal-content {
  border: none;
  border-radius: 14px;
  box-shadow: 0 24px 60px -20px rgba(15, 20, 34, 0.35), 0 0 0 1px rgba(15,20,34,0.04);
}

/* ─── MODAL CLOSE (X): clean circular button INSIDE the header ─── */
.modal-header {
  padding: 20px 22px;
  border-bottom: 1px solid var(--bs-border-color);
  align-items: center;
}
.modal-header .modal-title {
  font-size: 16px;
  font-weight: 600;
}
.modal-header .btn-close {
  margin: 0;                       /* kill Sneat's negative margins that detach it */
  padding: 0;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background-color: transparent;
  background-size: 0.75em;
  opacity: 0.6;
  transition: background-color 160ms ease-out, opacity 160ms ease-out, transform 160ms ease-out;
}
.modal-header .btn-close:hover {
  background-color: rgba(244, 82, 107, 0.12);
  opacity: 1;
  transform: rotate(90deg);
}
.modal-header .btn-close:focus { box-shadow: none; }
.modal-body   { padding: 22px; }
/* Sneat's core.css sets `.modal .modal-footer { padding: 0 1.5rem 1.5rem }` and
   strips margins from the footer children, which glues the buttons to the
   border-top and crowds them against each other. Re-raise with equal specificity
   so the buttons breathe, and use flex `gap` instead of per-child margins so
   spacing is consistent regardless of button count. */
.modal .modal-footer {
  padding: 16px 22px;
  gap: 10px;
  border-top: 1px solid var(--bs-border-color);
}
.modal .modal-footer > * { margin: 0; }

/* ─── PAGE CONTENT: subtle fade+rise on every server-rendered tab load ─── */
@keyframes ir-page-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.content-wrapper > .container-xxl {
  animation: ir-page-in 0.34s cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* Stagger cards slightly so a grid doesn't pop in all at once */
@keyframes ir-card-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
.content-wrapper .card {
  animation: ir-card-in 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.content-wrapper .row > [class*="col-"]:nth-child(1) .card { animation-delay: 0.02s; }
.content-wrapper .row > [class*="col-"]:nth-child(2) .card { animation-delay: 0.06s; }
.content-wrapper .row > [class*="col-"]:nth-child(3) .card { animation-delay: 0.10s; }
.content-wrapper .row > [class*="col-"]:nth-child(4) .card { animation-delay: 0.14s; }
.content-wrapper .row > [class*="col-"]:nth-child(5) .card { animation-delay: 0.18s; }
.content-wrapper .row > [class*="col-"]:nth-child(6) .card { animation-delay: 0.22s; }

/* ─── SIDEBAR menu items: smooth hover slide ─── */
.menu-vertical .menu-item .menu-link {
  transition: background-color 200ms ease-out, color 200ms ease-out, padding-inline-start 180ms ease-out;
}
.menu-vertical .menu-item:not(.active) .menu-link:hover {
  padding-inline-start: 18px;
}

/* ─── NAV PILLS: animated active fill ─── */
.nav-pills .nav-link {
  transition: color 180ms ease-out, background-color 240ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 240ms ease-out;
}

/* ─── TABLE ROWS: gentle hover ─── */
.table > tbody > tr {
  transition: background-color 160ms ease-out;
}

/* ─── BUTTONS: unified smooth press ─── */
.btn {
  transition: background-color 180ms ease-out, border-color 180ms ease-out,
              color 180ms ease-out, box-shadow 200ms ease-out, transform 120ms ease-out;
}
.btn:active { transform: translateY(1px) scale(0.99); }

/* ─── TOASTS: slide in from the right smoothly ─── */
@keyframes ir-toast-in {
  from { opacity: 0; transform: translateX(24px); }
  to   { opacity: 1; transform: translateX(0); }
}
.bs-toast-stack .toast.show {
  animation: ir-toast-in 0.34s cubic-bezier(0.16, 1, 0.3, 1);
}

/* ─── ALERTS: gentle drop-in ─── */
.content-wrapper > .container-xxl > .alert {
  animation: ir-page-in 0.3s ease-out both;
}

/* ─── SEGMENTED CONTROL (Operations filter) — clean, single-accent ─── */
.ops-segment {
  display: inline-flex;
  padding: 3px;
  background: rgba(0, 0, 0, 0.04);
  border: 1px solid var(--bs-border-color);
  border-radius: 8px;
  gap: 2px;
}
.ops-seg-btn {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--bs-secondary-color);
  font-size: 13px;
  font-weight: 500;
  padding: 6px 16px;
  border-radius: 6px;
  cursor: pointer;
  transition: color 160ms ease-out, background-color 220ms cubic-bezier(0.16,1,0.3,1), box-shadow 220ms ease-out;
}
.ops-seg-btn:hover { color: var(--bs-heading-color); }
.ops-seg-btn.active {
  color: #fff;
  background: linear-gradient(180deg, var(--ir-brand), var(--ir-brand-2));
  box-shadow: 0 3px 8px -3px rgba(255, 122, 47, 0.5);
}

/* ─── DEVICE THUMBNAIL (Models table) ─── */
.ir-device-thumb {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.03);
  border: 1px solid var(--bs-border-color);
  overflow: hidden;
}
.ir-device-thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: 3px;
}
.ir-device-thumb__fallback {
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  color: var(--bs-secondary-color);
  font-size: 16px;
}
.ir-device-thumb__fallback { display: inline-flex; }

/* ─── ICON BUTTONS: cohesive hover feedback across the panel ─── */
.btn-icon {
  transition: background-color 160ms ease-out, border-color 160ms ease-out,
              color 160ms ease-out, transform 140ms cubic-bezier(0.16,1,0.3,1),
              box-shadow 180ms ease-out;
}
.btn-icon:hover { transform: translateY(-1px) scale(1.05); }
.btn-icon:active { transform: translateY(0) scale(0.96); }

/* Action button groups in tables get a touch of breathing room */
.btn-group-sm > .btn-icon + .btn-icon { margin-left: 4px; }

/* Outline buttons: fill smoothly on hover instead of a hard swap */
.btn-outline-primary,
.btn-outline-success,
.btn-outline-danger,
.btn-outline-warning,
.btn-outline-info,
.btn-outline-secondary {
  transition: background-color 200ms ease-out, border-color 200ms ease-out,
              color 200ms ease-out, box-shadow 200ms ease-out, transform 120ms ease-out;
}

/* KPI numbers count-up feel: subtle scale-in */
@keyframes ir-kpi-in {
  from { opacity: 0; transform: scale(0.92); }
  to   { opacity: 1; transform: scale(1); }
}
.kpi-value { animation: ir-kpi-in 0.45s cubic-bezier(0.16,1,0.3,1) both; }

/* Badges: gentle appear */
.badge { transition: background-color 180ms ease-out, color 180ms ease-out; }

/* Modal-lg for the versions detail */
@media (min-width: 992px) {
  .modal-lg { --bs-modal-width: 640px; }
}

/* Input-group text (search icon, $) neutral border blend */
.input-group-text {
  background: rgba(0,0,0,0.02);
  border-color: var(--bs-border-color);
  color: var(--bs-secondary-color);
}

/* ─── STICKY TABLE HEADERS on tall tables (>12 rows worth) ─── */
.table-responsive { max-height: 620px; }
.table > thead > tr > th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bs-body-bg);
  box-shadow: inset 0 -1px 0 var(--bs-border-color);
}

/* ─── LIVE BADGE PULSE (active/online states) ─── */
.badge-live::before {
  content: "";
  display: inline-block;
  width: 6px; height: 6px;
  margin-right: 6px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 0 0 currentColor;
  animation: ir-badge-pulse 1.8s ease-out infinite;
  vertical-align: middle;
}
@keyframes ir-badge-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(113,221,55,0.5); }
  70%  { box-shadow: 0 0 0 7px rgba(113,221,55,0); }
  100% { box-shadow: 0 0 0 0 rgba(113,221,55,0); }
}

/* ─── PROGRESS BARS: animate fill width from 0 on first paint ─── */
@keyframes ir-progress-fill { from { width: 0 !important; } }
.content-wrapper .progress-bar { animation: ir-progress-fill 0.9s cubic-bezier(0.16,1,0.3,1) both; }

/* ─── STAT-CARD icon tile hover wiggle ─── */
.stat-card .card-body > span:first-child { transition: transform 220ms cubic-bezier(0.16,1,0.3,1); }
.stat-card:hover .card-body > span:first-child { transform: scale(1.08) rotate(-4deg); }

/* ─── CHART cards: gentle reveal ─── */
#chartActivity, #chartModels { animation: ir-page-in 0.5s cubic-bezier(0.16,1,0.3,1) both; }

/* ─── FOCUS-VISIBLE consistency (a11y) ─── */
.btn:focus-visible,
.form-control:focus-visible,
.form-select:focus-visible,
.nav-link:focus-visible,
.menu-link:focus-visible,
.ops-seg-btn:focus-visible {
  outline: 2px solid var(--ir-brand);
  outline-offset: 2px;
}

/* ─── BUTTON BUSY STATE (submit spinner without innerHTML swap) ─── */
/* .is-busy is added on submit by ir-admin.js. It fades the label + centers
   a small spinner over the button.
   Critical: Bootstrap's :disabled state OVERRIDES the button's color/bg/border
   via --bs-btn-disabled-* vars — that's why outline buttons looked filled with
   a dark "layer" during processing. We re-assert the original visual state
   here by pointing those vars back to the live theme vars. */
.btn.is-busy {
  position: relative;
  pointer-events: none;
  cursor: wait;
  color: transparent !important;
  /* Re-assert live styling so :disabled doesn't paint a dark overlay */
  --bs-btn-disabled-color:        var(--bs-btn-color);
  --bs-btn-disabled-bg:           var(--bs-btn-bg);
  --bs-btn-disabled-border-color: var(--bs-btn-border-color);
  --bs-btn-disabled-opacity:      1;
  opacity: 1 !important;
}
.btn.is-busy > * { opacity: 0; }   /* fully hide label/icons while spinner shows */
.btn.is-busy::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 16px; height: 16px;
  margin: -8px 0 0 -8px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-right-color: transparent;
  opacity: 1;
  color: inherit;
  animation: ir-btn-spin 0.7s linear infinite;
}
/* Filled buttons — spinner uses the fill's foreground (typically white) */
.btn.is-busy.btn-primary,
.btn.is-busy.btn-success,
.btn.is-busy.btn-danger,
.btn.is-busy.btn-warning,
.btn.is-busy.btn-info,
.btn.is-busy.btn-dark {
  /* Force the SAME filled colors as normal state — no dimming */
  background-color: var(--bs-btn-hover-bg, var(--bs-btn-bg)) !important;
  border-color: var(--bs-btn-hover-border-color, var(--bs-btn-border-color)) !important;
}
.btn.is-busy.btn-primary::after,
.btn.is-busy.btn-success::after,
.btn.is-busy.btn-danger::after,
.btn.is-busy.btn-warning::after,
.btn.is-busy.btn-info::after,
.btn.is-busy.btn-dark::after {
  border-color: #fff;
  border-right-color: transparent;
}
/* Outline buttons — keep transparent bg, border stays the theme color */
.btn.is-busy[class*="btn-outline-"] {
  background-color: transparent !important;
}
@keyframes ir-btn-spin { to { transform: rotate(360deg); } }

/* ─── TIMELINE (device history / process log) ─── */
.ir-timeline { list-style: none; margin: 0; padding: 0 0 0 6px; max-height: 40vh; overflow-y: auto; }
.ir-tl {
  position: relative;
  padding: 0 0 16px 22px;
  border-left: 2px solid var(--bs-border-color);
}
.ir-tl:last-child { border-left-color: transparent; padding-bottom: 4px; }
.ir-tl::before {
  content: "";
  position: absolute; left: -7px; top: 2px;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--bs-secondary-color);
  box-shadow: 0 0 0 3px var(--bs-card-bg);
}
.ir-tl--ok::before  { background: #71dd37; }
.ir-tl--bad::before { background: #f4526b; }
.ir-tl--info::before{ background: #03c3ec; }
.ir-tl__time { font-size: 11px; color: var(--bs-secondary-color); margin-bottom: 2px; }
.ir-tl__body { font-size: 13.5px; }

/* ─── Respect reduced-motion ─── */
@media (prefers-reduced-motion: reduce) {
  .kpi-value, .btn-icon, .content-wrapper .progress-bar,
  #chartActivity, #chartModels, .badge-live::before { animation: none !important; }
  .content-wrapper > .container-xxl,
  .content-wrapper .card,
  .bs-toast-stack .toast.show,
  .modal.fade .modal-dialog {
    animation: none !important;
    transition: none !important;
    transform: none !important;
    opacity: 1 !important;
  }
}
