/* ══════════════════════════════════════════════════════════════════════
   FT Platform — Global Styles
   Supports: Light + Dark mode via MudBlazor ThemeProvider
   ══════════════════════════════════════════════════════════════════════ */

/* ── DevExpress popup z-index fix ─────────────────────────────────
   DevExpress popups (export dropdown, toolbar menus) render ở body level.
   MudDialog fullscreen z-index ~1300 → đè popup DevExpress.
   Fix: boost DevExpress popup lên trên MudDialog. */
.dxbl-popup-root {
    z-index: 1500 !important;
}

/* ── NotificationBell popover — constrain mobile width ─────────────
   MudPopover render ở body level (portal), scoped CSS không reach được.
   !important vì MudBlazor có thể set inline width override. */
.ft-notif-popover {
    max-width: min(360px, calc(100vw - 16px)) !important;
}

/* Mobile (< 600px): force full-width sheet, bỏ anchor theo bell —
   sheet cố định 8px lề 2 bên, đảm bảo KHÔNG bao giờ tràn viewport.
   Trade-off: popup KHÔNG dính bell trên mobile, nhưng pattern này là chuẩn
   trên Gmail/Slack/iOS — user mobile mong full-width sheet, không phải tooltip. */
@media (max-width: 600px) {
    .ft-notif-popover {
        left: 8px !important;
        right: 8px !important;
        max-width: calc(100vw - 16px) !important;
        width: calc(100vw - 16px) !important;
        transform: none !important;
    }
}


/* ── Guided walkthrough panel (TourPanel, Tier A) ──
   Docked bottom-right, survives navigation (rendered in MainLayout, outside @Body).
   Global (not scoped) because it styles a MudPaper — scoped CSS would not reach the
   Mud component root. z-index below Popover (1200) so dropdowns/menus/date-pickers stay
   on top, and below Dialog (1400) so dialogs cover it. */
.ft-tour-panel {
    position: fixed;
    right: 16px;
    bottom: 16px;
    z-index: 1150;
    width: min(360px, calc(100vw - 32px));
    max-height: min(70vh, 560px);
    overflow-y: auto;
    padding: 12px 14px;
    border-radius: 12px;
}

.ft-tour-fab {
    position: fixed;
    right: 16px;
    bottom: 16px;
    z-index: 1150;
}

.ft-tour-head {
    display: flex;
    align-items: center;
    gap: 2px;
}

.ft-tour-title {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ft-tour-steps {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.ft-tour-step {
    display: flex;
    align-items: center;
    padding: 4px 6px;
    border-radius: 6px;
    cursor: pointer;
}

.ft-tour-step:hover {
    background: var(--mud-palette-action-default-hover);
}

.ft-tour-step--active {
    background: var(--mud-palette-primary-hover);
}

.ft-tour-actions {
    display: flex;
    align-items: center;
    margin-top: 8px;
}

/* Mobile: sit above the bottom navigation bar (d-md-none, ~56px). */
@media (max-width: 959.98px) {
    .ft-tour-panel,
    .ft-tour-fab {
        bottom: 72px;
    }
}


/* ── Clickable / interactive cards (Task list, kanban, categories) ──
   Affordance "bấm được": nâng nhẹ + đổ bóng + viền primary khi hover.
   Dùng cho MudCard Outlined; inline border-left (category) vẫn giữ vì
   inline style thắng class. */
.ft-card-hover {
    transition: box-shadow .15s ease, transform .15s ease, border-color .15s ease;
}

.ft-card-hover:hover {
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
    transform: translateY(-2px);
    border-color: var(--mud-palette-primary);
}

/* ── Kanban board (Task) ──
   Trước: cột cố định min-width 260px + flex:0 0 auto → tràn ngang (thanh cuộn) + cột lùn theo
   nội dung → trống cả vùng dưới. Giờ: cột flex chia đều bề ngang (min 200px, chỉ cuộn ngang khi
   quá hẹp) + board cao bằng viewport còn lại → drop zone giãn đầy + cuộn NỘI BỘ từng cột. */
.kanban-board {
    height: calc(100vh - 240px);
    min-height: 320px;
    overflow-x: auto;
    overflow-y: hidden;
    align-items: stretch;
}
.kanban-col {
    flex: 1 1 0;
    min-width: 200px;
    display: flex;
    flex-direction: column;
    border-radius: 10px;
}
.kanban-col .kanban-zone {
    flex: 1 1 auto;
    overflow-y: auto;
    min-height: 80px;
}


/* ── Base ─────────────────────────────────────────────────────────── */

html, body {
    font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Smooth transition khi chuyển dark/light mode — chỉ active SAU khi theme đã apply xong.
   Class .ft-transitions-ready được thêm bởi MainLayout sau firstRender.
   Tránh FOUC: không transition trong lúc load ban đầu. */
html.ft-transitions-ready *, html.ft-transitions-ready *::before, html.ft-transitions-ready *::after {
    transition: background-color 0.2s ease, color 0.15s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

a, .btn-link {
    color: #1565C0;
}

.btn-primary {
    color: #fff;
    background-color: #1565C0;
    border-color: #0D47A1;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
    box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #42A5F5;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #2E7D32;
}

.invalid {
    outline: 1px solid #C62828;
}

.validation-message {
    color: #C62828;
}

/* ── Error Boundaries ────────────────────────────────────────────── */

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

#blazor-error-ui {
    background: #fff3cd;
    bottom: 0;
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.15);
    display: none;
    left: 0;
    padding: 0.8rem 1.5rem;
    position: fixed;
    width: 100%;
    z-index: 9999;
    color: #856404;
    font-size: 0.95rem;
}

#blazor-error-ui .reload {
    color: #1565C0;
    font-weight: 600;
    margin-left: 0.5rem;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 1rem;
    top: 0.6rem;
    color: #856404;
    text-decoration: none;
    font-size: 1.2rem;
}

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ── Drawer Enhancement ──────────────────────────────────────────── */

.ft-drawer {
    border-right: 1px solid var(--mud-palette-divider, #E0E0E0);
}

.ft-drawer .mud-drawer-header {
    min-height: 56px;
}

/* NavGroup icon muted — nổi bật hơn khi hover */
.mud-nav-group .mud-nav-link .mud-icon-root {
    opacity: 0.7;
    transition: opacity 0.15s ease;
}

.mud-nav-group .mud-nav-link:hover .mud-icon-root {
    opacity: 1;
}

/* Active nav item — accent bar bên trái */
.mud-navmenu .mud-nav-link.active {
    border-left: 3px solid var(--mud-palette-primary, #1565C0);
    background-color: var(--mud-palette-primary-hover, rgba(21,101,192,0.08));
    font-weight: 500;
}

/* Nav item hover — nhẹ nhàng */
.mud-navmenu .mud-nav-link:hover:not(.active) {
    background-color: var(--mud-palette-action-default-hover, rgba(0,0,0,0.04));
}

/* ── Kẻ ngăn cụm trong NavMenu (2026-07-28) ──────────────────────────
   Gom từ 23 inline style rải khắp NavMenu.razor về 2 class → đổi bề rộng
   hay khoảng cách thì sửa Ở ĐÂY, không phải đi lục 23 chỗ.
   KHÔNG dùng MudDivider: render không ổn định trong MudNavMenu, dễ bị scope
   CSS nuốt (lý do cũ, vẫn giữ nguyên).
   Trước: main 40% / sub 30% + thụt lề trái (16px / 32px) để phân cấp bằng
   BỀ RỘNG. User chốt kéo dài hết (2026-07-28) ⇒ cả hai 100%, phân cấp giờ
   chỉ còn ở KHOẢNG CÁCH DỌC: main thoáng hơn sub. */
.ft-nav-divider,
.ft-nav-divider-sub {
    height: 1px;
    width: 100%;
    background-color: var(--mud-palette-divider);
}
.ft-nav-divider     { margin: 12px 0; }   /* giữa các cụm chính */
.ft-nav-divider-sub { margin: 8px 0; }    /* trong 1 submenu */

/* Phiên bản bản cài — dòng cuối menu trái. Là NHÃN, không phải mục menu:
   không hover, không con trỏ tay, mờ để không tranh chỗ với các mục thật.
   Thụt lề 16px khớp mục menu để nó nằm thẳng hàng chứ không lệch ra rìa.
   padding-bottom rộng hơn: chừa chỗ cho thanh điều hướng dưới trên mobile
   (app.css đã có luật padding-bottom:64px cho .mud-main-content ở ≤959.98px). */
.ft-nav-version {
    /* margin-top:auto = ghim xuống ĐÁY thanh menu. Dùng được vì .mud-drawer vốn đã
       là flex column (tra MudBlazor.min.css 9.4.0, không phải đoán) — nav ngắn thì
       dòng này rơi xuống đáy, nav dài thì nó nằm cuối phần cuộn. KHÔNG dùng
       position:fixed/absolute: sẽ đè lên mục menu cuối khi danh sách dài. */
    margin-top: auto;
    /* Lề ngang PHẢI cân hai bên thì canh giữa mới đúng giữa — lệch một bên là
       chữ nhích theo mà nhìn tưởng lỗi canh.
       Đáy chỉ 6px: đây là nhãn phụ, ngồi sát mép dưới. Bản đầu để 20px cho
       "thoáng" nhưng nhìn ra một ô trống to giữa chữ và mép (user báo bằng ảnh
       2026-08-28) — khoảng trắng ở chân thanh menu KHÔNG đọc ra là thoáng, nó
       đọc ra là hụt. */
    padding: 5px 8px 6px;
    text-align: center;
    font-size: .68rem;
    line-height: 1.15;
    opacity: .45;
    user-select: text;          /* bôi đen copy được khi hỗ trợ khách qua điện thoại */
    border-top: 1px solid var(--mud-palette-divider);
}

/* ── Cards & Paper Enhancement ───────────────────────────────────── */

/* Card hover effect cho dashboard cards */
.mud-card {
    transition: transform 0.15s ease, box-shadow 0.2s ease;
}

.mud-card:hover {
    transform: translateY(-1px);
}

/* ── Button Enhancement ──────────────────────────────────────────── */

.mud-button-root {
    transition: all 0.15s ease;
}

/* ── Chip & Badge ────────────────────────────────────────────────── */

.mud-chip {
    font-weight: 500;
    letter-spacing: 0.01em;
}

/* ── DataGrid compact styling ────────────────────────────────────── */

.mud-table-toolbar {
    padding-top: 4px !important;
    padding-bottom: 4px !important;
    min-height: 40px !important;
}

.mud-table-head .mud-table-cell {
    padding-top: 6px !important;
    padding-bottom: 6px !important;
    font-weight: 600;
    text-transform: uppercase;
    font-size: 0.75rem;
    letter-spacing: 0.05em;
}

.mud-table-body .mud-table-cell {
    padding-top: 4px !important;
    padding-bottom: 4px !important;
}

.mud-table-pagination {
    padding-top: 2px !important;
    padding-bottom: 2px !important;
    min-height: 36px !important;
}

.mud-table-container {
    overflow-x: auto;
}

.mud-table-container > table {
    width: 100%;
}

/* Table row hover — smooth */
.mud-table-body .mud-table-row {
    transition: background-color 0.1s ease;
}

/* ── Dialog Enhancement ──────────────────────────────────────────── */

.mud-dialog {
    border-radius: 12px;
}

.mud-dialog .mud-dialog-title {
    font-weight: 600;
}

/* ── Snackbar Enhancement ────────────────────────────────────────── */

.mud-snackbar {
    border-radius: 8px;
}

/* ── Page Header Pattern ─────────────────────────────────────────── */

/* Consistent page header spacing */
.page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 16px;
}

/* ── Responsive styling ──────────────────────────────────────────── */

@media (max-width: 599.98px) {
    .mud-main-content .mud-container {
        padding-left: 8px !important;
        padding-right: 8px !important;
    }

    .mud-typography-h5 {
        font-size: 1.15rem !important;
    }

    .mud-paper {
        padding: 8px !important;
    }

    .mud-table-pagination .mud-typography {
        font-size: 0.75rem;
    }

    /* Hộp thoại KHÔNG được chạm mép màn. Bản cũ chừa 8px mỗi bên — trên điện thoại nhìn như
       tràn viền, và chữ dài (vd "Xác nhận gửi lệnh Custom: speed = 25 đến Inverter01?") bị ép
       sát cạnh. Nay chặn bằng CẢ trần cứng LẪN tỉ lệ: 444px là trần MaxWidth.ExtraSmall của
       MudBlazor (.mud-dialog-width-xs), 88vw bảo đảm luôn còn 6% mỗi bên ở mọi khổ máy.
       88vw ở đây gần 90vw của băng 600-960px ngay dưới ⇒ vượt mốc 600px không thấy nhảy bậc. */
    .mud-dialog {
        margin: 16px !important;
        max-width: min(444px, 88vw) !important;
    }

    /* Hide card hover effect on mobile (no hover) */
    .mud-card:hover {
        transform: none;
    }
}

@media (min-width: 600px) and (max-width: 959.98px) {
    .mud-dialog {
        max-width: min(600px, 90vw) !important;
    }
}

/* ── Scrollbar Styling ───────────────────────────────────────────── */

::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.2);
    border-radius: 3px;
}

::-webkit-scrollbar-thumb:hover {
    background: rgba(0, 0, 0, 0.35);
}

/* Dark mode scrollbar */
.mud-theme-dark ::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.15);
}

.mud-theme-dark ::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.25);
}

/* ── Selection Color ─────────────────────────────────────────────── */

::selection {
    background: rgba(21, 101, 192, 0.2);
    color: inherit;
}

/* ── Mobile Bottom Navigation ───────────────────────────────────── */

.ft-bottom-nav {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1300;
    background: var(--mud-palette-surface);
    border-top: 1px solid var(--mud-palette-lines-default);
    justify-content: space-around;
    align-items: center;
    padding: 4px 0;
    padding-bottom: env(safe-area-inset-bottom, 4px);
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
}

.ft-bottom-nav__item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    padding: 6px 12px;
    border-radius: 8px;
    cursor: pointer;
    color: var(--mud-palette-text-secondary);
    font-size: 0.65rem;
    font-weight: 500;
    min-width: 56px;
    -webkit-tap-highlight-color: transparent;
    transition: color 0.15s ease, background-color 0.15s ease;
}

.ft-bottom-nav__item:active {
    background-color: var(--mud-palette-action-default-hover);
}

.ft-bottom-nav__item.active {
    color: var(--mud-palette-primary);
}

.ft-bottom-nav__item.active .mud-icon-root {
    color: var(--mud-palette-primary);
}

/* Padding bottom for main content on mobile to avoid bottom nav overlap */
@media (max-width: 959.98px) {
    .mud-main-content {
        padding-bottom: 64px !important;
    }
}

/* ── Touch-Friendly Enhancements ───────────────────────────────── */

@media (max-width: 959.98px) {
    /* Larger touch targets for buttons */
    .mud-icon-button {
        min-width: 44px;
        min-height: 44px;
    }

    /* Larger touch targets for text buttons on mobile */
    .mud-button-root.mud-button {
        min-height: 44px;
    }

    /* Larger touch targets for nav items */
    .mud-nav-link {
        min-height: 44px;
    }

    /* More padding for form fields on touch */
    .mud-input-control .mud-input {
        min-height: 44px;
    }

    /* Allow filter bar buttons to wrap on narrow screens */
    .d-flex.gap-2 {
        flex-wrap: wrap;
    }
}

/* ── Nút trong cụm .d-flex.gap-2: không ngắt dòng giữa chữ ───────────────── */

/* LỊCH SỬ (gỡ 2026-07-17) — rule này từng là:
       .d-flex.gap-2 > .mud-button-root            { flex: 1 1 0; white-space: nowrap; }
       .d-flex.gap-2 > .mud-button-root:first-child:last-child { flex: 0 1 auto; }
   dưới tiêu đề "Filter Bar Button Equal Width".
   VÌ SAO GỠ PHẦN flex:
   1. Filter bar KHÔNG còn dùng nó. Bản viết lại §6 (2026-05-06) cho filter bar class riêng, và
      `.page-toolbar .tb-actions` (dưới file này) đã tự cấp `display:flex; gap:8px` + `min-width:90px`.
      Không file nào có class chứa cả `tb-actions` lẫn `gap-2` → rule là HOÁ THẠCH, phục vụ 0 thứ nó nói.
   2. Nhưng selector `.d-flex.gap-2` là GLOBAL: MudStack Row Spacing="2" render đúng class đó
      → rule kéo giãn nút ở **38 chỗ** (123 MudStack + 76 div literal, quét 2026-07-17). Rà cả 38: **0 chỗ
      muốn nút bằng nhau**; 6 chỗ có bằng chứng NGƯỢC — tác giả đặt `flex:1` cho phần tử khác
      (MaintenanceChecklistPanel:16 thanh tiến độ bị bóp còn 1/3; KnowledgeBasePage:200 /
      ScadaImagePickerDialog:16 / ScadaFaceplateParamsDialog:21 ô nhập; RevealSecretDialog:46) hoặc đặt
      <MudSpacer/> sau nút (EnergyBudgetBatchDialog:46, WaterBudgetBatchDialog:50) — MudSpacer chỉ có
      nghĩa khi nút giữ bề rộng tự nhiên.
   3. ⚠️ `:first-child:last-child` KHÔNG cứu được "nút đơn độc" như tưởng — nó nghĩa là CON DUY NHẤT của
      container. Nút đứng cạnh MudText/MudSelect/MudFileUpload vẫn dính. Đó là lý do bề mặt là 38 chứ không phải 10.
   GIỮ LẠI `white-space: nowrap`: nó nằm trong chính rule cũ và MudBlazor KHÔNG cấp lại từ đâu
   (đã verify MudBlazor.min.css 9.4.0) → gỡ trọn thì nhãn nhiều từ xuống 2 dòng.
   ⇒ MUỐN nút bằng nhau ở 1 chỗ cụ thể: đặt `style="flex:1"` TẠI CHỖ ĐÓ. ĐỪNG dựng lại rule global. */
.d-flex.gap-2 > .mud-button-root {
    white-space: nowrap;
}

/* ── Utility Classes ─────────────────────────────────────────────── */

.text-gradient-primary {
    background: linear-gradient(135deg, #1565C0, #42A5F5);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

/* ── Attendance Calendar ────────────────────────────────────────── */

.att-calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

.att-calendar-grid:last-of-type {
    border-bottom: none;
}

.att-calendar-header {
    background-color: var(--mud-palette-surface);
}

.att-calendar-header-cell {
    padding: 8px 4px;
    text-align: center;
    border-right: 1px solid var(--mud-palette-lines-default);
}

.att-calendar-header-cell:last-child {
    border-right: none;
}

.att-calendar-cell {
    min-height: 72px;
    border-right: 1px solid var(--mud-palette-lines-default);
    position: relative;
}

.att-calendar-cell:last-child {
    border-right: none;
}

.att-calendar-empty {
    background-color: var(--mud-palette-background);
    opacity: 0.5;
}

.att-calendar-day {
    padding: 4px 6px;
    cursor: default;
    display: flex;
    flex-direction: column;
    gap: 2px;
    transition: transform 0.1s ease, box-shadow 0.15s ease;
}

.att-calendar-day:hover {
    transform: scale(1.02);
    box-shadow: 0 2px 8px rgba(0,0,0,0.15);
    z-index: 1;
}

.att-weekend {
    background-color: var(--mud-palette-background);
    opacity: 0.85;
}

.att-today {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
    z-index: 1;
}

.att-day-number {
    font-weight: 600;
    font-size: 0.85rem;
    color: var(--mud-palette-text-primary);
}

.att-today-number {
    background: var(--mud-palette-primary);
    color: white;
    border-radius: 50%;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.75rem;
}

.att-day-times {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.att-time-in,
.att-time-out {
    font-size: 0.65rem;
    color: var(--mud-palette-text-secondary);
    line-height: 1.2;
}

.att-time-in::before {
    content: "↓ ";
    color: var(--mud-palette-success);
}

.att-time-out::before {
    content: "↑ ";
    color: var(--mud-palette-error);
}

.att-day-badge {
    position: absolute;
    top: 3px;
    right: 4px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.7rem;
    font-weight: 700;
    color: #fff;
    box-shadow: 0 1px 3px rgba(0,0,0,0.3);
    line-height: 1;
}

.att-calendar-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    padding: 10px 16px;
    background: var(--mud-palette-surface);
    border-top: 1px solid var(--mud-palette-lines-default);
}

.att-legend-item {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 0.75rem;
    color: var(--mud-palette-text-secondary);
}

.att-legend-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    display: inline-block;
}

/* Responsive: mobile hiển thị nhỏ gọn hơn */
@media (max-width: 600px) {
    .att-calendar-cell {
        min-height: 52px;
    }
    .att-day-times {
        display: none;
    }
    .att-day-number {
        font-size: 0.75rem;
    }
    .att-calendar-legend {
        gap: 8px;
    }
}

/* AI Diagnosis Response */
.ai-response-content {
    color: var(--mud-palette-text-primary);
    font-size: 0.875rem;
    line-height: 1.6;
}
.ai-response-content h4, .ai-response-content h5, .ai-response-content h6 {
    color: var(--mud-palette-primary);
}
.ai-response-content ul, .ai-response-content ol {
    color: var(--mud-palette-text-primary);
}
.ai-response-content strong {
    color: var(--mud-palette-text-primary);
    font-weight: 600;
}
.ai-response-content code {
    background: var(--mud-palette-background-gray);
    padding: 1px 4px;
    border-radius: 3px;
    font-size: 0.85em;
}

/* ── ApexCharts Dark Mode Tooltip Fix ──────────────────────────── */
/* ApexCharts tooltip render ở body level (portal), KHÔNG nằm trong .mud-theme-dark.
   Dùng html[data-ft-dark] (set bởi App.razor) để target chính xác. */

html[data-ft-dark="true"] .apexcharts-tooltip {
    background: #1E1E2D !important;
    color: #E0E0E0 !important;
    border: 1px solid #3a3a4a !important;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5) !important;
}

html[data-ft-dark="true"] .apexcharts-tooltip-title {
    background: #27293d !important;
    color: #E0E0E0 !important;
    border-bottom: 1px solid #3a3a4a !important;
}

html[data-ft-dark="true"] .apexcharts-tooltip-text,
html[data-ft-dark="true"] .apexcharts-tooltip-text-y-label,
html[data-ft-dark="true"] .apexcharts-tooltip-text-y-value,
html[data-ft-dark="true"] .apexcharts-tooltip-text-goals-label,
html[data-ft-dark="true"] .apexcharts-tooltip-text-goals-value,
html[data-ft-dark="true"] .apexcharts-tooltip-text-z-label,
html[data-ft-dark="true"] .apexcharts-tooltip-text-z-value {
    color: #E0E0E0 !important;
}

html[data-ft-dark="true"] .apexcharts-tooltip-series-group {
    background: transparent !important;
}

/* ApexCharts xAxisTooltip */
html[data-ft-dark="true"] .apexcharts-xaxistooltip {
    background: #1E1E2D !important;
    color: #E0E0E0 !important;
    border: 1px solid #3a3a4a !important;
}

html[data-ft-dark="true"] .apexcharts-xaxistooltip-text {
    color: #E0E0E0 !important;
}

/* ApexCharts legend text */
html[data-ft-dark="true"] .apexcharts-legend-text {
    color: #E0E0E0 !important;
}

/* Giữ lại fallback cho trường hợp tooltip nằm trong MudBlazor scope */
.mud-theme-dark .apexcharts-tooltip {
    background: #1E1E2D !important;
    color: #E0E0E0 !important;
    border: 1px solid #3a3a4a !important;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5) !important;
}

.mud-theme-dark .apexcharts-tooltip-title {
    background: #27293d !important;
    color: #E0E0E0 !important;
    border-bottom: 1px solid #3a3a4a !important;
}

.mud-theme-dark .apexcharts-tooltip-text,
.mud-theme-dark .apexcharts-tooltip-text-y-label,
.mud-theme-dark .apexcharts-tooltip-text-y-value {
    color: #E0E0E0 !important;
}

.mud-theme-dark .apexcharts-tooltip-series-group {
    background: transparent !important;
}

.mud-theme-dark .apexcharts-xaxistooltip {
    background: #1E1E2D !important;
    color: #E0E0E0 !important;
    border: 1px solid #3a3a4a !important;
}

.mud-theme-dark .apexcharts-legend-text {
    color: #E0E0E0 !important;
}

/* ── Fullwidth dashboard override ──
   MainLayout wraps every page in <MudContainer MaxWidth.ExtraLarge pa-2 pa-sm-4>
   which caps content at 1920px — leaving ugly empty space on 2K/4K monitors for
   dashboard pages. When a page uses .dashboard-root, neutralize the container cap
   and padding so dashboard-root can fill the full main-content area (respecting
   drawer when open). Applied globally because scoped CSS cannot reach parent. */
body:has(.dashboard-root) .mud-main-content > .mud-container {
    max-width: none !important;
    padding: 0 !important;
}

/* SCADA page — full viewport (fit-to-contain canvas) */
body:has(.scada-page) .mud-main-content > .mud-container {
    max-width: none !important;
    padding: 0 !important;
    /* ⚠️ MẮT XÍCH BẮT BUỘC của chuỗi chiều cao 100vh → 100% → 100% bên dưới: .scada-page nằm
       TRONG .mud-container, mà height:% chỉ tính được khi CHA có chiều cao xác định — thiếu dòng
       này là %% sụp về auto ⇒ canvas co còn một mẩu, dưới trắng toác (đã dính 2026-08-17). */
    height: 100%;
}

/* SCADA = màn HMI: PHẢI khít màn hình, KHÔNG BAO GIỜ cuộn.
   Trước 2026-08-17 chiều cao ghi cứng inline trong ScadaDesigner.razor là `calc(100vh - 64px)` —
   chỉ trừ AppBar TRÊN. Nhưng .mud-main-content còn có padding-top do MudBlazor cấp (BA giá trị:
   56px màn dọc <600px · 48px màn NGANG · 64px từ 600px) và, ở <=959.98px, thêm
   `padding-bottom:64px` (chừa chỗ cho .ft-bottom-nav). Cộng lại vượt 100vh từ 48 đến 64px
   ⇒ THANH CUỘN DỌC, chỉ lộ khi thu nhỏ cửa sổ.

   Cách chữa dưới đây độc lập breakpoint, không cần biết mấy con số kia: MudBlazor reset
   `box-sizing: border-box` toàn cục, nên `height:100vh` của cha ĐÃ BAO GỒM padding trên+dưới;
   `height:100%` của con vì thế giải ra đúng phần content-box còn lại ở MỌI khổ màn.

   ⛔ ĐỪNG "chữa gọn" bằng cách cho padding-bottom:0 ở mobile — nó cắt được thanh cuộn nhưng
   trả lại đúng lỗi đang chữa: .ft-bottom-nav (fixed, z-index 1300) sẽ đè lên 64px đáy canvas.
   ⚠️ Chiều cao sống Ở ĐÂY, không phải inline style — thêm lại `height:` vào thẻ .scada-page là
   inline thắng stylesheet, bản vá này thành vô hiệu mà không báo gì. */
body:has(.scada-page) .mud-main-content {
    height: 100vh;
}

body:has(.scada-page) .scada-page {
    height: 100%;
}

/* Chart history pages — full width, charts can span to 2K/4K without empty gutters.
   Keeps existing px-4 padding from the wrapper div. */
body:has(.chart-history-page) .mud-main-content > .mud-container {
    max-width: none !important;
    padding: 0 !important;
}

/* Overview Dashboard (/dashboard) — full width with dynamic padding-inline.
   Uses .dashboard-page wrapper which manages its own breakpoint padding.
   Different from .dashboard-root (Machine/Energy fixed-viewport, no-scroll). */
body:has(.dashboard-page) .mud-main-content > .mud-container {
    max-width: none !important;
    padding: 0 !important;
}

/* Energy Cost Dashboard (/energy/cost-dashboard) — same overview pattern (§15). */
body:has(.cost-dashboard-page) .mud-main-content > .mud-container {
    max-width: none !important;
    padding: 0 !important;
}

/* Warehouse Dashboard (/warehouse/dashboard) — same overview pattern (§15). */
body:has(.warehouse-dashboard-page) .mud-main-content > .mud-container {
    max-width: none !important;
    padding: 0 !important;
}

/* Giám sát theo cụm (/iot/nodes) — §15 Overview Dashboard Pattern: nhiều khối, có cuộn, full-width.
   Không có override này thì trang kẹt ở cap 1920px của MainLayout ⇒ 2K/4K trống hai bên
   (đúng dòng "Trang vẫn cap 1920px" trong bảng Troubleshooting §15.9). */
body:has(.node-monitor-page) .mud-main-content > .mud-container {
    max-width: none !important;
    padding: 0 !important;
}
.node-monitor-page {
    width: 100%;
    padding-inline: 16px;
    padding-block: 12px;
}
@media (min-width: 1280px) { .node-monitor-page { padding-inline: 24px; } }
@media (min-width: 1920px) { .node-monitor-page { padding-inline: 32px; } }
@media (min-width: 2560px) { .node-monitor-page { padding-inline: 48px; } }
@media (min-width: 3200px) { .node-monitor-page { padding-inline: 72px; } }

/* System Management pages — full width with dynamic padding-inline.
   Applied to list/table/dashboard/form pages. Form sections inside should be wrapped
   in .sysmgmt-form to cap field width and prevent inputs from stretching too wide. */
body:has(.sysmgmt-page) .mud-main-content > .mud-container {
    max-width: none !important;
    padding: 0 !important;
}
.sysmgmt-page {
    padding-inline: 16px;
    padding-top: 16px;
    padding-bottom: 16px;
}
@media (min-width: 1280px) { .sysmgmt-page { padding-inline: 24px; } }
@media (min-width: 1920px) { .sysmgmt-page { padding-inline: 32px; } }
@media (min-width: 2560px) { .sysmgmt-page { padding-inline: 48px; } }

/* Form sections inside sysmgmt-page — cap width so inputs don't stretch ugly on FHD/2K/4K.
   Use this around groups of MudCard form fields. Data grids/tables stay outside this wrapper
   to keep using the full container width.
   margin-inline:auto centers the form horizontally on wide viewports (2K/4K). */
.sysmgmt-form {
    max-width: 1200px;
    margin-inline: auto;
}

/* Standard MudDataGrid action column — prevent wrap, fix minimum width.
   Trick: width:1% + white-space:nowrap → column shrinks to content width,
   other columns fill remainder. Apply CellClass + HeaderClass = "actions-cell". */
.mud-table-cell.actions-cell,
th.actions-cell {
    width: 1%;
    white-space: nowrap;
    text-align: right;
    padding-inline: 8px !important;
}
.actions-cell-inner {
    display: inline-flex;
    gap: 4px;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: nowrap;
}
/* Touch target ≥ 36px on HMI 7" (1024×600) and tablets — auto bump from Size.Small (32px).
   Desktop (>1280px) keeps Size.Small native size. */
@media (max-width: 1280px) {
    .actions-cell-inner .mud-icon-button,
    .actions-cell-inner .mud-button-root {
        min-width: 36px;
        min-height: 36px;
    }
}

/* Standard list-page toolbar (search + filters + actions).
   Replaces the old MudGrid xs=12 sm=4 pattern that stretched inputs to ~620px on FHD.
   Layout: flex row with fixed max-widths per slot; actions push right via margin-left:auto.
   On mobile (<600px), every slot stacks full-width. */

/* When a list page contains .page-toolbar, override MainLayout's MudContainer cap (ExtraLarge=1920px)
   so the page extends full-width on 2K/4K monitors. Inner content (PageHeader, page-toolbar, DataGrid)
   gets natural padding from the elements themselves; container padding is reset to 0.

   .page-wide = the SAME override declared STATICALLY by the page itself, no toolbar required.
   Needed by tabbed pages where only some tabs own a .page-toolbar: the :has() rule would then
   flip on/off as the user switches tabs and the whole page would visibly resize sideways.
   Such a page declares .page-wide on its root <div> so the width never depends on which tab is
   open. Purely a stability marker — same values, no new layout behaviour. */
body:has(.page-toolbar) .mud-main-content > .mud-container,
body:has(.page-wide) .mud-main-content > .mud-container {
    max-width: none !important;
    padding-inline: 16px !important;
}
@media (min-width: 1280px) { body:has(.page-toolbar) .mud-main-content > .mud-container, body:has(.page-wide) .mud-main-content > .mud-container { padding-inline: 24px !important; } }
@media (min-width: 1920px) { body:has(.page-toolbar) .mud-main-content > .mud-container, body:has(.page-wide) .mud-main-content > .mud-container { padding-inline: 32px !important; } }
@media (min-width: 2560px) { body:has(.page-toolbar) .mud-main-content > .mud-container, body:has(.page-wide) .mud-main-content > .mud-container { padding-inline: 48px !important; } }

.page-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: flex-end;
    padding: 12px;
}
.page-toolbar .tb-search    { flex: 1 1 240px; max-width: 320px; }
.page-toolbar .tb-filter    { flex: 0 1 200px; min-width: 160px; max-width: 200px; }
.page-toolbar .tb-range     { flex: 0 1 240px; max-width: 240px; }                  /* single date picker */
.page-toolbar .tb-daterange { flex: 0 1 320px; min-width: 280px; max-width: 320px; } /* date range (dd/MM/yyyy → dd/MM/yyyy) — cần rộng hơn để không cắt năm */
.page-toolbar .tb-actions   { display: flex; gap: 8px; margin-left: auto; align-items: stretch; }
.page-toolbar .tb-actions:has(.task-assignee-toggle) { flex-wrap: wrap; }     /* mobile: toggle + nút xuống dòng thay vì tràn */
.page-toolbar .tb-actions .task-assignee-toggle { align-self: center; }       /* canh giữa toggle Của tôi/Tất cả trong cụm action bên phải */
.page-toolbar .tb-actions .mud-button-root { align-self: stretch; min-width: 90px; }

/* PageHeader Actions slot — ensure all buttons (New / Export / Import / etc.) share equal height
   regardless of variant. MudButton Filled and Outlined render slightly different padding/border,
   so align-items:stretch + align-self:stretch on the button forces visual height parity.
   Also handles MudFileUpload-wrapped buttons (Import) so they line up with raw MudButtons. */
.page-header-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: stretch;
}
.page-header-actions > .mud-button-root,
.page-header-actions > .mud-file-upload,
.page-header-actions > .mud-menu {
    align-self: stretch;
}
.page-header-actions > .mud-button-root,
.page-header-actions .mud-file-upload .mud-button-root,
.page-header-actions .mud-menu .mud-button-root {
    min-width: 100px;
}
.page-header-actions .mud-file-upload,
.page-header-actions .mud-menu {
    display: flex;
}
.page-header-actions .mud-file-upload .mud-button-root,
.page-header-actions .mud-menu .mud-button-root {
    height: 100%;
}

@media (max-width: 599px) {
    .page-toolbar .tb-search,
    .page-toolbar .tb-filter,
    .page-toolbar .tb-range,
    .page-toolbar .tb-daterange,
    .page-toolbar .tb-actions {
        flex: 1 1 100%;
        max-width: none;
        min-width: 0;
        margin-left: 0;
    }
}

/* ============================================================
   DevExpress report viewer — cách ly light mode + loading
   Thay cho CSS chết trong StockImportReportDialog.razor.css
   (::deep compile ra selector cần ancestor có scope attribute —
   không bao giờ match trong MudDialog portal; class .dxrv-*
   không tồn tại trong DevExpress 25.2).
   Scope: .dx-isolated-wrapper (5 report dialog trong MudDialog)
   + .report-layout (trang @layout ReportLayout: Designer).
   ============================================================ */

/* Bước 2 chuẩn hóa report (2026-07-12): theme BootstrapExternal theo
   hợp đồng cần app chủ cấp biến --bs-* (app KHÔNG load Bootstrap để
   tránh xung đột MudBlazor). Danh sách 37 biến dưới = TOÀN BỘ biến
   theme thực sự tiêu thụ (trích từ bootstrap-external.bs5.min.css,
   18 biến không có fallback). Giá trị light chuẩn Bootstrap 5.3;
   primary = xanh FT. PHẢI đặt ở :root vì theme định nghĩa chuỗi
   trung gian --dxbl- và --dxbrv- tại :root (custom property tính tại
   nơi ĐỊNH NGHĨA rồi mới kế thừa — scope vào wrapper là vô tác dụng).
   CẢNH BÁO viết comment CSS: KHÔNG viết chuỗi "sao + gạch chéo" trong
   comment (như pattern glob của tên biến) — nó ĐÓNG comment sớm, phần
   còn lại thành rác nuốt luôn rule kế tiếp (đã dính chính tại đây).
   Trơ với MudBlazor: chỉ là biến, Mud không đọc --bs-*, không có
   class/reboot Bootstrap nào được load. Viewer luôn light theo
   quyết định dự án — không cấp bộ giá trị dark. */
:root {
    --bs-white: #fff;
    --bs-black: #000;
    --bs-primary: #2E7D32;
    --bs-primary-rgb: 46, 125, 50;
    --bs-secondary: #6c757d;
    --bs-secondary-rgb: 108, 117, 125;
    --bs-success: #198754;
    --bs-success-rgb: 25, 135, 84;
    --bs-danger: #dc3545;
    --bs-danger-rgb: 220, 53, 69;
    --bs-warning: #ffc107;
    --bs-warning-rgb: 255, 193, 7;
    --bs-info: #0dcaf0;
    --bs-info-rgb: 13, 202, 240;
    --bs-light: #f8f9fa;
    --bs-light-rgb: 248, 249, 250;
    --bs-dark: #212529;
    --bs-dark-rgb: 33, 37, 41;
    --bs-body-bg: #fff;
    --bs-body-color: #212529;
    --bs-body-color-rgb: 33, 37, 41;
    --bs-border-color: #dee2e6;
    --bs-secondary-bg: #e9ecef;
    --bs-tertiary-bg: #f8f9fa;
    --bs-secondary-color-rgb: 33, 37, 41;
    --bs-light-border-subtle: #e9ecef;
    --bs-gray: #6c757d;
    --bs-gray-100: #f8f9fa;
    --bs-gray-200: #e9ecef;
    --bs-gray-300: #dee2e6;
    --bs-gray-400: #ced4da;
    --bs-gray-500: #adb5bd;
    --bs-gray-600: #6c757d;
    --bs-gray-700: #495057;
    --bs-gray-800: #343a40;
    --bs-gray-900: #212529;
}
.dx-isolated-wrapper {
    color-scheme: light;
    background: #fff;
    color: #333;
    width: 100%;
}

/* Chấm spinner của viewer: theme BootstrapExternal trỏ
   var(--bs-primary) không được app cấp → guaranteed-invalid →
   background trong suốt, spinner vô hình. Override SCOPED
   (không đụng :root — Bước 2 mới cấp bộ --bs-* đầy đủ). */
.dx-isolated-wrapper,
.report-layout {
    --dxbrv-primary-color: #2E7D32;
}

/* Overlay loading mặc định của DevExpress chỉ đục 40%
   (rgba 255,255,255,.4) → các text stub "Loading..." bên dưới
   xuyên qua và chồng chữ trên nền tối. Tăng độ đục để che stub
   và hiện spinner trên nền trắng rõ ràng. */
.dx-isolated-wrapper .dxv-blazor-loading-screen,
.report-layout .dxv-blazor-loading-screen {
    background: rgba(255, 255, 255, 0.95);
}

/* Theme DevExpress fix cứng .dxbrv-wrapper { height: 50rem } (800px)
   → viewer KHÔNG co theo khung dialog, "Whole Page" fit theo 800px
   → trang giấy tràn ra ngoài. Host (.dx-isolated-wrapper / .report-body)
   đã cấp height nên cho wrapper ăn theo 100%. */
.dx-isolated-wrapper .dxbrv-wrapper,
.report-layout .dxbrv-wrapper {
    height: 100%;
}

/* Nền vùng quanh trang giấy: theme trỏ var(--dxbrv-secondary-color)
   → invalid → trong suốt, lộ nền tối của MudDialog dark mode.
   Sơn xám nhạt cố định kiểu PDF viewer, không phụ thuộc biến theme. */
.dx-isolated-wrapper .dxbrv-surface-wrapper,
.report-layout .dxbrv-surface-wrapper {
    background: #ececec;
}

/* Chữ "Loading..." chồng nhau khi tạo document — DOM verify qua Chrome
   2026-07-12: chữ nằm ở span.dxbl-loading-panel-message, trong vùng
   .dxbl-loading-panel-indicator-area.dxbl-loading-panel-hidden
   (thuộc dxbl-loading-panel.dxbrv-page-loading-panel của page stub),
   chồng lên chữ Loading của .dxbrv-document-placeholder cùng tọa độ.
   Class "-hidden" là DevExpress CHỦ Ý ẩn, nhưng theme BootstrapExternal
   chỉ set màu cho nó, KHÔNG có rule ẩn → chữ lộ ra + đè nhau.
   PHẢI !important: theme có ".dxbl-loading-panel .dxbl-loading-panel-container
   .dxbl-loading-panel-indicator-area { display:flex }" specificity (0,3,0)
   thắng selector 2 class ở đây — không !important là rule nạp mà không ăn
   (đã dính, verify runtime 2026-07-12). State-respecting: DevExpress gỡ
   class -hidden là phần tử hiện lại bình thường. */
.dx-isolated-wrapper .dxbl-loading-panel-hidden,
.report-layout .dxbl-loading-panel-hidden {
    display: none !important;
}

/* Ẩn tab strip dọc bên phải viewer (2 tab Search + Export options) —
   thừa theo chuẩn "1 cửa": Search đã ẩn khỏi toolbar, Export đi qua
   MudMenu footer. 2 rule này ĐI CẶP với nhau: DevExpress set INLINE
   width trên .dxbrv-surface-container (chừa sẵn 3.5rem cho strip) →
   ẩn strip xong phải trả lại full width (!important thắng inline).
   Nếu sau này muốn hiện lại tab → xóa CẢ 2 rule. Thay thế chính thống:
   OnCustomizeTabs (có trong DevExpress.Blazor.v25.2.Viewer.dll) —
   cân nhắc khi gộp dialog ở bước 7 chuẩn hóa report. */
.dx-isolated-wrapper .dxbrv-tabs,
.report-layout .dxbrv-tabs {
    display: none !important;
}
.dx-isolated-wrapper .dxbrv-surface-container,
.report-layout .dxbrv-surface-container {
    width: 100% !important;
}


/* ── Dialog lịch gửi chia tab (2026-08-11) ─────────────────────────────────────
   Responsive-Layout-Standard §7.2a: 3 tab lệch nhau nhiều (tab Lịch chạy co giãn theo chế độ,
   tab Chung dài gấp mấy lần) → KHOÁ chiều cao vùng panel + cuộn bên trong, thay vì để hộp thoại
   tự phình/tụt mỗi lần đổi tab (nút Lưu/Hủy nhảy khỏi chỗ ngón tay vừa nhắm).
   min-height KHÔNG dùng được ở ca này — nó chỉ cứu chiều "tab quá ngắn".
   Đặt ở app.css chứ không phải .razor.css: DOM của .mud-tabs-panels do MudTabs render nên
   không mang scope attribute của component (cùng lý do với .ft-notif-popover). */
.ft-schedule-tabs .mud-tabs-panels {
    height: 60vh;
    overflow-y: auto;
    padding-right: 8px;   /* chừa chỗ thanh cuộn, không đè lên nút tăng/giảm của ô số ở mép phải */
}

@media (max-width: 600px) {
    /* Màn nhỏ: thanh tiêu đề + nút chiếm tỉ lệ lớn hơn nên nới thêm chiều cao panel */
    .ft-schedule-tabs .mud-tabs-panels { height: 68vh; }
}

/* ── Trang Cảnh báo máy 3 tab (2026-08-11) ─────────────────────────────────────
   Responsive-Layout-Standard §7.2a, ca "lệch ÍT": 3 tab đều là bảng giới hạn theo pager
   (cận trên xấp xỉ nhau) → chỉ cần min-height chặn tab ngắn/lúc nạp làm trang sụp,
   KHÔNG khoá height + cuộn trong như dialog lịch gửi (trang cuộn ở document là bình thường,
   khoá height sẽ đẻ 2 thanh cuộn). Đi cặp với KeepPanelsAlive trên MudTabs (panel nạp 1 lần,
   chuyển tab = CSS toggle). Đặt ở app.css: DOM .mud-tabs-panels do MudTabs render nên
   không mang scope attribute của component (cùng lý do .ft-schedule-tabs ở trên). */
.ft-alarm-tabs .mud-tabs-panels {
    min-height: 55vh;
}

/* ── Bề rộng thẻ máy trên dashboard — MỘT NGUỒN cho 2 nơi (GĐ8 2026-08-15) ───────────────
   Dùng ở: .machine-device-grid (--card-w, quyết định số cột + JS auto-pagination đọc biến này)
   và khung "Xem trước" trong tab Cấu hình dashboard. Xem trước PHẢI rộng đúng bằng ô lưới thật,
   nếu không thì mọi phán đoán "chữ có cắt không / mấy ô một hàng" đều sai — user báo đúng lỗi đó
   khi xem trước còn thả tự do.
   480px = 400 cũ + 20% (user chốt): thẻ máy nay mang tới 15 ô cấu hình được nên cần rộng hơn.
   Energy/Water/Generic vẫn 400px trong CSS riêng của chúng — nội dung ít hơn hẳn, cố ý không đổi. */
:root {
    --ft-machine-card-w: 480px;
    /* Bề rộng thẻ ĐIỆN — cùng vai trò cho tab Cấu hình dashboard của trang Điểm đo điện.
       ⚠️ Giữ BẰNG `--card-w` của .energy-device-grid (EnergyDashboardPage.razor.css). Hai số lệch
       nhau thì khung xem trước lại nói dối về việc chữ có bị cắt — đúng lỗi đã sửa bên Machine.
       Cố ý KHÔNG dùng chung biến với máy: thẻ điện ít nội dung hơn nên vẫn 400px. */
    --ft-energy-card-w: 400px;
}

/* Trang Máy 2 tab (GĐ7 2026-08-15 — Danh sách + Cấu hình dashboard, ADR-008 làn C).
   §7.2a ca "lệch ít": cả 2 tab đều cao (bảng có pager ↔ 2 cột 15 ô) → min-height chặn tab
   ngắn/lúc nạp làm trang sụp là đủ, KHÔNG khoá height (trang cuộn ở document là bình thường).
   Đi cặp KeepPanelsAlive. Đặt ở app.css vì .mud-tabs-panels do MudTabs render, không mang
   scope attribute của component. */
.ft-machine-tabs .mud-tabs-panels {
    min-height: 60vh;
}

/* Trang Điểm đo điện 2 tab (2026-08-15) — cùng lý do với .ft-machine-tabs ngay trên. */
.ft-energy-tabs .mud-tabs-panels {
    min-height: 60vh;
}

/* Form máy — GIỮ class dù form nay chỉ còn 1 tab: chưa gỡ MudTabs khỏi các form khác dùng
   chung quy ước này. Vô hại nếu không có phần tử nào mang class. */
.ft-machine-form-tabs .mud-tabs-panels {
    min-height: 480px;
}

/* Dòng bảng đang được chọn (trang Nhóm người nhận: nhóm đang mở panel thành viên bên dưới).
   Dùng biến palette của MudBlazor nên tự đúng ở cả sáng lẫn tối — đặt màu cứng là dark mode
   chữ chìm vào nền. !important vì MudDataGrid Striped/Hover đã có nền riêng cho từng dòng. */
.mud-table-row.ft-row-selected > td {
    background-color: var(--mud-palette-primary-hover) !important;
}

/* ── SCADA xoay-giả: cho tầng HỘP THOẠI xoay theo bản vẽ ────────────────────
   Bật bởi scada.js (toggleLandscape thêm class + 2 biến; _clearLandscape gỡ cả ba).
   Đặt ở app.css chứ không phải khối style của component: 3 provider của MudBlazor render ở gốc
   MainLayout, NGOÀI mọi component SCADA — CSS scoped không với tới.

   ⚠️ Vì sao xoay CONTAINER chứ không xoay .mud-dialog: .mud-dialog đã có
   `animation: mud-open-dialog-center ... both`, mà animation đó chiếm thuộc tính `transform` với
   fill-mode `both` ⇒ rule transform của ta sẽ bị animation đè vĩnh viễn.

   ⚠️ Vì sao dùng biến --scada-rot-* chứ không 100vh/100vw: trên di động hai đơn vị đó KHÁC
   window.innerWidth/innerHeight (thanh URL thu/phóng). Khung xoay của canvas ghim bằng px lấy từ
   innerWidth/innerHeight, nên nếu tầng này dùng vh/vw thì hai lớp đo bằng hai thước và trôi khỏi nhau. */
body.scada-landscape .mud-dialog-container {
    /* Hoán trục: container gốc là 100%×100% (chân dung). Chỉ thêm transform mà không hoán
       kích thước thì hộp xoay xong nằm lệch hẳn ra ngoài mép màn. */
    width: var(--scada-rot-w);
    height: var(--scada-rot-h);
    transform-origin: top left;
    transform: rotate(90deg) translateY(-100%);
}

/* max-height gốc của .mud-dialog tính bằng 100dvh — sau khi xoay, trục "cao" của hộp thoại nằm
   NGANG trên màn hình, nên 100dvh là con số của TRỤC SAI ⇒ hộp thoại cao hơn khung xoay và tràn.
   Ghim lại theo chiều cao THẬT của khung đã xoay. Bề rộng cũng vậy: rule mobile phía trên dùng
   88vw (bề rộng màn DỌC), ở đây phải là bề rộng khung ĐÃ xoay. */
body.scada-landscape .mud-dialog {
    max-width: min(444px, calc(var(--scada-rot-w) * 0.88)) !important;
    max-height: calc(var(--scada-rot-h) - 32px) !important;
}
