/* ===========================================================================
   STYLE.CSS — CSS đặc thù ứng dụng Homestay Manager (app-shell, sidebar,
   topbar, Gantt calendar, accordion, stat-tile...). Mọi token dùng chung
   (màu/spacing/radius/shadow/font) nằm ở design-system.css — file này PHẢI
   được include sau design-system.css trong base.html.
   =========================================================================== */
:root {
    --sidebar-width: 256px;
    --topbar-height: 64px;

    /* Dark là mặc định: sidebar nền đen tuyền hơn cả bg-app (phân lớp rõ),
       chữ xám sáng, item active dùng chữ brand sáng (brand-400) dễ đọc trên nền đen. */
    --sidebar-bg: #14161b;
    --sidebar-bg-hover: #1f232b;
    --sidebar-text: #9a9fa8;
    --sidebar-text-active: var(--brand-400);
    --sidebar-border: #252932;
}
:root[data-theme="light"] {
    --sidebar-bg: #ffffff;
    --sidebar-bg-hover: #eef0f3;
    --sidebar-text: #565c66;
    --sidebar-text-active: var(--brand-600);
    --sidebar-border: #e4e7ec;
}
@media (prefers-color-scheme: light) {
    :root:not([data-theme="light"]):not([data-theme="dark"]) {
        --sidebar-bg: #ffffff;
        --sidebar-bg-hover: #eef0f3;
        --sidebar-text: #565c66;
        --sidebar-text-active: var(--brand-600);
        --sidebar-border: #e4e7ec;
    }
}

/* ===================== FADE-IN ON LOAD ===================== */
.app-content > * {
    animation: fadeSlideUp 0.35s var(--ease-out) both;
}
.app-content > *:nth-child(2) { animation-delay: 0.03s; }
.app-content > *:nth-child(3) { animation-delay: 0.06s; }
.app-content > *:nth-child(4) { animation-delay: 0.09s; }
.app-content > *:nth-child(5) { animation-delay: 0.12s; }

/* ===================== TAP / TOUCH FEEDBACK ===================== */
/* Bấm mượt hơn trên mobile: bỏ hiệu ứng chớp xanh mặc định của trình duyệt,
   thay bằng phản hồi nhấn (:active) nhất quán cho mọi nút/thẻ có thể bấm. */
a, button, .btn, .sidebar-link, .gantt-cell, .gantt-booking,
.status-select, .card[data-clickable], .table tbody tr {
    -webkit-tap-highlight-color: transparent;
}
.sidebar-link:active {
    background: var(--sidebar-bg-hover);
    transform: scale(0.99);
}
.gantt-booking:active {
    transform: scale(0.98);
}
/* Chạm giữ lâu trên ô lịch không hiện menu "Lưu ảnh/Copy" mặc định của mobile */
.gantt-cell, .gantt-booking, .sidebar-link, .btn {
    -webkit-touch-callout: none;
}

/* ===================== APP SHELL ===================== */
.app-shell {
    display: flex;
    min-height: 100vh;
}

/* ----- Sidebar ----- */
.app-sidebar {
    width: var(--sidebar-width);
    flex-shrink: 0;
    background: var(--sidebar-bg);
    color: var(--sidebar-text);
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 1040;
    transition: transform 0.25s var(--ease-out), background-color var(--transition-base);
}

.sidebar-brand {
    height: var(--topbar-height);
    margin: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 var(--space-4);
    font-weight: 700;
    font-size: var(--text-base);
    color: var(--text-primary);
    border-bottom: 1px solid var(--sidebar-border);
    flex-shrink: 0;
}
.sidebar-brand a {
    color: var(--text-primary);
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.sidebar-close {
    background: none;
    border: none;
    color: var(--text-primary);
    font-size: var(--text-xl);
    line-height: 1;
    cursor: pointer;
}

.sidebar-nav {
    padding: var(--space-3) var(--space-2);
    overflow-y: auto;
    flex: 1;
}
.sidebar-nav-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.sidebar-link {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: 11px 14px;
    border-radius: var(--radius-sm);
    color: var(--sidebar-text);
    text-decoration: none;
    font-size: var(--text-sm);
    font-weight: 500;
    margin-bottom: 3px;
    transition: background var(--transition-fast), color var(--transition-fast), transform var(--transition-fast);
}
.sidebar-link .side-icon { font-size: var(--text-base); width: 20px; text-align: center; }
.sidebar-link:hover { background: var(--sidebar-bg-hover); color: var(--text-primary); transform: translateX(2px); }
/* Item đang chọn: nền subtle mờ + chữ brand sáng (--sidebar-text-active) +
   thanh chỉ báo 3px bên trái, đúng tinh thần dark-mode-first (không nền tươi
   chói, không chữ trắng trên nền tươi). */
.sidebar-link.active {
    background: var(--sidebar-bg-hover);
    color: var(--sidebar-text-active);
    font-weight: 700;
    border-left: 3px solid var(--brand-500);
    padding-left: 11px;
}

.sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(8, 9, 16, 0.55);
    z-index: 1030;
    backdrop-filter: blur(1px);
}
.sidebar-overlay.is-visible { display: block; }

/* ----- Theme toggle ----- */
.theme-toggle-wrap {
    padding: var(--space-3) var(--space-3) var(--space-4);
    border-top: 1px solid var(--sidebar-border);
    flex-shrink: 0;
}
.sidebar-user-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}
.sidebar-user-name {
    color: var(--sidebar-text);
    font-size: var(--text-sm);
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sidebar-user-row .theme-toggle {
    width: auto;
    padding: 6px 10px;
    font-size: var(--text-xs);
    flex-shrink: 0;
}
.theme-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    gap: var(--space-2);
    padding: 9px 12px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--sidebar-border);
    background: var(--sidebar-bg-hover);
    color: var(--sidebar-text);
    font-size: var(--text-sm);
    font-weight: 500;
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast);
}
.theme-toggle:hover { color: var(--text-primary); }
.theme-toggle .theme-toggle-icon { font-size: var(--text-sm); }

/* ----- Main area ----- */
.app-main {
    flex: 1;
    margin-left: var(--sidebar-width);
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.app-topbar {
    min-height: var(--topbar-height);
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-2) var(--space-6);
    position: sticky;
    top: 0;
    z-index: 20;
    transition: background-color var(--transition-base), border-color var(--transition-base);
}
/* Hàng trên: hamburger + tiêu đề + CTA — tách riêng khỏi khung tìm kiếm để
   xuống hàng độc lập trên mobile (xem media query cuối file). */
.topbar-row-main {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    flex: 1;
    min-width: 0;
}
.topbar-toggle {
    display: none;
    background: none;
    border: none;
    font-size: var(--text-xl-plus);
    cursor: pointer;
    color: var(--text-secondary);
    border-radius: var(--radius-sm);
    transition: background var(--transition-fast);
}
.topbar-toggle:hover { background: var(--bg-subtle); }
.topbar-title {
    font-weight: 700;
    font-size: var(--text-lg);
    color: var(--text-primary);
    flex: 1;
    margin: 0;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.topbar-cta { white-space: nowrap; flex-shrink: 0; }

.app-content {
    padding: var(--space-6) var(--space-6) 40px;
    flex: 1;
    min-width: 0;
}
.flash-messages {
    list-style: none;
    margin: 0;
    padding: 0;
}
.flash-messages .alert {
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
}
.room-type-list,
.document-list {
    list-style: none;
    padding: 0;
}

/* ===================== TÌM KIẾM TOÀN CỤC — khung cố định trên topbar =====================
   ĐẶT TRƯỚC khối RESPONSIVE bên dưới: đây là base rule không điều kiện —
   nếu đứng SAU 1 rule cùng specificity nằm trong @media (dù có điều kiện),
   base rule vẫn thắng theo thứ tự nguồn (source order), ghi đè mất override
   mobile "width: 100%" (đã tái hiện đúng bug này qua E2E: width đo được
   luôn là 320px trên viewport 375px thay vì full-width). */
.global-search {
    position: relative;
    flex-shrink: 0;
    width: 320px;
    max-width: 100%;
}
.global-search-bar {
    display: flex;
    gap: var(--space-2);
}
.global-search-input {
    flex: 1;
    min-width: 0;
    min-height: 40px;
    padding: 0 var(--space-3);
    border-radius: var(--radius-sm);
    border: 1.5px solid var(--border-color-strong);
    background: var(--bg-app);
    color: var(--text-primary);
    font-size: var(--text-sm);
}
.global-search-input:focus {
    outline: none;
    border-color: var(--brand-600);
    box-shadow: 0 0 0 3px rgba(72, 112, 191, 0.28);
}
.global-search-submit { flex-shrink: 0; white-space: nowrap; }

.global-search-overlay {
    position: fixed;
    inset: 0;
    z-index: 1055;
    background: rgba(0, 0, 0, 0.35);
}

/* Panel giờ CHỈ chứa kết quả (input đã tách ra ngoài, luôn hiển thị) — bung
   xuống dưới khung tìm kiếm khi có nội dung gõ. */
.global-search-panel {
    position: absolute;
    top: calc(100% + var(--space-2));
    left: 0;
    right: 0;
    z-index: 1056;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: var(--space-3);
}

.global-search-results {
    max-height: 60vh;
    overflow-y: auto;
}
/* Đợt 3 design audit (#4) — gợi ý còn kết quả cuộn xuống tiếp: fade gradient
   đặt trên .global-search-panel (KHÔNG phải bên trong .global-search-results
   đang cuộn) — bài học từ Đợt 2 (xem CLAUDE.md): overlay cần nằm NGOÀI khung
   đang overflow:auto để không bị cắt/cuộn theo nội dung, đơn giản hơn nhiều
   so với cách "sticky bên trong khung cuộn" đã dùng cho cuộn NGANG (#5) —
   ở đây chỉ cần 1 lớp phủ TĨNH ở đáy panel, ẩn/hiện qua class JS bật/tắt
   theo còn cuộn được xuống hay không, không cần theo dõi vị trí pixel. */
.global-search-panel { position: relative; }
.global-search-panel::after {
    content: "";
    position: absolute;
    left: var(--space-3);
    right: var(--space-3);
    bottom: var(--space-3);
    height: 28px;
    pointer-events: none;
    border-radius: 0 0 var(--radius-md) var(--radius-md);
    background: linear-gradient(to bottom, transparent, var(--bg-surface));
    opacity: 0;
    transition: opacity var(--transition-fast);
}
.global-search-panel.has-more-below::after { opacity: 1; }
.global-search-more {
    text-align: center;
    color: var(--text-tertiary);
    font-size: var(--text-xs);
    padding: var(--space-2) var(--space-1) 0;
    margin: var(--space-2) 0 0;
    border-top: 1px solid var(--border-color);
}
.global-search-group-label {
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-tertiary);
    margin: var(--space-3) 0 var(--space-1);
}
.global-search-group-label:first-child { margin-top: 0; }

.global-search-item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 48px;
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    text-decoration: none;
    color: var(--text-primary);
    transition: background var(--transition-fast);
}
.global-search-item:hover, .global-search-item.is-active { background: var(--bg-subtle); }
.global-search-item-icon { font-size: var(--text-lg); flex-shrink: 0; }
.global-search-item-body { display: flex; flex-direction: column; min-width: 0; }
.global-search-item-body strong { font-size: var(--text-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.global-search-item-body small { color: var(--text-secondary); font-size: var(--text-xs); }

.global-search-empty {
    padding: var(--space-4) var(--space-2);
    text-align: center;
    color: var(--text-secondary);
    font-size: var(--text-sm);
}

.global-search-skeleton { padding: var(--space-2); }
.global-search-skeleton .skeleton-line {
    height: 40px;
    border-radius: var(--radius-sm);
    background: var(--bg-subtle);
    margin-bottom: var(--space-2);
    animation: skeletonPulse 1.2s ease-in-out infinite;
}
@keyframes skeletonPulse {
    0%, 100% { opacity: 0.6; }
    50% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .global-search-skeleton .skeleton-line { animation: none; }
}

/* ===================== RESPONSIVE ===================== */
@media (max-width: 991.98px) {
    .app-sidebar {
        transform: translateX(-100%);
        box-shadow: var(--shadow-drawer);
    }
    .app-sidebar.is-open { transform: translateX(0); }
    .app-main { margin-left: 0; }
    .topbar-toggle {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 44px;
        height: 44px;
    }
    /* Vùng bấm to hơn cho menu bên trái trên tablet/mobile */
    .sidebar-link {
        padding: 13px 14px;
        font-size: var(--text-sm);
    }
}

@media (max-width: 575.98px) {
    .app-content { padding: var(--space-4) var(--space-3) 32px; }
    /* Khung tìm kiếm LUÔN hiển thị (không còn ẩn sau icon) — topbar cho
       phép xuống hàng, khung tìm kiếm chiếm trọn hàng riêng full-width. */
    .app-topbar { padding: var(--space-2) var(--space-3); gap: var(--space-3); flex-wrap: wrap; }
    .global-search { width: 100%; order: 3; }
    .topbar-title { font-size: var(--text-base); }
    .topbar-cta { font-size: var(--text-xs); padding: 6px 10px; }

    /* Nút hành động trong bảng: nới khoảng cách để không bấm nhầm hàng khác */
    .table td, .table th {
        padding-top: 0.65rem;
        padding-bottom: 0.65rem;
    }
    .table td .btn, .table td .btn + form,
    .table td form + form {
        margin-top: 4px;
    }

    .status-select {
        min-width: 140px;
        padding-top: 0.45rem;
        padding-bottom: 0.45rem;
    }

    .page-header {
        align-items: stretch;
    }
    .page-header > a.btn, .page-header > form {
        width: 100%;
    }
}

/* ===================== SHARED UI (đặc thù app) ===================== */
/* Danh sách chi tiết dạng "Nhãn: Giá trị" (thay cho các thẻ <p><strong> rời rạc
   trước đây) - mỗi cặp dt/dd nằm trên 1 dòng, dt in đậm giống <strong> cũ. */
.detail-list {
    margin: 0;
}
.detail-list > div {
    margin-bottom: var(--space-3);
}
.detail-list > div:last-child {
    margin-bottom: 0;
}
.detail-list dt {
    display: inline;
    font-weight: 700;
    color: var(--text-primary);
}
.detail-list dd {
    display: inline;
    margin: 0;
    color: var(--text-secondary);
}
.detail-list dt::after {
    content: ": ";
}

.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}
.page-header h1, .page-header h2, .page-header h3 {
    margin: 0;
}

.table-responsive-card {
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    position: relative;
    transition: background-color var(--transition-base), border-color var(--transition-base);
}
.table-responsive-card .table {
    margin-bottom: 0;
}
/* Không cho chữ trong bảng tự xuống dòng khi màn hình hẹp - nếu không đủ chỗ thì
   cuộn ngang (đã có sẵn từ .table-responsive của Bootstrap), thay vì bị bóp chữ
   xuống dòng làm bảng cao ngất, khó đọc trên mobile. */
.table-responsive-card th,
.table-responsive-card td {
    white-space: nowrap;
}
.table-responsive-card .table-responsive {
    -webkit-overflow-scrolling: touch;
}

/* Đợt 1 design audit (#Rooms select trạng thái) — TRƯỚC ĐÂY <select> tô nền
   MÀU ĐẶC theo trạng thái (--success-500/--warning-500), trông như 1 badge
   cố định thay vì 1 control có thể bấm/đổi giá trị — gây nhầm lẫn "đây có
   tương tác được không". Giờ <select> dùng style CHUẨN như mọi form-select
   khác trong app (nền/viền trung tính), trạng thái chỉ còn thể hiện qua 1
   chấm tròn màu (.status-dot) đặt cạnh — cùng vai trò với badge trạng thái
   booking, không còn lẫn giữa "control" và "chỉ báo trạng thái". */
.room-status-form {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}
.status-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
}
.status-dot-active { background-color: var(--success-500); }
.status-dot-maintenance { background-color: var(--warning-500); }

.status-select {
    font-weight: 600;
    width: auto;
    min-width: 150px;
}

/* Stat tiles used on Reports / list pages */
.stat-tile-list {
    list-style: none;
    padding: 0;
}
.stat-tile {
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    padding: var(--space-4);
    height: 100%;
    margin: 0;
    transition: background-color var(--transition-base), border-color var(--transition-base), transform var(--transition-fast);
}
.stat-tile:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.stat-tile .stat-label {
    font-size: var(--text-xs);
    color: var(--text-secondary);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin: 0;
}
.stat-tile .stat-value {
    font-size: var(--text-xl);
    font-weight: 800;
    color: var(--text-primary);
    margin-top: var(--space-1);
    margin-left: 0;
}
/* Modifier cho tile có 2 dòng số (vd "so với tháng trước": % công suất +
   % doanh thu) — chữ nhỏ hơn .stat-value mặc định để vừa 2 dòng gọn hơn. */
.stat-tile .stat-value.stat-value-compact { font-size: var(--text-lg); }

@media (max-width: 575.98px) {
    .stat-tile .stat-value { font-size: var(--text-xl-plus); }
}

/* ---- Accordion (Quản lý khách hàng) ---- */
.accordion-item {
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md) !important;
    overflow: hidden;
    margin-bottom: var(--space-2);
}
.accordion-button {
    background: var(--bg-surface);
    color: var(--text-primary);
    font-weight: 600;
    border-radius: 0 !important;
}
.accordion-button:not(.collapsed) {
    background: var(--bg-subtle);
    color: var(--text-primary);
    box-shadow: none;
}
.accordion-button:focus { box-shadow: 0 0 0 3px rgba(72, 112, 191, 0.28); }
/* Icon mũi tên Bootstrap mặc định là màu đen — dark là mặc định nên cần
   invert để hiện rõ trên nền tối; light override trả lại icon gốc. */
.accordion-button::after { filter: invert(1) brightness(1.6); }
:root[data-theme="light"] .accordion-button::after { filter: none; }

/* ---- Header accordion (Quản lý khách hàng): tên phòng+ngày trên 1 dòng,
   các badge trạng thái/số khách xuống dòng riêng bên dưới trên mobile, tránh
   5 phần tử chen chúc vỡ dòng lộn xộn như flex ngang mặc định của Bootstrap. */
.guest-accordion-btn {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
}
.guest-accordion-title { font-weight: 600; }
.guest-accordion-badges {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    align-items: center;
}
.guest-accordion-badges .badge { margin: 0; }

/* ---- Bảng danh sách khách trong accordion (Quản lý khách hàng) — chuyển
   sang dạng card list trên mobile thay vì cuộn ngang 6 cột, vì đây là bảng
   thao tác nhiều (tải ảnh CCCD, xóa) chứ không chỉ xem, cuộn ngang khó dùng
   trên màn cảm ứng. Vẫn dùng chung 1 DOM (<table>/<tr>/<td>) với desktop —
   chỉ đổi display bằng CSS — để JS (querySelector theo class trên <td>) không
   cần viết lại 2 lần. */
@media (max-width: 767.98px) {
    .guest-table-wrap .table-responsive { overflow-x: visible; }
    .guest-table thead { display: none; }
    .guest-table, .guest-table tbody, .guest-table tr, .guest-table td {
        display: block;
        width: 100% !important;
    }
    .guest-table tr {
        border: 1px solid var(--border-color);
        border-radius: var(--radius-sm);
        padding: var(--space-3);
        margin-bottom: var(--space-3);
        background: var(--bg-surface);
    }
    .guest-table tr:last-child { margin-bottom: 0; }
    .guest-table td {
        padding: var(--space-1) 0;
        border: none !important;
        white-space: normal;
    }
    .guest-table td[data-label]:not([data-label=""])::before {
        content: attr(data-label);
        display: block;
        font-size: var(--text-xs);
        font-weight: 700;
        color: var(--text-tertiary);
        text-transform: uppercase;
        letter-spacing: 0.04em;
        margin-bottom: 2px;
    }
    /* Cột số thứ tự (#) không cần nhãn riêng, gộp lên đầu card cho gọn */
    .guest-table td[data-label="#"] {
        font-weight: 700;
        color: var(--text-tertiary);
        padding-bottom: var(--space-2);
        border-bottom: 1px solid var(--border-color) !important;
        margin-bottom: var(--space-2);
    }
    .guest-table td[data-label="#"]::before { content: "Khách "; display: inline; }
    /* Vùng chạm input file + nút Xóa đủ lớn hơn trên mobile */
    .guest-table .guest-cccd-input { min-height: 44px; }
    .guest-table td[data-label=""] .btn { min-height: 44px; width: 100%; }
}

/* ===================== GANTT CALENDAR ===================== */
.gantt-legend {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 4px 16px;
}
.gantt-legend li {
    display: inline-flex;
    align-items: center;
    color: var(--text-secondary);
    font-size: var(--text-sm);
}

.gantt-toolbar {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
    flex-wrap: wrap;
}

/* Bọc ngoài .gantt-wrapper để làm điểm neo layout (width: fit-content) —
   gợi ý cuộn ngang thật giờ nằm trên .gantt-wrapper trực tiếp qua
   .scroll-hint-x (xem cuối file, gắn bằng static/js/scroll-hint.js). */
.gantt-scroll-hint {
    position: relative;
    width: fit-content;
    max-width: 100%;
}

.gantt-wrapper {
    background: var(--bg-surface);
    border-radius: var(--radius-md);
    overflow: auto;
    max-height: 75vh;
    /* Co lại vừa đúng nội dung bên trong (vd chọn "Xem 7 ngày" thì lưới hẹp hơn) -
       tránh để lại khoảng trắng thừa bên phải khi lưới hẹp hơn khung chứa. Khi lưới
       rộng hơn khung chứa thì tự cuộn ngang như bình thường nhờ overflow:auto ở trên. */
    width: 100%;
    border: 1px solid var(--border-color);
    box-shadow: var(--shadow-sm);
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    transition: background-color var(--transition-base), border-color var(--transition-base);
}

.gantt-root {
    display: flex;
    flex-direction: column;
}

.gantt-header-row {
    display: flex;
    position: sticky;
    top: 0;
    z-index: 30;
}

.gantt-corner {
    position: sticky;
    left: 0;
    z-index: 31;
    background: var(--sidebar-bg);
    color: var(--text-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: var(--text-xs);
    flex-shrink: 0;
    border-right: 1px solid var(--sidebar-border);
    border-bottom: 1px solid var(--sidebar-border);
}

.gantt-header-dates {
    display: flex;
    flex-shrink: 0;
}

.gantt-date-header {
    flex-shrink: 0;
    box-sizing: border-box;
    background: var(--sidebar-bg);
    color: var(--text-secondary);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: var(--text-xs);
    border-right: 1px solid var(--sidebar-border);
    border-bottom: 1px solid var(--sidebar-border);
    padding: 6px 2px;
    text-align: center;
}
.gantt-date-header.is-today { background: var(--brand-500); color: var(--brand-600); font-weight: 700; }
.gantt-date-header .dow { font-size: var(--text-2xs); opacity: 0.85; }
.gantt-date-header .dnum { font-size: var(--text-sm); font-weight: 700; }

.gantt-body {
    display: flex;
    flex-direction: column;
}

.gantt-group-row {
    position: sticky;
    left: 0;
    box-sizing: border-box;
    background: var(--bg-subtle);
    color: var(--text-secondary);
    font-weight: 700;
    font-size: var(--text-xs);
    letter-spacing: 0.05em;
    text-transform: uppercase;
    padding: 7px 14px;
    border-bottom: 1px solid var(--border-color);
    border-top: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    flex-shrink: 0;
}

.gantt-room-row:nth-child(odd) .gantt-room-label,
.gantt-room-row:nth-child(odd) .gantt-room-timeline {
    background-image: none;
}
.gantt-room-row:hover .gantt-room-label {
    background: var(--bg-hover);
}

.gantt-room-row {
    display: flex;
    flex-shrink: 0;
    border-bottom: 2px solid var(--border-color);
}

.gantt-room-label {
    position: sticky;
    left: 0;
    z-index: 10;
    flex-shrink: 0;
    box-sizing: border-box;
    background: var(--bg-surface);
    border-right: 1px solid var(--border-color);
    padding: 8px 12px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    transition: background-color var(--transition-fast);
}
.gantt-room-label .room-number { font-weight: 700; font-size: var(--text-base); color: var(--text-primary); }
.gantt-room-label .room-type { font-size: var(--text-xs); color: var(--text-tertiary); }

.gantt-room-timeline {
    position: relative;
    flex-shrink: 0;
    overflow: hidden;
    box-sizing: border-box;
    background-image: repeating-linear-gradient(
        to right,
        var(--bg-subtle) 0, var(--bg-subtle) 1px,
        transparent 1px, transparent 110px
    );
    user-select: none;
}

.gantt-cell {
    position: absolute;
    top: 0;
    bottom: 0;
    box-sizing: border-box;
}
.gantt-cell.is-today { background: rgba(91, 141, 239, 0.08); }

/* Mặc định (chỉ 1 đặt phòng trong ô ngày đó): neo Ở TRÊN ô, không căn giữa. */
.gantt-booking {
    position: absolute;
    top: 6px;
    height: 36px;
    box-sizing: border-box;
    border-radius: var(--radius-sm);
    padding: 4px 8px;
    margin: 0 2px;
    /* Màu chữ do từng .status-* quyết định (chữ đậm cùng tông trên nền pastel) —
       không set màu chung ở đây để tránh phải override lại mỗi trạng thái. */
    font-size: var(--text-xs);
    line-height: 1.3;
    cursor: pointer;
    overflow: hidden;
    /* Nền pastel nhạt cần viền/shadow đen rõ để nổi khối trên nền lịch tối. */
    border: 1px solid rgba(0, 0, 0, 0.25);
    box-shadow: var(--shadow-sm);
    display: flex;
    flex-direction: column;
    justify-content: center;
    z-index: 5;
    transition: box-shadow var(--transition-fast), transform var(--transition-fast);
}
.gantt-booking:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); z-index: 6; }
.gantt-booking .b-name {
    font-weight: 700;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.gantt-booking .b-time {
    font-size: var(--text-2xs);
    opacity: 0.92;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* 2 đặt phòng cùng ngày, cùng phòng -> chia ô làm 2 nửa TRÊN/DƯỚI bằng mốc 50%,
   đảm bảo không bao giờ đè lên nhau dù chiều cao hàng là bao nhiêu. */
.gantt-booking.stack-top {
    top: 4px;
    bottom: 50%;
    height: auto;
}
.gantt-booking.stack-bottom {
    top: 50%;
    bottom: 4px;
    height: auto;
}
.gantt-booking.stack-top .b-name,
.gantt-booking.stack-bottom .b-name { font-size: var(--text-2xs); }
.gantt-booking.stack-top .b-time,
.gantt-booking.stack-bottom .b-time { font-size: var(--text-2xs); }

/* Mobile: card xếp chồng chỉ cao ~36px (nửa hàng 80px), dưới mức chạm khuyến
   nghị 44px — tăng chiều cao hàng phòng khi có ít nhất 1 card xếp chồng để dễ
   bấm hơn trên màn hình cảm ứng. ROOM_ROW_HEIGHT trong JS set qua style inline
   nên cần !important để ghi đè đúng breakpoint mobile. */
@media (max-width: 767.98px) {
    .gantt-room-row:has(.gantt-booking.stack-top) {
        height: 112px !important;
    }
    .gantt-room-row:has(.gantt-booking.stack-top) .gantt-room-timeline,
    .gantt-room-row:has(.gantt-booking.stack-top) .gantt-room-label {
        height: 112px !important;
    }
}

/* Cùng bảng màu với .badge-status-* (design-system.css) — đọc chung biến
   --status-*-bg/-fg nên tự đổi theo theme (dark = nền trầm/chữ sáng, light =
   pastel nhạt/chữ đậm), nổi khối nhờ border/shadow (xem .gantt-booking). */
.status-pending       { background: var(--status-pending-bg);       color: var(--status-pending-fg); }
.status-confirmed     { background: var(--status-confirmed-bg);     color: var(--status-confirmed-fg); }
.status-checkin_today { background: var(--status-checkin_today-bg); color: var(--status-checkin_today-fg); }
.status-checked_in    { background: var(--status-checked_in-bg);    color: var(--status-checked_in-fg); }
.status-checked_out   { background: var(--status-checked_out-bg);   color: var(--status-checked_out-fg); }
.status-cancelled     { background: var(--status-cancelled-bg);     color: var(--status-cancelled-fg); }

.gantt-legend-dot {
    display: inline-block;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    margin-right: 4px;
}

/* ===================== TOAST (xem partials/_toast.html) ===================== */
.toast-stack {
    position: fixed;
    top: var(--space-4);
    right: var(--space-4);
    z-index: 1080;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    width: min(360px, calc(100vw - 32px));
}
.toast-item {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    background: var(--bg-surface);
    color: var(--text-primary);
    border: 1px solid var(--border-color);
    border-left: 4px solid var(--brand-600);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg);
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-sm);
    animation: toastSlideIn var(--transition-modal);
}
/* Viền trái dùng *-600 (đậm) chứ không phải *-500 (pastel nhạt) — nền pastel
   nhạt sẽ gần như vô hình làm viền phân biệt trạng thái toast. */
.toast-item.toast-success { border-left-color: var(--success-600); }
.toast-item.toast-danger  { border-left-color: var(--danger-600); }
.toast-item.toast-warning { border-left-color: var(--warning-600); }
.toast-item.toast-info    { border-left-color: var(--info-600); }
.toast-item .toast-icon { font-size: var(--text-lg); line-height: 1; flex-shrink: 0; }
.toast-item .toast-msg { flex: 1; }
.toast-item .toast-close {
    background: none;
    border: none;
    color: var(--text-tertiary);
    cursor: pointer;
    font-size: var(--text-base);
    line-height: 1;
    padding: 0;
}
.toast-item .toast-close:hover { color: var(--text-primary); }

@media (max-width: 575.98px) {
    .toast-stack { left: var(--space-3); right: var(--space-3); width: auto; }
}

/* ===================== EMPTY STATE (xem partials/_empty_state.html) ===================== */
.empty-state {
    text-align: center;
    padding: var(--space-8) var(--space-4);
    color: var(--text-secondary);
}
.empty-state .empty-state-icon {
    font-size: var(--text-2xl);
    margin-bottom: var(--space-3);
    opacity: 0.85;
}
.empty-state .empty-state-title {
    font-weight: 700;
    color: var(--text-primary);
    font-size: var(--text-base);
    margin-bottom: var(--space-1);
}
.empty-state .empty-state-desc {
    font-size: var(--text-sm);
    margin-bottom: var(--space-4);
}

/* ===================== SKELETON LOADING (xem partials/_skeleton.html) ===================== */
.skeleton {
    background: var(--bg-subtle);
    border-radius: var(--radius-sm);
    animation: skeletonPulse 1.3s ease-in-out infinite;
}
.skeleton-text { height: 14px; margin-bottom: var(--space-2); }
.skeleton-text:last-child { margin-bottom: 0; width: 70%; }
.skeleton-title { height: 20px; width: 40%; margin-bottom: var(--space-3); }
.skeleton-row { height: 44px; margin-bottom: var(--space-2); border-radius: var(--radius-sm); }

@media (prefers-reduced-motion: reduce) {
    .skeleton { animation: none; opacity: 0.7; }
}

/* ===================== CARD FORM WIDTH ===================== */
/* Card chứa form/detail 1 cột không nên full-width trên desktop (dòng chữ quá
   dài khó đọc) — 2 mức rộng dùng lặp lại ở nhiều màn (settings, customer,
   booking, room-type...) nên gộp thành class chung thay vì style="" rải rác. */
.card-form-narrow, .card-detail-narrow { max-width: 600px; }
.card-form-wide { max-width: 800px; }
.form-search-narrow { max-width: 400px; }

/* ===================== ĐỢT 3 DESIGN AUDIT (#5) — FORM ĐẶT PHÒNG 2 CỘT =====
   Cột trái (col-lg-8): form như cũ, vẫn dùng .card-form-wide (max-width
   800px không xung đột vì cột chứa nó ở ≥1024px rộng hơn 800px trong hầu
   hết trường hợp thực tế — nếu màn quá hẹp trong khoảng 992-1200px, form
   tự co theo cột, max-width chỉ giới hạn chiều rộng TỐI ĐA, không ép giãn).
   Cột phải (col-lg-4): panel tóm tắt, sticky để luôn thấy khi cuộn form dài.
   Dưới 1024px: ẩn hẳn panel tóm tắt (mobile giữ đúng 1 cột như trước Đợt 3,
   không thêm hiển thị nào mới trên mobile theo đúng phạm vi đã chốt). */
.booking-summary-col { display: none; }
@media (min-width: 1024px) {
    .booking-summary-col { display: block; }
    .booking-summary-panel {
        position: sticky;
        top: calc(var(--topbar-height) + var(--space-4));
    }
}
.booking-summary-title {
    margin-bottom: var(--space-3);
    color: var(--text-primary);
}
.booking-summary-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    margin: 0;
}
.booking-summary-list > div {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.booking-summary-list dt {
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-tertiary);
}
.booking-summary-list dd {
    margin: 0;
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--text-primary);
}
#summaryPrice { color: var(--brand-400); }
:root[data-theme="light"] #summaryPrice { color: var(--brand-600); }

/* Preview tin nhắn xác nhận (booking_message.html) — giữ đúng xuống dòng như
   khi dán vào Zalo (white-space: pre-wrap), không dùng font monospace mặc
   định của <pre> để trông giống tin nhắn thật hơn là 1 khối code. */
.booking-message-preview {
    white-space: pre-wrap;
    font-family: var(--font-sans);
    font-size: var(--text-base);
    line-height: var(--line-height-body);
    color: var(--text-primary);
    margin: 0;
}

/* ===================== GIAI ĐOẠN E PHẦN 2 — BỘ LỌC ĐƠN ĐẶT PHÒNG ===================== */
.booking-filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
}
.booking-filter-field { min-width: 140px; }
.booking-filter-field .form-select, .booking-filter-field .form-control { width: 100%; }

.booking-filter-chips {
    display: flex;
    gap: var(--space-2);
    overflow-x: auto;
    padding-bottom: var(--space-2);
    margin-bottom: var(--space-4);
    -webkit-overflow-scrolling: touch;
}
.filter-chip {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    min-height: 36px;
    padding: 0 var(--space-3);
    border-radius: var(--radius-full);
    border: 1.5px solid var(--border-color-strong);
    background: var(--bg-surface);
    color: var(--text-secondary);
    font-size: var(--text-xs);
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    text-decoration: none;
    transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
/* .filter-chip dùng cả cho <button> (booking_list.html) lẫn <a> (reports.html)
   — <a> mặc định có gạch chân + màu link trình duyệt, phải reset tường minh
   ở đây thay vì dựa vào bootstrap.min.css (không đảm bảo thắng specificity).

   QUAN TRỌNG — bug specificity đã tái phát 1 lần: design-system.css có rule
   global "a" (nay là "a:not(.btn)") tô màu link toàn app. Mỗi lần rule đó
   đổi selector, specificity của nó đổi theo, có thể lại vượt qua chip. Để
   không phụ thuộc việc phải nhớ so sánh số mỗi lần, chip dùng CHUNG khuôn
   ":not(.btn)" như rule global — 2 rule luôn ngang specificity, ai đứng SAU
   trong nguồn (chip, ở đây) thắng, không cần tính lại. test_13b trong
   tests/e2e/test_reports.py bắt bug này qua RGB thật — chạy lại mỗi khi sửa
   rule "a" trong design-system.css. */
:root a.filter-chip:not(.btn) {
    color: var(--text-secondary);
}
.filter-chip:hover, .filter-chip:focus,
:root a.filter-chip:hover, :root a.filter-chip:focus {
    background: var(--bg-subtle);
    color: var(--text-secondary);
    text-decoration: none;
}
.filter-chip.active, :root a.filter-chip.active:not(.btn) {
    background: var(--brand-600);
    border-color: var(--brand-600);
    color: var(--text-on-brand);
}
.filter-chip.active:hover, .filter-chip.active:focus,
:root a.filter-chip.active:hover, :root a.filter-chip.active:focus {
    background: var(--brand-600);
    color: var(--text-on-brand);
}
/* focus-visible ring đã có sẵn qua rule chung a:focus-visible/button:focus-visible
   trong design-system.css — không cần lặp lại ở đây. */

@media (max-width: 767.98px) {
    .booking-filter-bar { display: none; }
}

/* ===================== MỤC F4 — TRANG BÁO CÁO ===================== */
.report-note {
    font-size: var(--text-xs);
    color: var(--text-tertiary);
    margin-top: var(--space-2);
    margin-bottom: var(--space-4);
    line-height: 1.5;
}

.revenue-chart-wrap {
    width: 100%;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: var(--space-4);
}
.revenue-chart-svg {
    width: 100%;
    height: 184px;
    display: block;
    overflow: visible;
}
.revenue-chart-bar {
    fill: var(--brand-500);
    transition: fill var(--transition-fast);
}
.revenue-chart-bar-group:hover .revenue-chart-bar { fill: var(--brand-600); }
/* Ngày không có doanh thu (height=0) vẫn cần thấy VỊ TRÍ cột trên trục thời
   gian — vẽ 1 vạch mờ 2px thay vì để trống hoàn toàn (xem revenue_chart()
   trong _revenue_chart.html, class .revenue-chart-bar-zero). */
.revenue-chart-bar-zero {
    fill: var(--border-color-strong);
}
/* Cột "hôm nay" — viền đậm hơn để định vị nhanh trong kỳ đang xem. */
.revenue-chart-bar-today {
    stroke: var(--brand-500);
    stroke-width: 1.5;
}
.revenue-chart-axis-label {
    font-size: var(--text-2xs);
    fill: var(--text-tertiary);
    font-family: var(--font-sans);
}
.revenue-chart-axis-label-today {
    fill: var(--brand-500);
    font-weight: 700;
}

.report-period-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-2);
}
/* Cụm chip preset dùng lại .booking-filter-chips/.filter-chip (giống trang
   Đơn đặt phòng) — chỉ 3 chip ngắn nên không cần overflow-x scroll như bản
   gốc (danh sách trạng thái có thể dài hơn), bỏ padding-bottom dành cho
   thanh cuộn ngang không dùng tới ở đây. */
.report-preset-chips {
    overflow-x: visible;
    padding-bottom: 0;
    margin-bottom: 0;
    flex: 0 1 auto;
}
.report-period-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: 0;
    flex: 0 1 auto;
}
.report-custom-range {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}
.report-custom-range .form-control { max-width: 160px; }

@media (max-width: 575.98px) {
    .report-period-row { flex-direction: column; align-items: stretch; }
    .report-preset-chips, .report-period-form { width: 100%; }
    .report-custom-range .form-control { max-width: none; flex: 1 1 120px; }
}

/* ===================== ĐỢT 2 DESIGN AUDIT (#5) — GỢI Ý CUỘN NGANG THẬT =====
   Thay cho CSS media-query cũ (chỉ hiện <768px, không hiện ở desktop dù
   bảng/lịch thực sự tràn — bug đã tìm qua audit). static/js/scroll-hint.js
   đo scrollWidth > clientWidth thật của MỌI khung cuộn (gắn tự động cho
   .gantt-wrapper và .table-responsive-card .table-responsive), gắn class
   .has-scroll-left/.has-scroll-right tương ứng còn cuộn được sang hướng
   nào — CSS chỉ vẽ gradient dựa theo 2 class này, hoạt động ở MỌI viewport,
   không riêng mobile.

   BÀI HỌC (3 vòng debug thật bằng ảnh chụp + getBoundingClientRect để dò
   nguyên nhân, xem CLAUDE.md mục bài học để không lặp lại):
   1. pseudo-element (::before/::after) + "position: sticky" + "float" —
      THẤT BẠI: marker debug ("background: red !important") luôn xuất hiện
      ở khoảng trắng DƯỚI nội dung thay vì overlay đúng chỗ. Chuyển sang 2
      <div> THẬT do JS chèn thay vì pseudo-element.
   2. <div> thật, "height: 100%" — THẤT BẠI: khung cha (.gantt-wrapper) dùng
      "max-height" theo nội dung (không phải height cố định), % height
      không resolve được, luôn ra 0px. Fix: JS gán biến CSS
      --scroll-hint-h = clientHeight thật (px).
   3. <div> "right" chèn ở CUỐI (sau nội dung) — THẤT BẠI: vị trí TĨNH theo
      chiều DỌC của nó (trước khi sticky áp dụng) nằm ở NGAY DƯỚI toàn bộ
      nội dung (vì width/height:0 không chiếm chỗ ngang nhưng vẫn tính theo
      luồng dọc bình thường) — "position: sticky; top: 0" chỉ hoạt động
      đúng cho trục NGANG mà chúng ta cần (cuộn ngang), còn vị trí DỌC vẫn
      giữ nguyên chỗ tự nhiên trong luồng (dưới đáy). Fix: chèn CẢ 2 div
      (left VÀ right) làm CON ĐẦU TIÊN, không phải 1 đầu 1 cuối. */
.scroll-hint-edge {
    position: sticky;
    top: 0;
    width: 0;
    height: 0;
    pointer-events: none;
    z-index: 2;
    overflow: visible;
}
.scroll-hint-edge-left { left: 0; }
.scroll-hint-edge-right { left: 100%; }
.scroll-hint-edge-inner {
    position: absolute;
    top: 0;
    width: 24px;
    height: var(--scroll-hint-h, 200px);
    opacity: 0;
    transition: opacity var(--transition-fast);
    display: block;
}
.scroll-hint-edge-left .scroll-hint-edge-inner {
    left: 0;
    background: linear-gradient(to right, rgba(0, 0, 0, 0.10), transparent);
}
.scroll-hint-edge-right .scroll-hint-edge-inner {
    right: 0;
    background: linear-gradient(to left, rgba(0, 0, 0, 0.10), transparent);
}
.has-scroll-left .scroll-hint-edge-left .scroll-hint-edge-inner { opacity: 1; }
.has-scroll-right .scroll-hint-edge-right .scroll-hint-edge-inner { opacity: 1; }
