/* ==========================================================================
   EnMon / Posho Mill: modern UI layer, v2 (Metronic 8.3.3 / Bootstrap 5.3)
   --------------------------------------------------------------------------
   Loaded by layout/v2/shell_open.php after Metronic's style.bundle.css and
   bs4-compat.css. Metronic provides the components; this file only:
     0. ties the dashboard's --ui-* tokens and Metronic's --bs-* variables
        together, both ways:
        - the user's skin colour becomes Metronic's primary;
        - every other --ui-* token is an alias of a Metronic variable, so
          page-local CSS written for v1 (var(--ui-border) etc.) picks up
          Metronic's palette and follows light/dark automatically;
     1. styles the few shell pieces Metronic has no class for (brand, avatar,
        accent swatches, phone bottom bar);
     2. keeps the shared ui-* helpers pages already use (.ui-kpi,
        .ui-fill-frame, .ui-table-scroll, .ui-signal ...).
   Dark mode is Metronic's: <html data-bs-theme="dark">.
   --ui-primary / -hover / -rgb are emitted per request by shell_open.php.
   Design notes: web/docs/ui_modernization.md (Phase 2)
   ========================================================================== */

/* ---------- 0. Tokens ---------------------------------------------------- */
:root {
  --ui-primary: #5e2dd8;
  --ui-primary-hover: #5425ca;
  --ui-primary-rgb: 94, 45, 216;
  --ui-primary-soft: rgba(var(--ui-primary-rgb), .10);
  --ui-primary-ring: rgba(var(--ui-primary-rgb), .28);

  /* Aliases of Metronic's palette (resolved per theme) */
  --ui-bg: var(--bs-app-bg-color);
  --ui-surface: var(--bs-body-bg);
  --ui-surface-2: var(--bs-gray-100);
  --ui-surface-hover: var(--bs-gray-100);
  --ui-border: var(--bs-border-color);
  --ui-border-strong: var(--bs-gray-300);
  --ui-text: var(--bs-gray-900);
  --ui-text-2: var(--bs-gray-700);
  --ui-muted: var(--bs-text-muted);
  --ui-success: var(--bs-success);  --ui-success-soft: var(--bs-success-light);
  --ui-warning: var(--bs-warning);  --ui-warning-soft: var(--bs-warning-light);
  --ui-danger:  var(--bs-danger);   --ui-danger-soft:  var(--bs-danger-light);
  --ui-info:    var(--bs-info);     --ui-info-soft:    var(--bs-info-light);

  --ui-radius-sm: .475rem;
  --ui-radius: .625rem;
  --ui-radius-lg: .75rem;
  --ui-shadow-sm: var(--bs-box-shadow-sm);
  --ui-shadow: var(--bs-box-shadow);
  --ui-shadow-lg: var(--bs-box-shadow-lg);

  --ui-font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  --ui-font-mono: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
  --ui-sidebar-dark: #0D0E12; /* Metronic's dark-sidebar background (style.bundle.css), for the theme-menu preview */
  --ui-bottom-nav-h: 60px;

  /* Ordered magnitude scale for bucketed quantities (/energy tiers).
     Same values as v1; dark mode re-steps it so higher still reads stronger. */
  --ui-energy-0: #c3c7d2;
  --ui-energy-1: #86b6ef;
  --ui-energy-2: #3987e5;
  --ui-energy-3: #1c5cab;
  --ui-energy-4: #0d366b;
}
[data-bs-theme=dark] {
  --ui-primary-soft: rgba(var(--ui-primary-rgb), .20);
  --ui-primary-ring: rgba(var(--ui-primary-rgb), .45);
  --ui-surface-2: var(--bs-gray-100);
  --ui-surface-hover: var(--bs-gray-200);
  --ui-energy-0: #4a4e5e;
  --ui-energy-1: #184f95;
  --ui-energy-2: #2a78d6;
  --ui-energy-3: #6da7ec;
  --ui-energy-4: #b7d3f6;
}

/* Skin colour -> Metronic primary, in both themes (html carries both the
   :root and the [data-bs-theme] rules, so :root[...] wins on specificity) */
:root[data-bs-theme] {
  --bs-primary: var(--ui-primary);
  --bs-primary-rgb: var(--ui-primary-rgb);
  --bs-primary-active: var(--ui-primary-hover);
  --bs-primary-light: var(--ui-primary-soft);
  --bs-primary-clarity: rgba(var(--ui-primary-rgb), .2);
  --bs-primary-inverse: #ffffff;
  --bs-text-primary: var(--ui-primary);
  --bs-link-color: var(--ui-primary);
  --bs-link-color-rgb: var(--ui-primary-rgb);
  --bs-link-hover-color: var(--ui-primary-hover);
  --bs-component-active-bg: var(--ui-primary);
  --bs-component-hover-color: var(--ui-primary);
  --bs-component-checked-bg: var(--ui-primary);
  --bs-input-focus-border-color: rgba(var(--ui-primary-rgb), .5);
  /* Metronic dropdown menus (theme and user menus), the back-to-top button and
     ribbons had their own hard-coded blue (#1B84FF / #006AE6) */
  --bs-menu-link-color-hover: var(--ui-primary);
  --bs-menu-link-color-show: var(--ui-primary);
  --bs-menu-link-color-here: var(--ui-primary);
  --bs-menu-link-color-active: var(--ui-primary);
  --bs-scrolltop-bg-color: var(--ui-primary);
  --bs-scrolltop-bg-color-hover: var(--ui-primary-hover);
  --bs-ribbon-label-bg: var(--ui-primary);
  /* No web font: system stack (no Google Fonts request, no firewall change) */
  --bs-font-sans-serif: var(--ui-font);
  --bs-body-font-family: var(--ui-font);
  --bs-font-monospace: var(--ui-font-mono);
}

/* ---------- 0b. Readability: text contrast, dividers, controls ------------ */
/* Audit 2026-10-29 (web/docs/ui_readability.md): Metronic's muted greys read
   at 2.5:1 (light) / 3.1:1 (dark), under WCAG AA's 4.5:1, and drive
   breadcrumbs, table headers, secondary cell text, KPI labels, form help,
   placeholders and the footer. Its divider colour is ~1.1:1 (table lines and
   card edges nearly invisible). Each colour below was measured against every
   surface it sits on (card, page, grey surface, its tint): text >= 4.5:1,
   form-control outlines >= 3:1, dividers ~1.4-1.5:1. :root[...] beats
   Metronic's :root / [data-bs-theme] declarations on specificity. */
:root[data-bs-theme=light] {
  --bs-text-muted: #5e6578;           /* was #99A1B7, 2.6:1 -> 5.8:1 on white */
  --bs-gray-500: #666d80;             /* placeholders, muted labels: 5.2:1 */
  --bs-gray-600: #575e72;             /* secondary text: 6.3:1 */
  --bs-menu-heading-color: #5e6578;
  --bs-border-color: #d6dae4;         /* table/card/section dividers, ~1.4:1 */
  --bs-table-border-color: #d6dae4;
  --bs-app-sidebar-light-menu-heading-color: #666d80;   /* was #B5B5C3, 1.9:1 */
  --bs-app-sidebar-light-menu-link-icon-color: #78829d;
  --ui-input-border: #848ca1;         /* >= 3.0:1 on white, grey surface and page: a control's edge must be findable */
  --ui-divider: #d6dae4;
  /* status colours as TEXT (badges, text-success ...); fills keep the originals */
  --ui-success-text: #0f8136;         /* was #17C653, 2.1:1 on its tint */
  --ui-warning-text: #8a6c00;         /* was #F6C000, 1.6:1 */
  --ui-danger-text: #d0224c;          /* was #F8285A, 3.5:1 */
  --ui-info-text: #7239ea;
}
:root[data-bs-theme=dark] {
  --bs-text-muted: #9497a8;           /* was #636674, 3.1:1 -> 6.2:1 on cards */
  --bs-gray-500: #8e91a1;             /* 5.7:1 */
  --bs-gray-600: #9a9cae;             /* 6.6:1 */
  --bs-gray-700: #b5b7c6;             /* secondary text, body-adjacent */
  --bs-menu-heading-color: #9497a8;
  --bs-border-color: #363843;         /* ~1.5:1 on cards (was 1.2:1) */
  --bs-table-border-color: #363843;
  --bs-app-sidebar-light-menu-heading-color: #8e91a1;
  --bs-app-sidebar-light-menu-link-icon-color: #8e91a1;
  --ui-input-border: #6b6e7d;         /* 3.5:1 */
  --ui-divider: #363843;
  --ui-success-text: #17c653;
  --ui-warning-text: #f6c000;
  --ui-danger-text: #f94a74;          /* was #E42855, 3.5:1 on its tint */
  --ui-info-text: #a27cf1;
}
/* the accent as TEXT uses its readable shade (--ui-primary-text, per skin and
   theme from layout/ui_skin.php); buttons, bars and fills keep --ui-primary */
:root[data-bs-theme] {
  --bs-text-primary: var(--ui-primary-text);
  --bs-link-color: var(--ui-primary-text);
  --bs-link-color-rgb: var(--ui-primary-text-rgb);
  --bs-link-hover-color: var(--ui-primary-text);
  --bs-menu-link-color-hover: var(--ui-primary-text);
  --bs-menu-link-color-show: var(--ui-primary-text);
  --bs-menu-link-color-here: var(--ui-primary-text);
  --bs-menu-link-color-active: var(--ui-primary-text);
}
.text-primary { color: var(--ui-primary-text) !important; }
.text-success { color: var(--ui-success-text) !important; }
.text-warning { color: var(--ui-warning-text) !important; }
.text-danger  { color: var(--ui-danger-text) !important; }
.text-info    { color: var(--ui-info-text) !important; }
.badge-light-primary { color: var(--ui-primary-text); }
.badge-light-success { color: var(--ui-success-text); }
.badge-light-warning { color: var(--ui-warning-text); }
.badge-light-danger  { color: var(--ui-danger-text); }
.badge-light-info    { color: var(--ui-info-text); }
.badge-light-secondary, .badge-light-dark { color: var(--bs-gray-700); }
/* "light" buttons: readable text at rest; the filled hover/active state is unchanged */
.btn.btn-light-primary:not(:hover):not(:focus):not(:active):not(.active) { color: var(--ui-primary-text); }
.btn.btn-light-success:not(:hover):not(:focus):not(:active):not(.active) { color: var(--ui-success-text); }
.btn.btn-light-warning:not(:hover):not(:focus):not(:active):not(.active) { color: var(--ui-warning-text); }
.btn.btn-light-danger:not(:hover):not(:focus):not(:active):not(.active)  { color: var(--ui-danger-text); }
.btn.btn-light-info:not(:hover):not(:focus):not(:active):not(.active)    { color: var(--ui-info-text); }
.symbol-label:not([class*="bg-"]):not([class*="text-"]) { color: var(--ui-primary-text); }
/* form controls: a findable edge (Metronic's is 1.3:1). Solid (filled) fields
   get the same edge, so they don't vanish into a grey card. */
.form-control, .form-select { border-color: var(--ui-input-border); }
.form-control.form-control-solid, .form-select.form-select-solid { border-color: var(--ui-input-border); }
.form-control:focus, .form-select:focus { border-color: var(--ui-primary); }
.form-control:disabled, .form-select:disabled { border-color: var(--ui-divider); }
.form-check-input:not(:checked) { border: 1px solid var(--ui-input-border); }
/* dark sidebar: section headings and icons (Metronic: 3.4:1 and 2.1:1) */
[data-kt-app-layout=dark-sidebar] .app-sidebar .menu .menu-item .menu-heading { color: #878a9c !important; }
[data-kt-app-layout=dark-sidebar] .app-sidebar .menu .menu-item .menu-link .menu-icon,
[data-kt-app-layout=dark-sidebar] .app-sidebar .menu .menu-item .menu-link .menu-icon i { color: #7e8193; }
[data-kt-app-layout=dark-sidebar] .app-sidebar .menu .menu-item .menu-link .menu-bullet .bullet { background-color: #7e8193; }
[data-kt-app-layout=dark-sidebar] .app-sidebar .menu .menu-item .menu-link .menu-title { color: #b2b4c4; }
[data-kt-app-layout=dark-sidebar] .app-sidebar .menu .menu-item .menu-link:hover .menu-title,
[data-kt-app-layout=dark-sidebar] .app-sidebar .menu .menu-item .menu-link.active .menu-title,
[data-kt-app-layout=dark-sidebar] .app-sidebar .menu .menu-item.here > .menu-link .menu-title { color: #fff; }

/* Page vs cards: Metronic's light page (#fcfcfc) and white cards were 1.03:1,
   with a 1.1:1 edge, so cards didn't separate. A soft grey page and a defined
   card edge (with Metronic's shadow) fix that in both themes. */
:root[data-bs-theme=light] { --bs-app-bg-color: #f3f4f7; --ui-card-border: #dfe2ea; }
:root[data-bs-theme=dark]  { --ui-card-border: #2c2e38; }
.card { border: 1px solid var(--ui-card-border); }
.card .card { border-color: var(--ui-divider); }
/* alerts and info surfaces: readable status text (Metronic dark info was 3.1:1) */
.alert-primary, .alert-primary .alert-heading { color: var(--ui-primary-text); }
.alert-success, .alert-success .alert-heading { color: var(--ui-success-text); }
.alert-warning, .alert-warning .alert-heading { color: var(--ui-warning-text); }
.alert-danger,  .alert-danger .alert-heading  { color: var(--ui-danger-text); }
.alert-info,    .alert-info .alert-heading    { color: var(--ui-info-text); }
.btn-info-light { color: var(--ui-info-text) !important; }
[data-bs-theme=dark] code { color: #cf78b6; }   /* was #B93993, 2.7:1 */
/* dropdown items: the active one in the readable accent on its tint */
.dropdown-menu { --bs-dropdown-link-active-color: var(--ui-primary-text); --bs-dropdown-link-active-bg: var(--ui-primary-soft); }
/* initials avatars: near-white on the tint in dark mode (the accent tint is dark) */
[data-bs-theme=dark] .ui-avatar, [data-bs-theme=dark] .ui-cell-user .symbol-label { color: var(--bs-gray-900) !important; }
/* grey "light" buttons (Cancel, History ...): Metronic dark text was 4.46:1 */
.btn.btn-light:not(:hover):not(:focus):not(:active):not(.active) { color: var(--ui-text-2); }

/* Minimum text size: nothing a person has to read is under 12px. Metronic's
   root is 13px, so its .875em "small", .85rem table heads and .8rem labels
   came out at 10.4-11.4px. */
small, .small { font-size: max(.925em, 12px); }
.badge { font-size: max(.925rem, 12px); }
.table > thead > tr > th, .table > tfoot > tr > td { font-size: max(.925rem, 12px); }
.fs-8 { font-size: max(.925rem, 12px) !important; }

/* ---------- 1. Base ------------------------------------------------------ */
body { font-family: var(--ui-font); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
table, .number-font, .count, .kpi-value { font-variant-numeric: tabular-nums; }
::selection { background: rgba(var(--ui-primary-rgb), .2); }
.form-check-input:checked { background-color: var(--ui-primary); border-color: var(--ui-primary); }
/* Cards stack with a gap, as in the v1 template (Metronic cards have no
   margin). Kept early and low-specificity so .mb-0 and the component rules
   below (KPI tiles, detail cards) still win. */
.app-content .card { margin-bottom: 1.25rem; }
/* Cards inside cards (gauge tiles, detail panes) are compact: Metronic's
   2rem/2.25rem padding leaves too little room for their content. */
.card .card > .card-body { padding: 1.25rem; }
.card .card > .card-header { padding: 0 1.25rem; min-height: 56px; }

.ui-skip-link { position: absolute; left: -9999px; top: 8px; z-index: 2000; padding: 8px 14px; border-radius: var(--ui-radius-sm);
                background: var(--ui-primary); color: #fff; }
.ui-skip-link:focus { left: 8px; color: #fff; }

/* ---------- 2. Shell ----------------------------------------------------- */
.ui-brand { letter-spacing: -.01em; }
/* Brand text: white on the dark sidebar, normal text on the light one (theme menu: Sidebar) */
[data-kt-app-layout=dark-sidebar] .ui-brand, [data-kt-app-layout=dark-sidebar] .ui-brand:hover { color: #fff; }
[data-kt-app-layout=light-sidebar] .ui-brand, [data-kt-app-layout=light-sidebar] .ui-brand:hover { color: var(--ui-text); }
/* Light sidebar: active page tinted with the accent, like the dark sidebar */
[data-kt-app-layout=light-sidebar] .app-sidebar .menu .menu-item .menu-link.active { background-color: var(--ui-primary-soft); }
.ui-brand img { flex-shrink: 0; }
/* Minimised desktop sidebar: icon only */
[data-kt-app-sidebar-minimize=on]:not([data-kt-app-sidebar-hoverable=true]) .app-sidebar .ui-brand span,
[data-kt-app-sidebar-minimize=on][data-kt-app-sidebar-hoverable=true] .app-sidebar:not(:hover) .ui-brand span { display: none; }

/* Sidebar menu: themify/feather glyphs in the icon slot, and the active page
   in the accent colour (Metronic's dark sidebar uses grey only) */
.app-sidebar .menu .menu-icon i { line-height: 1; }
[data-kt-app-layout=dark-sidebar] .app-sidebar .menu .menu-item .menu-link.active {
  background-color: rgba(var(--ui-primary-rgb), .22);
}
[data-kt-app-layout=dark-sidebar] .app-sidebar .menu .menu-item .menu-link.active .menu-icon i { color: #fff; }
[data-kt-app-layout=dark-sidebar] .app-sidebar .menu .menu-item .menu-link.active .menu-bullet .bullet { background-color: var(--ui-primary); }

.ui-avatar { background: var(--ui-primary-soft) !important; color: var(--ui-primary-text) !important; font-size: .95rem; }

.ui-swatch {
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--sw);
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .35);
  display: inline-block; flex-shrink: 0;
}
.ui-swatch:hover { transform: scale(1.08); }

/* Theme menu: sidebar style tiles (a small page preview: sidebar strip + content) */
.ui-sidebar-pick {
  flex: 1 1 0; display: flex; flex-direction: column; align-items: center; gap: .35rem;
  padding: .4rem .4rem .3rem; border-radius: var(--ui-radius-sm); border: 1px solid var(--ui-border);
  color: var(--ui-text-2); font-size: .925rem; font-weight: 600; text-decoration: none;
}
.ui-sidebar-pick:hover { color: var(--ui-primary-text); border-color: var(--ui-primary); }
.ui-sidebar-pick.is-active { color: var(--ui-primary-text); box-shadow: 0 0 0 2px var(--ui-primary); border-color: transparent; }
.ui-sidebar-pick > span {
  width: 100%; height: 30px; border-radius: 4px; border: 1px solid var(--ui-border);
  background: linear-gradient(90deg, var(--ui-pick-side) 0 34%, var(--ui-surface-2) 34%);
}
/* Dark mode: the choice only shows in light mode (Metronic's light sidebar
   turns dark with the page), so the tiles are dimmed but still clickable to
   set the light-mode style; a note under them says so. */
.ui-sidebar-picks-off .ui-sidebar-pick > span { opacity: .45; }
.ui-sidebar-picks-off .ui-sidebar-pick:not(.is-active) { color: var(--ui-muted); }
.ui-sidebar-picks-off .ui-sidebar-pick:hover > span, .ui-sidebar-picks-off .ui-sidebar-pick:focus-visible > span { opacity: 1; }
.ui-sidebar-note { padding: 0 1.5rem .35rem; font-size: .925rem; color: var(--ui-muted); line-height: 1.35; }
.ui-sidebar-pick.is-dark { --ui-pick-side: var(--ui-sidebar-dark); }
/* always previews the light-mode look (white), which is when the choice applies */
.ui-sidebar-pick.is-light { --ui-pick-side: var(--bs-white, #fff); }
.ui-swatch.is-active { box-shadow: 0 0 0 2px var(--bs-body-bg), 0 0 0 4px var(--sw); }

.app-toolbar .page-heading { letter-spacing: -.01em; }

/* Bootstrap's plain .btn-outline-{colour} (30+ uses across the pages):
   Metronic removes the border of every .btn without .btn-outline and
   hard-codes its own blue. Drawn with an inset ring instead of a border, so
   an outline button stays the same height as a filled one beside it. */
.btn.btn-outline-primary:not(.btn-outline)   { --ui-btn: var(--bs-primary); --ui-btn-text: var(--ui-primary-text); --ui-btn-on: var(--bs-primary-inverse); }
.btn.btn-outline-secondary:not(.btn-outline) { --ui-btn: var(--bs-gray-600); --ui-btn-on: var(--bs-body-bg); }
.btn.btn-outline-success:not(.btn-outline)   { --ui-btn: var(--bs-success); --ui-btn-text: var(--ui-success-text); --ui-btn-on: var(--bs-success-inverse); }
.btn.btn-outline-danger:not(.btn-outline)    { --ui-btn: var(--bs-danger);  --ui-btn-text: var(--ui-danger-text); --ui-btn-on: var(--bs-danger-inverse); }
.btn.btn-outline-warning:not(.btn-outline)   { --ui-btn: var(--bs-warning); --ui-btn-text: var(--ui-warning-text); --ui-btn-on: var(--bs-warning-inverse); }
.btn.btn-outline-info:not(.btn-outline)      { --ui-btn: var(--bs-info);    --ui-btn-text: var(--ui-info-text); --ui-btn-on: var(--bs-info-inverse); }
.btn[class*="btn-outline-"]:not(.btn-outline) {
  /* !important: Metronic's .btn:not(.btn-shadow):not(...) clears box-shadow at (0,6,0) */
  color: var(--ui-btn-text, var(--ui-btn)); background-color: transparent; box-shadow: inset 0 0 0 1px var(--ui-btn) !important;
}
.btn[class*="btn-outline-"]:not(.btn-outline):hover:not(:disabled),
.btn[class*="btn-outline-"]:not(.btn-outline).active,
.btn[class*="btn-outline-"]:not(.btn-outline):active {
  color: var(--ui-btn-on); background-color: var(--ui-btn);
}
.btn[class*="btn-outline-"]:not(.btn-outline) i { color: inherit; }

/* Card headers: Metronic stretches header children to full height (meant for
   tab strips); ours hold a title plus a note or buttons, so centre them. */
.card > .card-header { align-items: center; gap: .5rem; }

/* ---------- 3. Shared page helpers -------------------------------------- */
/* KPI tiles (homepage, /analytics, /energy) */
.ui-kpi .card { height: calc(100% - var(--bs-gutter-y, 1.25rem)); margin-bottom: 1.25rem; }
.ui-kpi .card-body { padding: 1rem 1.25rem; }
.ui-kpi p.mb-1 { color: var(--ui-muted); font-size: .95rem; font-weight: 500; line-height: 1.35; min-height: 2.7em; }
.ui-kpi h3 { font-size: 1.75rem; font-weight: 700; margin: 2px 0 8px; color: var(--ui-text) !important; }
.ui-kpi .progress { height: 4px; }
.ui-kpi .progress-bar.bg-light { background: var(--ui-border-strong) !important; }
.ui-kpi .card-body > div:last-child { font-size: .925rem; color: var(--ui-muted); }

/* Wide tables: ui-modern.js (v1, shared) wraps them in .ui-table-scroll */
.ui-table-scroll { display: block; width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; border-radius: var(--ui-radius); }
.ui-table-scroll.is-scrollable {
  background:
    linear-gradient(to right, var(--ui-surface) 30%, transparent) left center / 24px 100% no-repeat local,
    linear-gradient(to left, var(--ui-surface) 30%, transparent) right center / 24px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgba(0,0,0,.12), transparent) left center / 10px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.12), transparent) right center / 10px 100% no-repeat scroll;
}
[data-bs-theme=dark] .ui-table-scroll.is-scrollable { background: none; }

/* Legacy form tables (label | field rows on /dconfig, /otaconfig, /dgraphconf,
   user and org forms) carry .text-nowrap; one long label then pushes the
   table past its card at Metronic's larger type. Let label cells wrap in
   tables that contain form fields; data tables keep nowrap and scroll. */
table.text-nowrap:has(.form-control, .form-select, select) > tbody > tr > td { white-space: normal; }

/* Viewport-filling embeds (Documentation viewer); sized by ui-modern.js */
.card-body.ui-card-flush { padding: 0; }
.ui-fill-frame { display: block; width: 100%; border: 0; height: calc(100vh - 200px); min-height: 360px; background: var(--ui-surface); }

/* Signal reading: bars + label on one line */
.ui-signal { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.ui-signal .signal-bars { margin: 0; }

/* Empty / loading / error placeholder */
.ui-empty { padding: 2.5rem 1rem; text-align: center; color: var(--ui-muted); }
.ui-empty > i { font-size: 2rem; display: block; margin-bottom: .5rem; opacity: .6; }

/* Loading dots for values fetched after render (DeviceHtmlRenderer) */
.stat-loading-dots { display: inline-flex; align-items: center; gap: 3px; height: 1em; vertical-align: middle; }
.stat-loading-dots .dot { width: 4px; height: 4px; border-radius: 50%; background-color: currentColor; opacity: .3;
                          animation: uiDotPulse 1.2s ease-in-out infinite; }
.stat-loading-dots .dot:nth-child(2) { animation-delay: .2s; }
.stat-loading-dots .dot:nth-child(3) { animation-delay: .4s; }
@keyframes uiDotPulse { 0%, 80%, 100% { opacity: .3; transform: scale(.8); } 40% { opacity: 1; transform: scale(1.15); } }

/* ---------- 3b. Device list and detail card ----------------------------- */
/* back2front_if/DeviceHtmlRenderer.php: home page list/detail and the
   Device Activity overlay. */
.device-list-item { padding: .85rem 1rem; }
.device-list-item h5 { font-size: 1.05rem; font-weight: 600; margin-bottom: 2px !important; color: var(--ui-text); }
.device-list-item p { font-size: .925rem; color: var(--ui-muted); margin: 0 !important; }
.device-list-item.active h5, .device-list-item.active p { color: #fff; }
.device-list-item.active p { opacity: .85; }
.device-details-container { scroll-margin-top: calc(var(--bs-app-header-height, 70px) + 12px); }
.device-details-container > .card { margin-bottom: 0; }
.device-details-container .table td { padding: .55rem .5rem; vertical-align: middle; }
.device-details-container .table td:first-child { color: var(--ui-text-2); }
.device-details-container .table td.border-end { color: var(--ui-muted); width: 2.25rem; text-align: center; }
/* Actions: a wrapping grid instead of squeezed columns */
.device-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: .5rem; }
/* AI diagnostics card (renderAiAnalyticsCard); JS adds .glass-card when it shows */
#ai-analytics-card { margin-top: 1.25rem; }
#ai-analytics-card > .card-header { min-height: 0; padding: .85rem 1.25rem; gap: .5rem; flex-wrap: wrap; }
#ai-analytics-card .accordion .card-header { min-height: 0; padding: .25rem .5rem; }
.glass-insight { padding: .75rem 1rem; border-radius: var(--ui-radius-sm); background: var(--ui-surface-2);
                 border: 1px solid var(--ui-border); }
.glass-insight + .glass-insight { margin-top: .75rem; }

/* Legacy assets/css/sengine.css (quick search, signal bars) inside v2.
   Pages link sengine.css after this file, so these are prefixed with `body`
   to win on specificity rather than source order. */
body .input-group { min-width: 0; }  /* sengine.css forces 450px, wider than a phone */
/* ...and squeezes text inputs in any input group to 5px/10px padding */
body .input-group > input.form-control:not(.form-control-sm):not(.form-control-lg) { padding: .775rem 1rem; }
body .resultx { margin-top: .5rem; background: var(--bs-body-bg); border-radius: var(--ui-radius); box-shadow: var(--ui-shadow-lg); }
body .resultx:empty { display: none; }
body .resultx li { color: var(--ui-primary-text); font-family: inherit; }
body .resultx li:hover { background: var(--ui-surface-hover); }
body .search-word { color: var(--ui-primary-text); }
body .search-word-secondary { color: var(--ui-text); }
body .signal-bar:not(.filled) { background-color: var(--ui-border); border-color: var(--ui-border-strong); }

/* ---------- 3a. Data tables (assets/modern/v2/ui-table.js) -------------- */
/* One look for every list page: a card with a toolbar (search, filters,
   actions), quiet uppercase headers that sort on click, dashed row
   separators, and a footer with the count, rows-per-page and the pager.
   On phones the rows become cards ("LABEL  value" lines). Pattern page:
   web/14-enmon-man-users.php. */
.ui-table-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; }
.card-header > .ui-table-toolbar { flex: 1 1 auto; justify-content: flex-end; padding: .75rem 0; }
.ui-table-search { position: relative; flex: 0 1 280px; min-width: 200px; }
.ui-table-search > i { position: absolute; left: .9rem; top: 50%; transform: translateY(-50%); color: var(--ui-muted); pointer-events: none; }
.ui-table-search > input { padding-left: 2.5rem; }
.ui-table-toolbar .form-select { width: auto; min-width: 140px; }
.ui-table-count { color: var(--ui-muted); font-weight: 500; font-size: .95rem; margin-left: .5rem; }

.ui-table { width: 100%; margin-bottom: 0; border-collapse: separate; border-spacing: 0; }
.ui-table > thead > tr > th {
  color: var(--ui-muted); background: transparent;
  font-size: .925rem; font-weight: 600; text-transform: uppercase; letter-spacing: .03em;
  white-space: nowrap; padding: .75rem .75rem; vertical-align: bottom;
  border-bottom: 1px solid var(--ui-border);
}
.ui-table > tbody > tr > td {
  color: var(--ui-text-2); padding: .9rem .75rem; vertical-align: middle;
  border-bottom: 1px dashed var(--ui-border);
}
.ui-table > tbody > tr:last-child > td,
.ui-table > tbody > tr.ui-table-last > td { border-bottom: 0; }
.ui-table > tbody > tr:not(.ui-table-empty):hover > td { background: var(--ui-surface-hover); }
.ui-table > thead > tr > th:first-child, .ui-table > tbody > tr > td:first-child { padding-left: 0; }
.ui-table > thead > tr > th:last-child, .ui-table > tbody > tr > td:last-child { padding-right: 0; }
.ui-table .ui-actions { text-align: end; white-space: nowrap; }

/* Sort indicators */
.ui-table th.ui-sortable { cursor: pointer; user-select: none; position: relative; padding-right: 1.4rem; }
.ui-table > thead > tr > th.ui-sortable:last-child { padding-right: 1.4rem; } /* room for the arrows on a sortable last column */
.ui-table th.ui-sortable:hover { color: var(--ui-text); }
.ui-table th.ui-sortable::before,
.ui-table th.ui-sortable::after {
  content: ""; position: absolute; right: .35rem; width: 0; height: 0;
  border-left: 4px solid transparent; border-right: 4px solid transparent; opacity: .35;
}
.ui-table th.ui-sortable::before { bottom: calc(.75rem + 9px); border-bottom: 4px solid currentColor; }
.ui-table th.ui-sortable::after { bottom: calc(.75rem + 2px); border-top: 4px solid currentColor; }
.ui-table th.ui-sortable[aria-sort=ascending],
.ui-table th.ui-sortable[aria-sort=descending] { color: var(--ui-primary-text); }
.ui-table th.ui-sortable[aria-sort=ascending]::before,
.ui-table th.ui-sortable[aria-sort=descending]::after { opacity: 1; }
.ui-table th.ui-sortable[aria-sort=ascending]::after,
.ui-table th.ui-sortable[aria-sort=descending]::before { opacity: .15; }

.ui-table-empty > td { border-bottom: 0 !important; }
.ui-table-footer { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .75rem 1.5rem; padding-top: 1.1rem; }
.ui-table-info { color: var(--ui-muted); font-size: .95rem; }
.ui-table-nav { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem 1.25rem; }
.ui-table-size { display: flex; align-items: center; gap: .5rem; margin: 0; color: var(--ui-muted); font-size: .95rem; }
.ui-table-size .form-select { width: auto; }

/* Cell helpers: person (initials + name + email), muted second line */
.ui-cell-user { display: flex; align-items: center; gap: .85rem; min-width: 0; }
.ui-cell-user .symbol-label { font-weight: 700; font-size: .95rem; background: var(--ui-primary-soft); color: var(--ui-primary-text); }
.ui-cell-title { color: var(--ui-text); font-weight: 600; line-height: 1.35; overflow-wrap: anywhere; }
.ui-cell-sub { color: var(--ui-muted); font-size: .925rem; overflow-wrap: anywhere; }

/* Phones: rows become cards */
@media (max-width: 767.98px) {
  .ui-table-search { flex: 1 1 100%; }
  .ui-table.ui-table-cards > thead { display: none; }
  .ui-table.ui-table-cards,
  .ui-table.ui-table-cards > tbody,
  .ui-table.ui-table-cards > tbody > tr,
  .ui-table.ui-table-cards > tbody > tr > td { display: block; width: 100%; }
  .ui-table.ui-table-cards > tbody > tr:not(.ui-table-empty) {
    border: 1px solid var(--ui-border); border-radius: var(--ui-radius);
    padding: .5rem 1rem; margin-bottom: .75rem; background: var(--bs-body-bg);
  }
  .ui-table.ui-table-cards > tbody > tr > td {
    display: flex; justify-content: space-between; align-items: center; gap: 1rem;
    padding: .45rem 0 !important; border: 0; text-align: end;
  }
  .ui-table.ui-table-cards > tbody > tr > td + td { border-top: 1px dashed var(--ui-border); }
  .ui-table.ui-table-cards > tbody > tr > td::before {
    content: attr(data-label); flex-shrink: 0; text-align: start;
    color: var(--ui-muted); font-size: .925rem; font-weight: 600; text-transform: uppercase; letter-spacing: .03em;
  }
  .ui-table.ui-table-cards > tbody > tr > td[data-label=""]::before,
  .ui-table.ui-table-cards > tbody > tr > td.ui-cell-full::before { display: none; }
  .ui-table.ui-table-cards > tbody > tr > td.ui-cell-full { justify-content: flex-start; text-align: start; }
  /* actions: no label, buttons to the right */
  .ui-table.ui-table-cards > tbody > tr > td.ui-actions { justify-content: flex-end; }
  .ui-table.ui-table-cards > tbody > tr > td.ui-actions::before { display: none; }
  .ui-table.ui-table-cards > tbody > tr:hover > td { background: transparent; }
  .ui-table.ui-table-cards > tbody > tr.ui-table-empty > td { display: block; }
  .ui-table.ui-table-cards > tbody > tr.ui-table-empty > td::before { display: none; }
  /* loading / empty / error rows that page scripts draw as one colspan cell */
  .ui-table.ui-table-cards > tbody > tr > td[colspan] { display: block; text-align: center; }
  .ui-table.ui-table-cards > tbody > tr > td[colspan]::before { display: none; }
  .ui-table-footer { justify-content: center; }
}

/* Full-page "working…" overlays on the user/org/assignment pages were
   hard-coded white; follow the theme. */
#loadingOverlay { background-color: rgba(var(--bs-body-bg-rgb), .85) !important; color: var(--ui-text); }

/* Wide tables (many columns): keep cells on one line on desktop/tablet and
   let .table-responsive scroll sideways (phones get cards anyway). */
@media (min-width: 768px) {
  .ui-table.ui-table-wide > tbody > tr > td { white-space: nowrap; }
  .ui-table.ui-table-wide .ui-cell-user { min-width: 14rem; }
}
/* Cells that hold a form control (/orgs, /deviceassign): select or input with
   its button side by side; in phone cards the label goes above the control. */
.ui-table .ui-cell-control .form-control { min-width: 12rem; }
.ui-cell-control-group { display: flex; gap: .5rem; align-items: center; }
.ui-cell-control-group .form-select { min-width: 13.5rem; }
@media (max-width: 767.98px) {
  .ui-table.ui-table-cards > tbody > tr > td.ui-cell-control { flex-direction: column; align-items: stretch; gap: .35rem; text-align: start; }
  .ui-table .ui-cell-control .form-control { min-width: 0; }
  .ui-cell-control-group .form-select { min-width: 0; flex: 1 1 auto; }
}
/* Server-side search form inside a card-header toolbar (/deviceassign) */
.ui-server-search { display: flex; flex: 1 1 auto; flex-wrap: wrap; justify-content: flex-end; gap: .5rem; align-items: center; }
.ui-server-search .ui-table-search { flex: 0 1 300px; min-width: 16rem; }
@media (max-width: 767.98px) { .ui-server-search, .ui-server-search .ui-table-search { flex: 1 1 100%; min-width: 0; } }
/* Result line filled by JS after an action; takes no space while empty */
.ui-response:empty { display: none; }
/* Server-paged tables (/dconque, /data, /m2mdata): ui-table only searches and
   sorts the loaded page, so its own footer is hidden and the server pager
   below the table does the paging. Markup: <div class="table-responsive ui-server-paged"><table class="ui-table">
   ... then <div class="ui-server-pager">note + .pagination</div>. */
.ui-server-paged + .ui-table-footer { display: none; }
.ui-server-pager { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .75rem 1.5rem; padding-top: 1.1rem; color: var(--ui-muted); font-size: .95rem; }
.ui-server-pager .pagination { flex-wrap: wrap; margin: 0; padding: 0; gap: .25rem 0; }
/* Unit after a number in a cell: <span class="text-nowrap">12.5 <span class="ui-unit">V</span></span> */
.ui-unit { color: var(--ui-muted); font-size: .925em; }
/* Raw packet / hex / JSON shown in a dialog */
.ui-code-block { word-break: break-all; font-family: var(--ui-font-mono); background: var(--ui-surface-2); color: var(--ui-text); padding: .75rem 1rem; border-radius: var(--ui-radius-sm); }
/* Export row above a table: label, date range, Download (same shape as /perform) */
.ui-export-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem; padding: .75rem 0 1rem; border-bottom: 1px dashed var(--ui-border); margin-bottom: .5rem; }
.ui-export-bar label { margin: 0; color: var(--ui-muted); font-weight: 500; }
.ui-export-bar .form-control { width: 22rem; max-width: 100%; text-align: center; }
@media (max-width: 575.98px) {
  .ui-export-bar .form-control { width: 100%; }
  .ui-export-bar .btn { flex: 1 1 auto; }
}

/* ---------- 3a+. Forms ---------------------------------------------------- */
/* Labelled form sections instead of "Parameter | Value" tables
   (/dconfig, /otaconfig, /dedit, /addusers, /orgs ...). Markup:
     <form class="ui-form">
       <section class="ui-form-section">
         <div class="ui-form-section-head"><h4>Title</h4><p>What this group does</p></div>
         <div class="ui-form-row">
           <label class="ui-form-label" for="x">Label <span class="ui-req">*</span></label>
           <div class="ui-form-field"><input class="form-control" id="x">
             <div class="ui-form-help">Help text</div>
             <div class="invalid-feedback">Error</div></div>
         </div>
       </section>
       <div class="ui-form-actions"><button class="btn btn-primary">Save</button></div>
     </form>
   Label left / field right from 768px; stacked on phones. */
.ui-form { max-width: 920px; margin-inline: auto; }
.ui-form-section { padding: 1.25rem 0; border-top: 1px solid var(--ui-border); }
.ui-form-section:first-of-type { border-top: 0; padding-top: .25rem; }
.ui-form-section-head { margin-bottom: 1rem; }
.ui-form-section-head h4 { font-size: 1rem; font-weight: 600; color: var(--ui-text); margin: 0 0 .2rem; }
.ui-form-section-head p { color: var(--ui-muted); font-size: .925rem; margin: 0; }
.ui-form-row { display: grid; grid-template-columns: 1fr; gap: .35rem; margin-bottom: 1.1rem; }
.ui-form-row:last-child { margin-bottom: 0; }
.ui-form-label { font-weight: 600; color: var(--ui-text-2); font-size: .925rem; margin: 0; }
.ui-form-label .ui-req { color: var(--ui-danger); margin-inline-start: .15rem; }
.ui-form-field { min-width: 0; }
.ui-form-field > .form-control, .ui-form-field > .form-select { width: 100%; }
.ui-form-help { color: var(--ui-muted); font-size: .925rem; margin-top: .3rem; }
.ui-form-help code, .ui-form-section-head p code { font-size: inherit; padding: .05rem .3rem; }
.ui-form-inline { display: flex; flex-wrap: wrap; gap: .75rem; }
.ui-form-inline > * { flex: 1 1 200px; min-width: 0; }
/* a button at the end of an inline row (Create, Use my location ...) */
.ui-form-inline > .ui-form-inline-action { flex: 0 0 auto; align-self: flex-end; }
@media (max-width: 575.98px) { .ui-form-inline > .ui-form-inline-action, .ui-form-inline > .ui-form-inline-action .btn { flex: 1 1 100%; width: 100%; } }
.ui-form-actions {
  display: flex; flex-wrap: wrap; gap: .75rem; justify-content: flex-end; align-items: center;
  padding-top: 1.25rem; border-top: 1px solid var(--ui-border);
}
.ui-form-actions .ui-form-actions-note { margin-inline-end: auto; color: var(--ui-muted); font-size: .925rem; }
/* "This is a risky option" callout inside a section */
.ui-form-warn {
  border-radius: var(--ui-radius-sm); padding: .6rem .8rem; font-size: .925rem;
  background: var(--ui-warning-soft); color: var(--ui-text);
  border-inline-start: 3px solid var(--ui-warning);
}
/* Validation: Bootstrap's .is-invalid / .invalid-feedback, plus the legacy
   input_filter.js class so its rejections look the same. */
.ui-form .input-error { border-color: var(--ui-danger) !important; }
.ui-form .was-validated .form-control:invalid ~ .ui-form-help,
.ui-form .form-control.is-invalid ~ .ui-form-help,
.ui-form .form-select.is-invalid ~ .ui-form-help { display: none; }
@media (min-width: 768px) {
  .ui-form-row { grid-template-columns: minmax(180px, 32%) 1fr; column-gap: 1.5rem; align-items: start; }
  .ui-form-label { padding-top: calc(.775rem + 1px); }
  .ui-form-row.ui-form-row-check .ui-form-label { padding-top: 0; }
}
@media (max-width: 575.98px) {
  .ui-form-actions { justify-content: stretch; }
  .ui-form-actions .btn { flex: 1 1 auto; }
  .ui-form-actions .ui-form-actions-note { flex-basis: 100%; }
}

/* ---------- 3d. Device hub (layout/device_strip.php, every device page) ---- */
/* Device name, summary figures and a tab row (Summary / Live / Data / Graphs /
   Diagnostics / More) that switches views in the same browser tab. */
.device-hub .card-body { padding-bottom: 1.25rem; }
.device-hub-head { display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem; min-width: 0; }
.device-hub-icon {
  width: 48px; height: 48px; flex-shrink: 0; border-radius: var(--ui-radius);
  display: inline-flex; align-items: center; justify-content: center; font-size: 22px;
  background: var(--ui-primary-soft); color: var(--ui-primary-text);
}
.device-hub-name { margin: 0; font-size: 1.35rem; font-weight: 600; line-height: 1.25; color: var(--ui-text); overflow-wrap: anywhere; }
.device-hub-imei { color: var(--ui-muted); font-size: .925rem; }
.device-hub-imei .js-device-imei { font-family: var(--ui-font-mono); }
.device-hub-facts {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .75rem 1.5rem; margin: 0;
  padding: .85rem 1rem; background: var(--ui-surface-2); border-radius: var(--ui-radius-sm);
}
.device-hub-facts dt { margin: 0 0 .15rem; color: var(--ui-muted); font-size: .925rem; font-weight: 500; }
.device-hub-facts dd { margin: 0; color: var(--ui-text); font-size: 1rem; font-weight: 600; overflow-wrap: anywhere; }
.device-hub-facts dd small { display: block; color: var(--ui-muted); font-size: .925rem; font-weight: 400; }
.device-hub-nav { display: flex; align-items: stretch; border-top: 1px solid var(--ui-border); padding: 0 1rem; }
.device-hub-tabs { display: flex; flex: 1 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.device-hub-tabs::-webkit-scrollbar { display: none; }
.device-hub-tab {
  display: inline-flex; align-items: center; gap: .45rem; padding: .95rem .9rem; margin-bottom: -1px;
  background: none; border: 0; border-bottom: 2px solid transparent; border-radius: 0;
  color: var(--ui-text-2); font-weight: 600; white-space: nowrap; text-decoration: none; cursor: pointer;
}
.device-hub-tab i { font-size: 1rem; opacity: .7; }
.device-hub-tab:hover, .device-hub-tab:focus-visible { color: var(--ui-primary-text); }
.device-hub-tab.active { color: var(--ui-primary-text); border-bottom-color: var(--ui-primary); }
.device-hub-tab.active i { opacity: 1; }
.device-hub-tab:focus-visible { outline: 2px solid var(--ui-primary-ring); outline-offset: -2px; }
.device-hub-more { flex: 0 0 auto; border-left: 1px solid var(--ui-border); padding-left: .25rem; }
.device-hub-caret { font-size: .8rem !important; }
.device-hub-more .dropdown-menu { min-width: 250px; max-height: 75vh; overflow-y: auto; }
.device-hub-more .dropdown-item { display: flex; align-items: center; gap: 10px; }
.device-hub-more .dropdown-item i { width: 16px; text-align: center; opacity: .75; }
@media (max-width: 767.98px) {
  .device-hub-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .device-hub-name { font-size: 1.15rem; }
  .device-hub-nav { padding: 0 .25rem; }
  .device-hub-tab { padding: .85rem .65rem; }
  .device-hub-more .device-hub-tab span { display: none; } /* icon + caret only */
}

/* ---------- 3e. Device finder (top bar, assets/modern/v2/ui-finder.js) --- */
.ui-finder { position: relative; display: flex; align-items: center; }
.ui-finder-box { position: relative; display: none; align-items: center; width: 100%; }
.ui-finder-icon { position: absolute; left: .9rem; color: var(--ui-muted); pointer-events: none; z-index: 1; }
.ui-finder-box > .form-control { padding-left: 2.5rem; padding-right: 2.5rem; height: 40px; }
.ui-finder-box > .form-control::-webkit-search-cancel-button { display: none; }
.ui-finder-kbd {
  position: absolute; right: .6rem; padding: .05rem .4rem; border-radius: 4px; font-size: .75rem;
  border: 1px solid var(--ui-border); color: var(--ui-muted); background: var(--ui-surface); pointer-events: none;
}
.ui-finder-box > .form-control:focus ~ .ui-finder-kbd, .ui-finder-box > .form-control:not(:placeholder-shown) ~ .ui-finder-kbd { display: none; }
.ui-finder-close { display: none; position: absolute; right: .35rem; border: 0; background: none; color: var(--ui-muted); padding: .4rem; }
.ui-finder.is-loading .ui-finder-icon { animation: ui-finder-pulse 1s ease-in-out infinite; }
@keyframes ui-finder-pulse { 50% { opacity: .3; } }
.ui-finder-panel {
  position: absolute; top: calc(100% + 6px); left: 0; width: 100%; min-width: 460px; max-height: min(70vh, 520px); overflow-y: auto;
  background: var(--ui-surface); border: 1px solid var(--ui-border); border-radius: var(--ui-radius); box-shadow: var(--ui-shadow-lg);
  z-index: 1060; padding: .35rem 0;
}
.ui-finder-head { padding: .35rem 1rem .25rem; color: var(--ui-muted); font-size: .925rem; font-weight: 600; text-transform: uppercase; letter-spacing: .03em; }
.ui-finder-head:empty { display: none; }
.ui-finder-list { list-style: none; margin: 0; padding: 0; }
.ui-finder-item { display: flex; align-items: center; gap: .25rem; padding: 0 .5rem 0 0; }
.ui-finder-item[aria-selected=true] { background: var(--ui-primary-soft); }
.ui-finder-main { display: flex; align-items: center; gap: .75rem; flex: 1 1 auto; min-width: 0; padding: .55rem 1rem; color: var(--ui-text); text-decoration: none; }
.ui-finder-main:hover { color: var(--ui-text); }
.ui-finder-text { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.ui-finder-name { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-finder-meta { color: var(--ui-muted); font-size: .925rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-finder-text mark { padding: 0; background: rgba(var(--ui-primary-rgb), .18); color: inherit; border-radius: 2px; }
.ui-finder-seen { color: var(--ui-muted); font-size: .925rem; white-space: nowrap; }
.ui-finder-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; background: var(--ui-muted); }
/* Same tiers as the Device Activity board (DeviceFreshnessRenderer) */
.ui-finder-dot.is-fresh_15m { background: var(--ui-success); }
.ui-finder-dot.is-recent_1h { background: var(--ui-success); opacity: .6; }
.ui-finder-dot.is-recent_5h { background: var(--ui-warning); }
.ui-finder-dot.is-stale_24h { background: var(--ui-warning); opacity: .6; }
.ui-finder-dot.is-stale_7d { background: var(--ui-danger); }
.ui-finder-dot.is-dormant { background: var(--ui-muted); }
.ui-finder-dot.is-none { visibility: hidden; }
.ui-finder-acts { display: flex; gap: .15rem; flex-shrink: 0; }
.ui-finder-act {
  width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--ui-radius-sm); color: var(--ui-muted);
}
.ui-finder-act:hover, .ui-finder-act:focus-visible { color: var(--ui-primary-text); background: var(--ui-surface-2); }
.ui-finder-empty { padding: .9rem 1rem; color: var(--ui-muted); }
.ui-finder-empty.is-danger { color: var(--ui-danger); }
.ui-finder-foot:empty { display: none; }
.ui-finder-foot { border-top: 1px solid var(--ui-border); margin-top: .35rem; padding: .55rem 1rem .25rem; font-size: .925rem; }
@media (min-width: 992px) {
  .ui-finder { flex: 0 1 520px; margin-right: 1rem; }
  .ui-finder-box { display: flex; }
}
/* Phones and tablets: a search icon; tapping it lays the box over the top bar */
@media (max-width: 991.98px) {
  .ui-finder.is-open .ui-finder-box {
    display: flex; position: fixed; top: 0; left: 0; right: 0; height: var(--bs-app-header-height, 60px);
    padding: 0 .75rem; background: var(--bs-app-header-base-bg-color, var(--ui-surface)); z-index: 1061;
    box-shadow: var(--ui-shadow);
  }
  .ui-finder.is-open .ui-finder-icon { left: 1.65rem; }
  .ui-finder.is-open .ui-finder-kbd { display: none; }
  .ui-finder.is-open .ui-finder-close { display: inline-flex; right: 1.1rem; }
  .ui-finder-panel { position: fixed; top: var(--bs-app-header-height, 60px); left: .5rem; right: .5rem; width: auto; min-width: 0; max-height: calc(100vh - var(--bs-app-header-height, 60px) - 1rem); }
}

/* ---------- 3a++. Map popups (OpenLayers: /locfleet, /dloc) ------------- */
.ol-popup {
  position: absolute; bottom: 12px; left: -50px; min-width: 180px;
  padding: 10px 14px; font-size: .925rem;
  background: var(--ui-surface); color: var(--ui-text);
  border: 1px solid var(--ui-border); border-radius: var(--ui-radius-sm); box-shadow: var(--ui-shadow);
}
.ol-popup-closer { position: absolute; top: 2px; right: 8px; text-decoration: none; }
.ol-popup-closer::after { content: "\2715"; color: var(--ui-muted); }

/* ---------- 3b+. Date/time range picker ---------------------------------- */
/* assets/datepicker/vanilla-datetimerange-picker (/dgraph, /data, /m2mdata,
   /perform, /fwhistory, /diag/<imei>, /commlog, user and org pages) is
   hard-coded white with inherited text, so in dark mode its month view and
   range list were near-invisible. Drawn with the tokens in both themes; the
   selected range uses the skin colour. `body` prefix: pages link the picker
   CSS after this file. */
body .daterangepicker {
  background-color: var(--bs-body-bg); color: var(--ui-text);
  border-color: var(--ui-border); border-radius: var(--ui-radius);
  box-shadow: var(--ui-shadow-lg); font-family: inherit;
}
body .daterangepicker:before { border-bottom-color: var(--ui-border); }
body .daterangepicker:after { border-bottom-color: var(--bs-body-bg); }
body .daterangepicker.drop-up:before { border-top-color: var(--ui-border); }
body .daterangepicker.drop-up:after { border-top-color: var(--bs-body-bg); }
body .daterangepicker .calendar-table { background-color: var(--bs-body-bg); border-color: var(--bs-body-bg); }
body .daterangepicker .calendar-table th,
body .daterangepicker .calendar-table td { color: var(--ui-text); }
body .daterangepicker .calendar-table .next span,
body .daterangepicker .calendar-table .prev span { border-color: var(--ui-text-2); }
body .daterangepicker th.month { color: var(--ui-text); font-weight: 600; }
body .daterangepicker td.available:hover,
body .daterangepicker th.available:hover { background-color: var(--ui-surface-hover); color: var(--ui-text); }
body .daterangepicker td.week,
body .daterangepicker th.week { color: var(--ui-muted); }
body .daterangepicker td.off,
body .daterangepicker td.off.in-range,
body .daterangepicker td.off.start-date,
body .daterangepicker td.off.end-date { background-color: transparent; color: var(--ui-muted); opacity: .55; }
body .daterangepicker td.in-range { background-color: var(--ui-primary-soft); color: var(--ui-text); }
body .daterangepicker td.active,
body .daterangepicker td.active:hover { background-color: var(--ui-primary); color: #fff; opacity: 1; }
body .daterangepicker td.disabled,
body .daterangepicker option.disabled { color: var(--ui-muted); }
body .daterangepicker select.monthselect,
body .daterangepicker select.yearselect,
body .daterangepicker select.hourselect,
body .daterangepicker select.minuteselect,
body .daterangepicker select.secondselect,
body .daterangepicker select.ampmselect {
  background: var(--ui-surface-2); border: 1px solid var(--ui-border); color: var(--ui-text); border-radius: var(--ui-radius-sm);
}
body .daterangepicker .calendar-time select.disabled { color: var(--ui-muted); }
body .daterangepicker .drp-buttons { border-top-color: var(--ui-border); }
body .daterangepicker .drp-selected { color: var(--ui-muted); }
body .daterangepicker.show-ranges.ltr .drp-calendar.left,
body .daterangepicker.show-ranges.rtl .drp-calendar.right,
body .daterangepicker.show-ranges.single.ltr .drp-calendar.left,
body .daterangepicker.show-ranges.single.rtl .drp-calendar.left { border-color: var(--ui-border); }
body .daterangepicker .ranges li { color: var(--ui-text-2); border-radius: var(--ui-radius-sm); }
body .daterangepicker .ranges li:hover { background-color: var(--ui-surface-hover); color: var(--ui-text); }
body .daterangepicker .ranges li.active { background-color: var(--ui-primary); color: #fff; }

/* ---------- 3b++. Highcharts styled mode + Dashboards (/dgraph) --------- */
/* /dgraph uses Highcharts Dashboards in styled mode: colours come from the
   --highcharts-* variables in assets/highcharts/highcharts.css and
   dashboard/dashboards.css (light on :root, dark only via the OS setting).
   Point them at the shell tokens so the charts follow the page theme;
   :root[data-bs-theme] outranks those files' :root rules. Series colours
   (--highcharts-color-N) are left as Highcharts' palette. */
:root[data-bs-theme] {
  --highcharts-background-color: var(--bs-body-bg);
  --highcharts-neutral-color-100: var(--ui-text);
  --highcharts-neutral-color-80: var(--ui-text-2);
  --highcharts-neutral-color-60: var(--ui-muted);
  --highcharts-neutral-color-40: var(--ui-muted);
  --highcharts-neutral-color-20: var(--ui-border-strong);
  --highcharts-neutral-color-10: var(--ui-border);
  --highcharts-neutral-color-5: var(--ui-surface-2);
  --highcharts-neutral-color-3: var(--ui-surface-2);
  --highcharts-neutral-color-0: var(--bs-body-bg);
  --highcharts-highlight-color-100: var(--ui-primary);
  --highcharts-highlight-color-80: var(--ui-primary-hover);
  --highcharts-highlight-color-60: rgba(var(--ui-primary-rgb), .6);
  --highcharts-highlight-color-20: rgba(var(--ui-primary-rgb), .2);
  --highcharts-highlight-color-10: rgba(var(--ui-primary-rgb), .1);
  --highcharts-dashboards-content-background-color: var(--bs-body-bg);
  --highcharts-dashboards-content-hover-color: var(--ui-surface-hover);
}
/* Chart tiles look like the shell's cards */
/* Frame drawn with an outline, not a border: Dashboards sizes each chart
   from the tile's measured height, and a border/overflow there collapsed
   the charts to 2px. */
.highcharts-dashboards-cell > .highcharts-dashboards-component {
  margin: 6px;
  background-color: var(--bs-body-bg);
  border-radius: var(--ui-radius);
  outline: 1px solid var(--ui-border);
  outline-offset: -1px;
  box-shadow: var(--ui-shadow-sm);
}
.highcharts-title { font-weight: 600 !important; }
/* Export menu: a quiet icon instead of a filled box */
.highcharts-contextbutton .highcharts-button-box { fill: transparent !important; stroke: none !important; }
.highcharts-contextbutton .highcharts-button-symbol { stroke: var(--ui-muted) !important; }
.highcharts-contextbutton:hover .highcharts-button-box { fill: var(--ui-surface-hover) !important; }
.highcharts-contextbutton:hover .highcharts-button-symbol { stroke: var(--ui-text) !important; }
.highcharts-menu { background: var(--bs-body-bg) !important; border: 1px solid var(--ui-border) !important;
                   border-radius: var(--ui-radius-sm) !important; box-shadow: var(--ui-shadow-lg) !important; }
.highcharts-menu-item { color: var(--ui-text) !important; }
.highcharts-menu-item:hover { background: var(--ui-surface-hover) !important; }
/* Rows of three charts: two per row on tablets, one on phones (the
   library's own breakpoint uses an invalid value and never stacks) */
@media (max-width: 1199.98px) {
  .highcharts-dashboards-row { flex-wrap: wrap; }
  .highcharts-dashboards-row > .highcharts-dashboards-cell { flex: 1 1 calc(50% - 2px); min-width: 0; }
}
@media (max-width: 767.98px) {
  .highcharts-dashboards-row > .highcharts-dashboards-cell { flex-basis: 100%; }
}

/* ---------- 3c. Highcharts in dark mode --------------------------------- */
/* /rtmon, /dgraph, /diag/<imei>, /fwhistory draw Highcharts with its light
   defaults (and assets/highcharts/highcharts.css), which write colours into
   the SVG. Recolour the standard Highcharts parts here, once, instead of
   theming every chart script; series colours are left alone. !important is
   needed to beat Highcharts' inline style attributes. */
[data-bs-theme=dark] .highcharts-background { fill: transparent !important; }
[data-bs-theme=dark] .highcharts-title,
[data-bs-theme=dark] .highcharts-subtitle,
[data-bs-theme=dark] .highcharts-axis-title,
[data-bs-theme=dark] .highcharts-axis-labels text,
[data-bs-theme=dark] .highcharts-legend-item text,
[data-bs-theme=dark] .highcharts-data-label text,
[data-bs-theme=dark] .highcharts-credits { fill: var(--ui-text-2) !important; color: var(--ui-text-2) !important; }
[data-bs-theme=dark] .highcharts-data-label span,
[data-bs-theme=dark] .highcharts-axis-labels span { color: var(--ui-text) !important; }
[data-bs-theme=dark] .highcharts-legend-item:hover text { fill: var(--ui-text) !important; }
[data-bs-theme=dark] .highcharts-legend-item-hidden text { fill: var(--ui-muted) !important; }
[data-bs-theme=dark] .highcharts-grid-line,
[data-bs-theme=dark] .highcharts-minor-grid-line { stroke: var(--ui-border) !important; }
[data-bs-theme=dark] .highcharts-axis-line,
[data-bs-theme=dark] .highcharts-tick { stroke: var(--ui-border-strong) !important; }
[data-bs-theme=dark] .highcharts-pane { fill: var(--ui-surface-2) !important; stroke: var(--ui-border) !important; }
[data-bs-theme=dark] .highcharts-tooltip-box { fill: var(--bs-gray-100) !important; stroke: var(--ui-border-strong) !important; }
[data-bs-theme=dark] .highcharts-tooltip text,
[data-bs-theme=dark] .highcharts-tooltip span { fill: var(--ui-text) !important; color: var(--ui-text) !important; }
[data-bs-theme=dark] .highcharts-button-box { fill: var(--ui-surface-2) !important; }
[data-bs-theme=dark] .highcharts-button-symbol { stroke: var(--ui-text-2) !important; }
[data-bs-theme=dark] .highcharts-no-data text { fill: var(--ui-muted) !important; }

/* ---------- 4. Phone bottom bar ----------------------------------------- */
/* Below the Metronic drawer (z-index 110) so the open menu covers it */
.ui-bottom-nav { display: none; }
@media (max-width: 767.98px) {
  .ui-bottom-nav {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 105;
    display: flex;
    height: calc(var(--ui-bottom-nav-h) + env(safe-area-inset-bottom, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: var(--bs-body-bg);
    border-top: 1px solid var(--ui-border);
    box-shadow: 0 -4px 16px rgba(16, 24, 40, .06);
  }
  .ui-bottom-nav__item {
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    border: 0; background: none;
    color: var(--ui-muted) !important;
    font: 500 11px/1.1 var(--ui-font);
    text-decoration: none !important;
    -webkit-tap-highlight-color: transparent;
  }
  .ui-bottom-nav__item i { font-size: 19px; line-height: 1; }
  .ui-bottom-nav__item.is-active { color: var(--ui-primary-text) !important; }
  .ui-bottom-nav__item:focus { outline: none; }
  .ui-bottom-nav__item:focus-visible { outline: 2px solid var(--ui-primary); outline-offset: -4px; border-radius: 10px; }
  body { padding-bottom: calc(var(--ui-bottom-nav-h) + env(safe-area-inset-bottom, 0px)); }
  .scrolltop { bottom: calc(var(--ui-bottom-nav-h) + 16px + env(safe-area-inset-bottom, 0px)) !important; }
  .ui-fill-frame { height: calc(100vh - 200px - var(--ui-bottom-nav-h)); }
  .form-control, .form-select { font-size: 16px; }  /* stops iOS zoom-on-focus */
  /* Metronic's card padding (2rem 2.25rem) is sized for desktop */
  .card > .card-body { padding: 1.25rem 1rem; }
  .card > .card-header { padding: 0 1rem; min-height: 56px; }
  .card .card .card-body { padding: 1rem .85rem; }
  .app-container { padding-left: 12px !important; padding-right: 12px !important; }
  .ui-kpi h3 { font-size: 1.5rem; }
  .ui-kpi p.mb-1 { min-height: 0; }
}

/* ---------- 6. Sign-in pages (layout/v2/auth_top.php: /login, /setpassword) */
body.ui-auth { background: var(--ui-bg); min-height: 100vh; }
.ui-auth-wrap {
  min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1.5rem; padding: 2rem 1rem;
}
.ui-auth-brand { display: inline-flex; align-items: center; gap: .75rem; color: var(--ui-text); font-size: 1.5rem; font-weight: 700; text-decoration: none; }
.ui-auth-brand:hover { color: var(--ui-text); }
.ui-auth-brand img { height: 40px; border-radius: var(--ui-radius-sm); }
.ui-auth-card { width: 100%; max-width: 440px; box-shadow: var(--ui-shadow); }
.ui-auth-card .card-body { padding: 2.25rem 2rem; }
.ui-auth-title { font-size: 1.5rem; font-weight: 700; color: var(--ui-text); margin: 0 0 .35rem; text-align: center; }
.ui-auth-intro { color: var(--ui-muted); text-align: center; margin: 0 0 1.5rem; }
/* single-column form inside the card */
.ui-auth .ui-form { max-width: none; }
.ui-auth .ui-form-row { grid-template-columns: 1fr; }
.ui-auth .ui-form-label { padding-top: 0; }
.ui-auth .ui-form-actions { border-top: 0; padding-top: .5rem; }
.ui-auth .ui-form-actions .btn { flex: 1 1 100%; }
.ui-auth-foot { color: var(--ui-muted); font-size: .925rem; }
/* account type: two-option segmented choice */
.ui-segmented { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; }
.ui-segmented .btn { width: 100%; }
/* password field with a show/hide button */
.ui-password { position: relative; }
.ui-password .form-control { padding-right: 3rem; }
.ui-password-toggle {
  position: absolute; top: 0; right: 0; height: 100%; width: 3rem; border: 0; background: none;
  color: var(--ui-muted); display: inline-flex; align-items: center; justify-content: center;
}
.ui-password-toggle:hover, .ui-password-toggle:focus-visible { color: var(--ui-primary-text); }
.ui-password .form-control.is-invalid { background-position: right 3rem center; padding-right: 5rem; }
/* password rules checklist */
.ui-checklist { list-style: none; margin: .5rem 0 0; padding: 0; font-size: .925rem; color: var(--ui-muted); }
.ui-checklist li { display: flex; align-items: center; gap: .4rem; }
.ui-checklist li::before { content: "\25CB"; font-size: .7rem; }
.ui-checklist li.ok { color: var(--ui-success); }
.ui-checklist li.ok::before { content: "\2713"; font-size: .8rem; }

/* ---------- 5. Accessibility & print ------------------------------------ */
:focus-visible { outline: 2px solid var(--ui-primary); outline-offset: 2px; }
.btn:focus-visible, .form-control:focus-visible, .form-select:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ui-primary-ring); }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important;
                           transition-duration: .01ms !important; scroll-behavior: auto !important; }
}
@media print {
  .app-sidebar, .app-header, .app-footer, .ui-bottom-nav, .scrolltop, .ui-skip-link { display: none !important; }
  .app-wrapper, .app-main { margin: 0 !important; padding: 0 !important; }
}
