/* Đ ơ ư ă ĩ ũ live in BOTH Google's `vietnamese` subset and `latin-ext`; Google declares latin-ext last, so
 * one "Đ" ("Phòng Đà Nẵng") pulled the 114 KB latin-ext file. Re-declared here, after Google's CSS, on the
 * vietnamese file the page already loads — same glyphs, no extra download. Same URLs as Google's CSS
 * (re-copy them if the font version in index.html changes). Test: ops/tests/test_font_vi_subset.py */
@font-face{ font-family:'EB Garamond'; font-style:normal; font-weight:500; font-display:swap; src:url(https://fonts.gstatic.com/s/ebgaramond/v33/SlGUmQSNjdsmc35JDF1K5GR6SDk_YAPIlWk.woff2) format('woff2'); unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0; }
@font-face{ font-family:'EB Garamond'; font-style:normal; font-weight:600; font-display:swap; src:url(https://fonts.gstatic.com/s/ebgaramond/v33/SlGUmQSNjdsmc35JDF1K5GR6SDk_YAPIlWk.woff2) format('woff2'); unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0; }
@font-face{ font-family:'EB Garamond'; font-style:normal; font-weight:700; font-display:swap; src:url(https://fonts.gstatic.com/s/ebgaramond/v33/SlGUmQSNjdsmc35JDF1K5GR6SDk_YAPIlWk.woff2) format('woff2'); unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0; }
@font-face{ font-family:'Be Vietnam Pro'; font-style:normal; font-weight:400; font-display:swap; src:url(https://fonts.gstatic.com/s/bevietnampro/v12/QdVPSTAyLFyeg_IDWvOJmVES_Hw4BXoYZ7AjSh8.woff2) format('woff2'); unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0; }
@font-face{ font-family:'Be Vietnam Pro'; font-style:normal; font-weight:500; font-display:swap; src:url(https://fonts.gstatic.com/s/bevietnampro/v12/QdVMSTAyLFyeg_IDWvOJmVES_HTEJm86Rb0JcBaoUUU.woff2) format('woff2'); unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0; }
@font-face{ font-family:'Be Vietnam Pro'; font-style:normal; font-weight:600; font-display:swap; src:url(https://fonts.gstatic.com/s/bevietnampro/v12/QdVMSTAyLFyeg_IDWvOJmVES_HToIW86Rb0JcBaoUUU.woff2) format('woff2'); unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0; }
@font-face{ font-family:'Be Vietnam Pro'; font-style:normal; font-weight:700; font-display:swap; src:url(https://fonts.gstatic.com/s/bevietnampro/v12/QdVMSTAyLFyeg_IDWvOJmVES_HSMIG86Rb0JcBaoUUU.woff2) format('woff2'); unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0; }
:root{
  --cream:#F6EFE2; --paper:#fffdf8; --ink:#2a241d; --muted:#6d6450;
  --cognac:#B5916A; --gold:#C6A368; --line:#e7dfce; --line2:#efe8d8;
  --shadow:0 1px 3px rgba(40,30,20,.10);
}
*{ box-sizing:border-box; } html,body{ margin:0; }
/* No grey tap flash (28/09/2026 frame capture: it faded over ~10 frames on the tab just tapped, after the page had
   already swapped). Buttons already show their own state instantly (.on / :active). */
html{ -webkit-tap-highlight-color:transparent; }

/* Tôn trọng công tắc "Giảm chuyển động" của hệ điều hành. KHÔNG phải chuyện thẩm mỹ: người
   bật nó thường vì chóng mặt hoặc đau đầu thật, và iOS/macOS đều có sẵn nút đó. Đo 18/09:
   app có 9 transition + 1 spinner quay VÔ HẠN mà không chỗ nào đọc thiết lập này.
   Dùng `.01ms` chứ không `0s`: sự kiện `transitionend`/`animationend` vẫn bắn, nên code nào
   đang chờ nó thì không treo. `.rf-btn.spin` mất vòng quay nhưng vẫn mờ đi + khoá bấm, tức
   vẫn nói được "đang tải" mà không xoay. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
    scroll-behavior:auto !important;
  }
}
[hidden]{ display:none !important; }
body{ font-family:"Be Vietnam Pro",system-ui,sans-serif; color:var(--ink); background:var(--cream); -webkit-text-size-adjust:100%; }
:focus-visible{ outline:2px solid var(--gold); outline-offset:2px; border-radius:6px; }

/* ---- header ---- */
/* Một khối dính duy nhất thay cho chuỗi .top→.modes: hai thanh cuộn rời nhau từng để hở khe. */
/* translateZ: own compositing layer. Khoa's iPhone recording 28/09/2026 (60 fps): Bảng → Lịch jumps the page from
   ~110.000px to 0 and Safari drew 2 frames WITHOUT the brand row + tabs while it re-rasterised the page around the
   header. A separate layer is kept across the jump — same fix as .tapehead's "khối trắng" (Kan 12/06). */
.hdr{ position:sticky; top:0; left:0; z-index:10; background:var(--paper); transform:translateZ(0); }
.top{ display:flex; align-items:center; gap:9px; padding:5px 16px; background:var(--paper); border-bottom:1px solid var(--line); }
.top .mono{ opacity:.92; height:22px; width:auto; }
.ttl{ display:flex; flex-direction:column; line-height:1.1; min-width:0; }
.top h1{ font-family:"EB Garamond",serif; font-size:16px; margin:0; font-weight:600; }
.summary .upd{ margin-left:auto; font-size:12px; color:var(--muted); white-space:nowrap; }
.add-btn{ border:0; background:var(--cognac); color:#fff; border-radius:18px; min-height:36px; padding:0 15px; font:inherit; font-weight:600; font-size:13.5px; cursor:pointer; box-shadow:0 2px 8px rgba(181,145,106,.35); white-space:nowrap; }
.add-btn:active{ transform:scale(.97); }

/* ---- mode tabs ---- */
/* Thêm nút thứ 5 (Trống) là tràn ngang trên iPhone 402pt: 5 nhãn + đệm ≈ 430px > 370px chỗ
   có. Cho VUỐT NGANG thay vì cắt cụt — cắt thì nút "Tài chính" biến mất mà không dấu hiệu
   gì, người dùng tưởng mất tính năng (đo 03/09/2026 trên ảnh 402pt). */
.modes{ display:flex; gap:6px; padding:4px 16px 4px; background:var(--paper); border-bottom:1px solid var(--line); overflow-x:auto; scrollbar-width:none; -webkit-overflow-scrolling:touch; }
.modes::-webkit-scrollbar{ display:none; }
@media (max-width:430px){ .modes button{ padding:0 13px; font-size:13.5px; } }
/* Thanh tab vuốt ngang được, nhưng trên điện thoại nó bị CẮT CỨNG ở mép phải: tab cuối
   chỉ ló hai chữ cái, không có dấu hiệu nào nói còn tab nữa. Người dùng phải tình cờ
   vuốt mới biết. Vệt mờ ở mép = dấu hiệu quen thuộc cho "còn nữa bên phải".
   Chỉ đặt ở khổ hẹp; desktop đã xếp một hàng, không cuộn. */
@media (max-width:759px){
  .modes{ position:relative;
    -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 26px),transparent 100%);
    mask-image:linear-gradient(90deg,#000 calc(100% - 26px),transparent 100%); }
}
.modes button{ flex:0 0 auto; border:1px solid var(--line); background:#fff; border-radius:22px; min-height:44px; padding:0 18px; font:inherit; font-size:14px; color:var(--ink); cursor:pointer; }
/* laptop widths: the tab row shares the title bar — 1024 cut "Ăn sáng" off and half of "Dọn phòng" (02/10/2026) */
@media(min-width:760px) and (max-width:1279px){ .modes{ gap:4px; } .modes button{ padding:0 11px; font-size:13.5px; } }
/* 8 manager tabs since "Kho đồ" (15/10/2026): at 1024 the strip was 53px short — tighter still below 1100 */
@media(min-width:760px) and (max-width:1099px){ .modes{ gap:3px; } .modes button{ padding:0 8px; } }
.modes button.on{ background:var(--cognac); color:#fff; border-color:var(--cognac); font-weight:600; }
/* no colour fade on tabs: the page swaps instantly, a fading highlight trailed behind it (27/09/2026) */
.modes button:hover:not(.on){ background:var(--cream); border-color:var(--cognac); }

/* today's summary strip — one compact line, mode-aware content */
.summary{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; padding:7px 16px 2px; background:var(--paper); }
.summary:empty{ display:none; }
.sum-lab{ font-size:12px; text-transform:uppercase; letter-spacing:.07em; color:var(--muted); margin-right:1px; }
.su{ display:inline-flex; align-items:baseline; gap:4px; background:var(--cream); border:1px solid var(--line); border-radius:16px; padding:4px 11px; font-size:12px; color:var(--muted); }
.su b{ font-size:14px; color:var(--ink); font-weight:700; }
.su small{ color:inherit; opacity:.75; font-size:12px; }
.su.in b{ color:#3a7ca5; } .su.out b{ color:#a8642a; }
.su.owe{ background:#fbecd0; border-color:#eccf9a; color:#8a6a2a; } .su.owe b{ color:#8a6a2a; }
.su.owe.zero{ background:#e3eddc; border-color:#cfe0c2; color:#5a8a4a; } .su.owe.zero b{ color:#5a8a4a; }
.su.od-pill{ background:#fbe4e0; border-color:#eebfb6; color:#b3493a; } .su.od-pill b{ color:#b3493a; }

/* ---- control bars ---- */
.ctrlbar{ display:flex; align-items:center; gap:6px; padding:7px 16px; flex-wrap:nowrap; }
.ctrlbar .nav{ flex:0 0 auto; width:44px; height:44px; border-radius:10px; border:1px solid var(--line); background:#fff; font-size:18px; cursor:pointer; color:var(--ink); }
/* min-width so the native dd/mm/yyyy value never clips its year on the busy Lịch toolbar */
.ctrlbar input[type=date], .ctrlbar input[type=month]{ flex:1 1 auto; min-width:118px; max-width:240px; font:inherit; font-size:14px; min-height:44px; padding:0 8px; border:1px solid var(--line); border-radius:9px; background:#fff; color:var(--ink); }
.today-btn{ flex:0 0 auto; border:1px solid var(--line); background:#fff; border-radius:10px; min-height:44px; padding:0 11px; font:inherit; font-size:13px; color:var(--ink); cursor:pointer; white-space:nowrap; }
.today-btn.on{ background:var(--cream); border-color:var(--cognac); color:var(--cognac); font-weight:600; cursor:default; }
/* narrow phones (≤374px): ease the date floor + button so the one-row toolbar still fits */
@media(max-width:374px){ .ctrlbar{ gap:5px; } .ctrlbar input[type=date], .ctrlbar input[type=month]{ min-width:100px; font-size:13px; } .today-btn{ padding:0 8px; } }

.loading{ text-align:center; color:var(--muted); padding:48px 16px; }

/* ---- legend ---- */
.legend{ display:flex; flex-wrap:wrap; gap:14px; align-items:center; padding:1px 16px 7px; font-size:12px; color:var(--muted); }
.legend span{ display:inline-flex; align-items:center; gap:6px; }
.legend i{ width:18px; height:12px; border-radius:3px; display:inline-block; }
.legend i.dot{ width:13px; height:13px; border-radius:50%; }
.legend.legend-col{ flex-direction:column; align-items:flex-start; gap:13px; font-size:14px; color:var(--ink); padding:4px 0 0; }
.legend.legend-col i{ width:20px; height:13px; } .legend.legend-col i.dot{ width:14px; height:14px; }
.lg-tip{ margin:16px 0 0; font-size:12.5px; color:var(--muted); line-height:1.4; }
.nav-q{ font-weight:700; color:var(--cognac); }

/* ---- tape chart: sticky date header + frozen room column + scrollable body ----
   .tapewrap is its OWN vertical scroller (fixed height set in app.js) so .tapehead can
   position:sticky → the date row stays visible while rooms scroll. Room column is a
   non-scrolling sibling (NOT position:sticky-left) so it can't vanish during iOS momentum
   scroll. The header's date strip (.tape-h) is JS-synced to the body's horizontal scroll.
   Row heights are responsive: BASE = mobile (compact, more rooms/screen); desktop restores
   bigger heights in the @media block. roomcol pane height MUST equal its grid counterpart. */
/* Mobile: Lịch TRÀN VIỀN (Kan 14/06: bề ngang hẹp, đừng chừa 2 khoảng trống) — desktop
   có override padding 28px ở media min-width:760 (Kan: desktop 2 lề như cũ ổn). */
.tapewrap{ overflow-y:auto; overflow-x:hidden; -webkit-overflow-scrolling:touch; padding:0 0 16px; }
/* translateZ: ép Safari tách layer riêng — trị artifact "khối trắng" repaint ở corner (Kan 2026-06-12) */
.tapehead{ position:sticky; top:0; z-index:6; display:flex; align-items:flex-start; background:var(--cream); box-shadow:0 3px 5px -3px rgba(40,30,20,.30); transform:translateZ(0); }
.rc-corner{ position:relative; z-index:1; }
.tape-h{ flex:1 1 auto; min-width:0; overflow:hidden; }
.tapebody{ display:flex; align-items:flex-start; }
/* (auto-hide chrome khi cuộn ĐÃ BỎ — Kan 2026-06-12: vuốt + ẩn không mượt) */
.roomcol{ flex:0 0 auto; background:var(--paper); border-right:1px solid var(--line); }
.rc-corner{ flex:0 0 auto; height:34px; background:var(--cream); border-bottom:1px solid var(--line); }
.rc-floor{ height:22px; display:flex; align-items:flex-end; padding:0 10px 3px; background:var(--cream); color:var(--muted); font-size:12px; font-weight:600; letter-spacing:.08em; text-transform:uppercase; border-bottom:1px solid var(--line2); white-space:nowrap; overflow:hidden; }
body{ --rowh:36px; }
.rc-room{ height:var(--rowh); display:flex; flex-direction:column; justify-content:center; padding:0 10px; border-bottom:1px solid var(--line2); overflow:hidden; }
.rc-room b{ font-size:14px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; line-height:1.15; }
.rc-room small{ color:var(--muted); font-weight:400; font-size:11.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; line-height:1.15; }
.tape{ flex:1 1 auto; min-width:0; overflow-x:auto; overflow-y:hidden; scrollbar-width:thin; }
.grid{ display:grid; width:100%; align-items:stretch; }
.gh{ height:34px; display:flex; flex-direction:column; justify-content:center; gap:0; text-align:center; border-bottom:1px solid var(--line); background:var(--cream); }
.gh .wd{ font-size:10.5px; color:var(--muted); line-height:1.1; }
.gh .dd{ font-size:12px; font-weight:500; line-height:1.1; }
.gh.today{ background:rgba(198,163,104,.22); box-shadow:inset 2px 0 0 var(--gold); }
.gh.today .wd, .gh.today .dd{ color:#9c7b34; font-weight:700; }
.gh.we .wd, .gh.we .dd{ color:#bf5b4b; }
/* Còn trống từng đêm (Kan 11/09/2026): tô NỀN ô ngày, không thêm dòng chữ — cột chỉ rộng
   40px trên điện thoại, và chiều cao vừa mới đòi lại được hơn 100px. Bấm vào ô để xem
   chi tiết từng hạng; desktop rê chuột cũng ra. `.gh.today` giữ nguyên ưu tiên vì mốc
   hôm nay quan trọng hơn cảnh báo kín phòng. */
.gh[title]{ cursor:pointer; }
/* Holidays and Tết (Khoa 14/10/2026): a red stripe on top + the label in place of the weekday — a stripe, not a
   background, so the sold-out / tight tints underneath still read. */
.gh.hol{ box-shadow:inset 0 3px 0 #a8322d; }
.gh.hol .wd{ color:#a8322d; font-weight:700; white-space:nowrap; font-size:9.5px; letter-spacing:-.3px; }
.gh.today.hol{ box-shadow:inset 2px 0 0 var(--gold), inset 0 3px 0 #a8322d; }
.dp-hol{ color:#a8322d; }
.gh-tight{ background:rgba(198,163,104,.20); }
.gh-full{ background:rgba(191,91,75,.20); }
.gh-full .dd{ color:#a33f2f; font-weight:700; }
.gh.today.gh-full, .gh.today.gh-tight{ background:rgba(198,163,104,.22); }
/* bảng nhỏ "còn trống đêm nay" trong popup */
.dp-sub{ margin:0 0 6px; font-size:12px; letter-spacing:.12em; text-transform:uppercase; color:var(--muted); }
.dp-t{ width:100%; border-collapse:collapse; margin:0 0 12px; }
.dp-t td{ padding:5px 0; border-bottom:1px solid var(--line2); font-size:14px; }
.dp-t td.n{ text-align:right; font-weight:600; font-variant-numeric:tabular-nums; }
.dp-t td.n.zero{ color:#a33f2f; }
.dp-note{ margin:0 0 10px; font-size:12.5px; color:var(--muted); line-height:1.5;
          border-left:2px solid var(--cognac); padding-left:9px; }
.dp-note b{ color:var(--ink); }
.dp-tot{ margin:0; font-size:14px; } .dp-tot b{ font-size:17px; }
.dp-cap{ color:var(--muted); font-size:12.5px; }
.floorspacer{ height:22px; background:var(--cream); border-bottom:1px solid var(--line2); }
.cell{ border-right:1px solid var(--line2); border-bottom:1px solid var(--line2); height:var(--rowh); }
.cell.today{ background:rgba(198,163,104,.10); box-shadow:inset 2px 0 0 var(--gold); }
.cell.we{ background:rgba(191,91,75,.022); }
/* Vùng bấm CHÍNH của cả trang: mở một đơn. Đo 11/09/2026 trên iPhone: thanh cao 20px
   nằm giữa hàng 36px — mỏng hơn nửa mức 44px khuyến nghị, mà 16px kia đang bỏ không.
   Đơn ở một đêm còn tệ hơn: 34×20px. Cao lên cho đầy hàng thì không tốn thêm một pixel
   chiều cao nào. Dùng line-height thay display:flex để GIỮ text-overflow:ellipsis —
   flex làm tên khách dài tràn ra thay vì cắt bằng dấu ba chấm. */
/* .bl = the guest name, sticky at the view's left edge; overflow:clip (not hidden) so the bar is no scroll container */
.bar .bl{ position:sticky; left:6px; display:inline-block; max-width:100%; overflow:hidden; text-overflow:ellipsis; vertical-align:top; }
.bar{ border-radius:8px; color:#fff; font-size:12px; min-height:calc(var(--rowh) - 6px); line-height:calc(var(--rowh) - 6px); padding:0 8px; margin:3px 3px; overflow:clip; white-space:nowrap; cursor:pointer; box-shadow:0 1px 5px rgba(0,0,0,.20); align-self:center; font-weight:500; z-index:1; }
.bar.inhouse{ box-shadow:inset 0 0 0 2px rgba(255,255,255,.9), 0 1px 6px rgba(0,0,0,.28); }
.bar.co{ opacity:.58; box-shadow:none; } /* đã check-out → mờ NHẸ (Kan 14/06: .42 đậm quá) — vẫn đọc được */
.legend i.co-dot{ border-radius:7px; background:#8a8270; opacity:.58; } /* swatch thanh đã trả phòng (mờ) */
/* Family: 1 bar phủ 2 lane. KHÔNG dùng flex để căn giữa — text-overflow:ellipsis không áp
   cho nội dung trong flex container, nên tên khách dài bị cắt phăng giữa chữ, không có "…"
   (đo 11/09: "OLDHAM/OO" trên thanh 74px cần 213px). Căn giữa bằng line-height = đúng
   chiều cao thanh: 2 hàng trừ lề 4px trên dưới. */
.bar.fam{ align-self:stretch; display:block; margin:4px 3px;
          line-height:calc(var(--rowh) * 2 - 8px); }
.legend i.ring{ width:18px; height:12px; border-radius:3px; background:#c8b89a; box-shadow:inset 0 0 0 2px #fff; }
/* derived stay-state badge */
.stay{ font-size:11.5px; padding:1px 7px; border-radius:10px; font-weight:600; vertical-align:middle; white-space:nowrap; }
.stay-now{ background:#e0ecdf; color:#2e5c47; border:1px solid #c4d8c2; }
.stay-soon{ background:#e9e0cd; color:#6b5e44; }
.stay-past{ background:#ece6da; color:#9a9384; }

/* ---- panes (day / table) ---- */
.pane{ max-width:760px; margin:0 auto; padding:6px 14px 24px; }
.pane.day{ padding-bottom:10px; } /* Kan: đuôi Theo ngày hở nhiều — tỉa đệm */

.pane.wide{ max-width:1300px; } /* +Loại phòng/Ăn sáng → rộng hơn (Kan 13/06) */
.floorhd{ font-size:12px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--cognac); margin:18px 4px 7px; padding-bottom:5px; border-bottom:1px solid var(--line); }
/* 1 dải stats liền — segment chia vạch, số 0 mờ (hết gãy 2 hàng pill) */
.daystat{ display:flex; width:fit-content; max-width:calc(100vw - 24px); margin:0 auto 12px; background:var(--paper); border:1px solid var(--line); border-radius:14px; padding:5px 4px; overflow-x:auto; }
.daystat .su{ background:none; border:0; border-radius:0; padding:2px 5px; white-space:nowrap; flex:none; }
.daystat:has(+ .daystat.cash){ margin-bottom:6px; }   /* rooms · money: two strips, one block (04/10/2026) */
/* Something still hidden to the right of a scrolling strip ⇒ fade that edge (class set by scrollHint()). */
.modes.more, .daystat.more{ -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 32px),transparent);
  mask-image:linear-gradient(to right,#000 calc(100% - 32px),transparent); }
/* …and the left edge once something is scrolled off it (02/10/2026: "Lịch" hid at -52px with no sign) */
.modes.less{ -webkit-mask-image:linear-gradient(to right,transparent,#000 32px); mask-image:linear-gradient(to right,transparent,#000 32px); }
.modes.less.more{ -webkit-mask-image:linear-gradient(to right,transparent,#000 32px,#000 calc(100% - 32px),transparent);
  mask-image:linear-gradient(to right,transparent,#000 32px,#000 calc(100% - 32px),transparent); }
.daystat .su + .su{ border-left:1px solid var(--line2); }
.daystat .su.z{ color:var(--muted); }
.daystat .su.z b{ color:var(--muted); font-weight:600; opacity:.8; }
.daystat .su.owe:not(.z){ color:#8a6a2a; } .daystat .su.owe:not(.z) b{ color:#8a6a2a; }
.daystat .su.od-pill, .daystat .su.od-pill b{ color:#bf5b4b; }
.su.free b{ color:#5a8a4a; }

/* ===== Báo cáo tài chính (manager-only) ===== */
.pane.report{ max-width:640px; }
.rep-hero{ background:var(--paper); border:1px solid var(--line); border-radius:16px; padding:15px 16px 14px; margin:6px 0 12px; }
.rep-lab{ font-size:12px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); }
.rep-big{ display:block; font-size:30px; line-height:1.1; font-weight:800; color:var(--cognac); margin:3px 0 2px; }
.rep-sub{ font-size:12px; color:var(--muted); }
.rep-shift{ font-size:12px; color:var(--muted); margin-top:4px; line-height:1.4; } /* money booked in another month than check-in (Khoa 28/09/2026) */
.rep-bar{ display:flex; height:7px; border-radius:4px; background:#e9e2d4; overflow:hidden; margin:11px 0 9px; }
.rep-bar > i{ display:block; height:100%; background:var(--cognac); }
.rep-bar > i.held{ background:var(--cognac); opacity:.38; }   /* an OTA holds it — not in the account yet */
/* month picker: our own Vietnamese label, the native input invisible on top so a tap still opens the phone's picker */
.rep-mwrap{ position:relative; display:inline-flex; align-items:center; min-height:44px; padding:0 12px; border:1px solid var(--line);
  border-radius:10px; background:#fff; font-size:15px; font-weight:600; color:var(--ink); }
.rep-mwrap #rep-month{ position:absolute; inset:0; width:100%; height:100%; opacity:0; border:0; }
.rep-split{ display:flex; gap:8px; }
.rep-split .rps{ flex:1; font-size:12px; color:var(--muted); background:var(--cream); border:1px solid var(--line); border-radius:10px; padding:7px 11px; }
.rep-split .rps b{ display:block; font-size:15px; margin-top:2px; color:var(--ink); }
.rep-split .rps.in b{ color:#3f7d54; } .rep-split .rps.owe b{ color:#8a6a2a; }
.rep-pl{ margin-top:8px; } .rep-split .rps.cost b{ color:#b3493a; }
.rep-split .rps.net b{ color:#3f7d54; } .rep-split .rps.net b.neg{ color:#b3493a; }
.rep-kpis{ display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-bottom:12px; }
.rep-kpis .kpi{ background:var(--paper); border:1px solid var(--line); border-radius:12px; padding:9px 12px; }
.rep-kpis .kpi b{ display:block; font-size:16px; color:var(--ink); line-height:1.2; }
.rep-kpis .kpi span{ font-size:12px; color:var(--muted); }
@media(min-width:560px){ .rep-kpis{ grid-template-columns:repeat(4,minmax(0,1fr)); } }
.rep-nodata{ background:#fdf3e3; border:1px solid #ecd9b0; color:#8a6a2a; border-radius:10px; padding:8px 12px; font-size:12px; margin:0 0 12px; }
.rep-src{ display:flex; flex-direction:column; gap:11px; margin-top:4px; }
.rsrc-top{ display:flex; align-items:center; gap:7px; font-size:13px; }
.rsrc-dot{ width:9px; height:9px; border-radius:50%; flex:0 0 auto; }
.rsrc-name{ font-weight:600; color:var(--ink); }
.rsrc-n{ color:var(--muted); font-size:12px; }
.rsrc-rev{ margin-left:auto; color:var(--ink); }
.rsrc-bar{ height:6px; border-radius:4px; background:#ece6da; overflow:hidden; margin-top:5px; }
.rsrc-bar > i{ display:block; height:100%; border-radius:4px; }
.rep-subrow{ display:flex; align-items:center; justify-content:space-between; gap:8px; }
.rep-delta{ font-size:12px; font-weight:700; padding:2px 8px; border-radius:11px; white-space:nowrap; }
.rep-delta.up{ color:#3f7d54; background:#e3eddc; } .rep-delta.dn{ color:#b3493a; background:#fbe4e0; }
.rep-split .rps small{ display:block; font-size:11.5px; font-weight:400; color:var(--muted); margin-top:1px; }
/* OTA đã thu nhưng chờ về tài khoản (Trip… giữ tới payout) — tách khỏi "đã về" */
.rep-held{ margin:8px 0 0; font-size:12px; color:#5d7a92; background:#eef2f5; border:1px solid #d8e0e6; border-radius:10px; padding:7px 11px; }
.rep-held b{ color:#3f5d72; }
/* Khoản OTA đã tới hạn rút (đơn đã trả phòng) — cần hành động, tô ấm để nổi khỏi
   phần chờ bình thường. Kan 29/07: Trip cho rút từ ngày check-out. */
.rep-held b.ready{ color:#a35a1f; }
.rep-trend{ display:flex; align-items:flex-end; gap:6px; height:96px; margin:2px 2px 4px; }
.rtr{ flex:1; display:flex; flex-direction:column; align-items:center; justify-content:flex-end; gap:3px; height:100%; min-width:0; }
.rtr-v{ font-size:10.5px; color:var(--muted); white-space:nowrap; line-height:1; }
.rtr-bar{ width:100%; max-width:30px; flex:1; display:flex; align-items:flex-end; background:#efe9dd; border-radius:4px 4px 0 0; overflow:hidden; }
.rtr-bar > i{ width:100%; background:#cdbfa3; border-radius:4px 4px 0 0; }
.rtr.cur .rtr-bar > i{ background:var(--cognac); }
.rtr.cur .rtr-v{ color:var(--cognac); font-weight:700; }
.rtr-m{ font-size:11.5px; color:var(--muted); }
.rep-inv{ display:flex; flex-direction:column; gap:10px; }
.invb{ background:var(--paper); border:1px solid var(--line); border-radius:13px; padding:12px 14px; }
.invb-h{ display:flex; align-items:baseline; gap:8px; }
.invb-h b{ font-size:14px; color:var(--ink); flex:1 1 auto; }
.invb-sub{ font-size:12px; color:var(--muted); white-space:nowrap; }
.invb-rev{ display:block; font-size:21px; font-weight:800; color:var(--cognac); line-height:1.25; margin:2px 0; }
.invb-pay{ font-size:12.5px; margin-top:1px; }
.invb-pay .in{ color:#3f7d54; } .invb-pay .owe{ color:#8a6a2a; } .invb-pay .held{ color:#5d7a92; }
.invb-pay.paid{ color:#3f7d54; } .invb-pay.owe{ color:#8a6a2a; }
.invb-pay.invb-empty{ color:var(--muted); opacity:.8; }
.invb-unassigned{ font-size:12px; color:#8a6a2a; background:#fbecd0; border:1px solid #eccf9a; border-radius:10px; padding:8px 12px; margin-top:2px; }
.invb-inh{ margin-top:7px; font-size:12px; color:#b3493a; background:#fbe4e0; border:1px solid #eebfb6; border-radius:8px; padding:5px 10px; }
.invb[data-inv-rooms]{ cursor:pointer; transition:border-color .15s, background-color .15s; }
.invb[data-inv-rooms]:hover{ border-color:var(--cognac); background:var(--cream); }
.invb[data-inv-rooms]:active{ transform:scale(.995); }
/* thanh công cụ Bảng: chip lọc nhanh + nút xuất — 1 hàng gọn, tự xuống dòng (mobile + desktop) */
.tbl-bar{ display:flex; align-items:center; gap:7px; flex-wrap:wrap; margin:2px 0 12px; }
.rfilter-tag{ display:inline-flex; align-items:center; gap:6px; padding:4px 6px 4px 11px; background:var(--cream); border:1px solid var(--cognac); border-radius:16px; font-size:12.5px; color:var(--ink); }
.rfilter-tag b{ color:var(--cognac); font-weight:600; }
.rfilter-x{ border:0; background:rgba(60,40,20,.10); border-radius:50%; width:23px; height:23px; font-size:12px; line-height:1; color:var(--ink); cursor:pointer; padding:0; flex:0 0 auto; }
.rfilter-x:hover{ background:rgba(60,40,20,.20); }
.rsrc[data-src]{ cursor:pointer; } .rsrc[data-src]:active{ opacity:.65; }
/* hành động trên thẻ doanh thu (gom vào card) + nút Báo cáo trên thẻ chủ đầu tư */
.rep-acts{ display:flex; gap:8px; justify-content:flex-end; margin-top:13px; }
.rep-act,.row-rep{ flex:0 0 auto; align-self:center; border:1px solid var(--cognac); background:#fff; color:var(--cognac); border-radius:22px; min-height:44px; padding:0 15px; font:inherit; font-size:12.5px; font-weight:600; cursor:pointer; white-space:nowrap; }
.rep-act:hover,.row-rep:hover{ background:var(--cream); }
.rep-act:active,.row-rep:active{ transform:scale(.97); }
.row-rep{ margin-left:8px; min-height:30px; padding:0 12px; font-size:12px; }

/* ===== Báo cáo in ra PDF (khớp mẫu booking-doc: EB Garamond + cognac #B5916A) ===== */
#report-print{ display:none; }
@media print{
  @page{ size:A4; margin:14mm 12mm 16mm;
    @bottom-right{ content:"Trang " counter(page) " / " counter(pages); font:9pt 'Be Vietnam Pro',sans-serif; color:#7a7167; }
    @bottom-left{ content:"Maison D'Rose"; font:italic 9.5pt 'EB Garamond',serif; color:#B5916A; }
  }
  html,body{ background:#fff !important; }
  body > *:not(#report-print){ display:none !important; }
  #report-print{ display:block; }
}
/* A4 thật, chảy tự nhiên qua trang (KHÔNG khoá 1 trang). Lề do @page lo nên .pr-page padding:0. */
.pr-page{ box-sizing:border-box; position:relative; width:auto; padding:0; background:#fff; color:#2b2622; font-family:'Be Vietnam Pro',sans-serif; font-size:12pt; line-height:1.5; -webkit-print-color-adjust:exact; print-color-adjust:exact; }
.pr-wm{ position:fixed; left:50%; top:50%; transform:translate(-50%,-50%); width:116mm; opacity:.05; filter:grayscale(1); pointer-events:none; z-index:0; }   /* fixed → lặp watermark mỗi trang */
.pr-page > *:not(.pr-wm){ position:relative; z-index:1; }
/* chống vỡ khối khi sang trang + danh sách booking sang TRANG 2 */
.pr-sum, .pr-kpis, .pr-inh, .pr-src, .pr-closing, .pr-rep-hd2, .pr-pl, .pr-panel, .pr-fn{ break-inside:avoid; }
.pr-sec{ break-after:avoid; }
.pr-tbl thead{ display:table-header-group; }
.pr-break{ break-before:page; }
.pr-rep-hd2{ display:none; }
.pr-break .pr-rep-hd2{ display:flex; justify-content:space-between; align-items:baseline; padding-bottom:9px; margin-bottom:9px; border-bottom:1px solid #e3d8c6; }
.pr-hd2-brand{ font-family:'EB Garamond',serif; font-size:14pt; color:#2b2622; }
.pr-hd2-meta{ font-size:9.5pt; letter-spacing:1.2px; text-transform:uppercase; color:#7a7167; }
.pr-hd{ display:flex; justify-content:space-between; align-items:flex-start; }
.pr-hl{ display:flex; align-items:center; gap:13px; }
.pr-mono{ width:54px; height:auto; }
.pr-brand{ font-family:'EB Garamond',serif; font-size:24pt; font-weight:600; letter-spacing:.4px; line-height:1; color:#2b2622; }
.pr-tag{ font-family:'EB Garamond',serif; font-style:italic; font-size:11pt; color:#7a7167; margin-top:4px; }
.pr-hr{ text-align:right; }
.pr-eyebrow{ font-size:10pt; letter-spacing:3px; color:#7a7167; }
.pr-title{ font-family:'EB Garamond',serif; font-weight:600; font-size:19pt; color:#B5916A; margin-top:3px; line-height:1.1; letter-spacing:.4px; }
.pr-month{ font-size:11pt; color:#7a7167; margin-top:3px; }
.pr-rule{ height:0; border:0; border-top:2px solid #B5916A; position:relative; margin:12px 0 0; }
.pr-rule::after{ content:""; position:absolute; left:0; right:0; top:3px; border-top:.6px solid #B5916A; }
.pr-sum{ display:flex; justify-content:space-between; align-items:flex-end; gap:18px; border-bottom:1px solid #cdb189; padding:13px 2px 14px; margin-top:12px; }
.pr-lab{ font-size:10.5pt; letter-spacing:2px; color:#7a7167; text-transform:uppercase; }
.pr-big{ font-family:'EB Garamond',serif; font-size:37pt; font-weight:600; letter-spacing:-.5px; color:#B5916A; line-height:1; font-variant-numeric:tabular-nums; margin-top:3px; }
.pr-sub2{ font-size:11pt; color:#7a7167; margin-top:5px; }
.pr-sub3{ font-size:9.5pt; color:#7a7167; margin-top:5px; max-width:270pt; line-height:1.45; }
.pr-sub3 b{ color:#4a4238; font-weight:600; }
.pr-sum-r{ display:flex; align-items:center; gap:15px; }
.pr-donut-num{ font:700 12px 'EB Garamond',serif; fill:#9c7b3f; }
.pr-split{ text-align:right; font-size:12pt; white-space:nowrap; line-height:1.7; }
.pr-split .in{ color:#B5916A; } .pr-split .owe{ color:#C4B79E; } .pr-split .held{ color:#5d7a92; }
.pr-split .lg{ color:#5f584c; }
.pr-split small{ color:#7a7167; }
.pr-inh{ margin-top:11px; background:#f7efe2; border:1px solid #e3d2ba; border-radius:8px; padding:9px 14px; color:#8a6a2a; font-size:11.5pt; }
/* Ghi chú giải thích cách tính (đơn vắt tháng) — thông tin TRUNG TÍNH, không phải
   cảnh báo như .pr-inh, nên dùng xám nhạt để không tranh chú ý với số liệu. */
.pr-note{ margin-top:11px; background:#faf8f4; border-left:3px solid #d8cbb6;
  border-radius:0 8px 8px 0; padding:8px 14px; color:#6b6154; font-size:10.5pt; line-height:1.5; }
.pr-note b{ color:#4a4238; }
.pr-kpis{ display:flex; margin:13px 0 2px; border-top:1px solid #e3d8c6; border-bottom:1px solid #e3d8c6; }
.pr-kpi{ flex:1; text-align:center; padding:12px 6px; }
.pr-kpi + .pr-kpi{ border-left:1px solid #efe7d8; }
.pr-kpi b{ display:block; font-family:'EB Garamond',serif; font-size:22pt; font-weight:500; color:#2b2622; font-variant-numeric:tabular-nums; }
.pr-kpi span{ display:block; margin-top:3px; font-size:10.5pt; letter-spacing:1px; text-transform:uppercase; color:#7a7167; }
.pr-kd{ display:block; font-style:normal; font-size:8.5pt; margin-top:3px; font-variant-numeric:tabular-nums; }
.pr-kd.up{ color:#3f7d54; } .pr-kd.dn{ color:#b3493a; }
.pr-pl{ display:flex; gap:24px; margin:13px 0 2px; padding:10px 2px; border-top:1px solid #e3d8c6; border-bottom:1px solid #e3d8c6; }
.pr-pl > div{ flex:1; }
.pr-pl-lab{ display:block; font-size:9.5pt; letter-spacing:1px; text-transform:uppercase; color:#7a7167; }
/* Trung tính, KHÔNG xanh: ô 'Chi phí đã ghi sổ' dùng chung class này, in xanh lá thành ra
   32 triệu tiền RA trông như tin tốt. Chỉ dòng 'Còn lại' mới mang màu (xanh/đỏ). */
.pr-pl b{ font-family:'EB Garamond',serif; font-size:15pt; color:#2b2622; }
.pr-pl b.pos{ color:#3f7d54; }
.pr-pl b.neg{ color:#b3493a; }
.pr-sec{ display:flex; align-items:center; gap:11px; margin:16px 0 8px; font-size:11pt; letter-spacing:2.5px; text-transform:uppercase; color:#7a7167; }
.pr-sec::after{ content:""; flex:1; height:1px; background:#e3d8c6; }
.pr-sec-no{ font-family:'EB Garamond',serif; font-style:italic; font-size:13pt; color:#B5916A; letter-spacing:.5px; }
.pr-srcs{ display:flex; flex-direction:column; gap:5px; }
.pr-src{ display:grid; grid-template-columns:110px 1fr 185px; align-items:center; gap:12px; padding:4px 0; font-size:11.5pt; }
.pr-src-n{ color:#2b2622; }
.pr-src-bar{ height:11px; background:#f0e8da; border-radius:6px; overflow:hidden; }
.pr-src-bar i{ display:block; height:100%; border-radius:6px; }
.pr-src-v{ text-align:right; color:#7a7167; font-variant-numeric:tabular-nums; white-space:nowrap; }
.pr-panels{ display:grid; grid-template-columns:1fr; gap:26px; }
.pr-panels.two{ grid-template-columns:1fr 1fr; }
.pr-panel-h{ display:flex; justify-content:space-between; align-items:baseline; gap:10px;
  border-bottom:1px solid #e3d8c6; padding-bottom:4px; margin-bottom:8px; }
.pr-panel-t{ font-size:9.5pt; letter-spacing:1.6px; text-transform:uppercase; color:#7a7167; }
.pr-panel-s{ font-family:'EB Garamond',serif; font-size:13pt; color:#2b2622; font-variant-numeric:tabular-nums; }
.pr-panel-n{ margin-top:7px; font-size:9pt; font-style:italic; color:#7a7167; }
/* Cột hẹp: tên + số nằm CÙNG dòng, thanh tỉ lệ tụt xuống dòng dưới trải hết bề ngang —
   giữ nguyên lưới 3 ô như bản 1 cột thì 110px+185px đã vượt bề rộng nửa trang. */
.pr-panels.two .pr-srcs{ gap:7px; }
.pr-panels.two .pr-src{ grid-template-columns:1fr auto; gap:3px 10px; padding:2px 0; font-size:10.5pt; }
.pr-panels.two .pr-src-n{ grid-column:1; grid-row:1; }
.pr-panels.two .pr-src-v{ grid-column:2; grid-row:1; font-size:9.5pt; }
.pr-panels.two .pr-src-bar{ grid-column:1/-1; grid-row:2; height:7px; }
.pr-spk{ display:block; width:100%; height:auto; margin:4px 0 6px; }
.pr-spk-v{ font:600 15px 'EB Garamond',serif; fill:#8a8072; }
.pr-spk-v.cur{ font-weight:700; fill:#B5916A; }
.pr-spk-v.z{ fill:#b8afa0; }
.pr-spk-m{ font:500 13px 'Be Vietnam Pro',sans-serif; fill:#7a7167; letter-spacing:.3px; }
.pr-trend-note{ font-family:'EB Garamond',serif; font-style:italic; font-size:11.5pt; color:#7a7167; padding:3px 0 1px; }
/* 10pt cho MỌI bảng báo cáo (Kan 01/09/2026). Trước đây bảng ngắn giữ 11pt, chỉ bảng dài
   (>7 đơn, class --zebra) mới hạ 10pt — nên bản chủ đầu tư "904+1104+1004" (6 đơn) bị bóp cột
   KHÁCH, 5 tên bẻ 2–3 dòng ("Nông / Quang / Hành"), còn bản "Khối phòng chính" (14 đơn) thì
   gọn. Hai bản này gửi HAI chủ đầu tư khác nhau về CÙNG một toà nhà nên phải nhìn giống nhau;
   nhất quán quan trọng hơn việc bảng ngắn được chữ to hơn vài phần trăm. */
.pr-tbl{ width:100%; border-collapse:collapse; font-size:10pt; }
.pr-tbl th{ background:none; border-top:1px solid #e3d8c6; border-bottom:1.2px solid #B5916A; color:#7a7167; font-weight:600; text-align:left; font-size:10pt; letter-spacing:.6px; text-transform:uppercase; padding:7px 8px; }
.pr-tbl td{ padding:7px 8px; border-top:1px solid #f0e8da; white-space:nowrap; }
/* Tên khách OTA nối bằng '/' và ',' KHÔNG có dấu cách ("JOO/YEONCHAN,KIM/JUNWOO") là MỘT
   từ với trình duyệt → white-space:normal không xuống dòng được, mà cột đã khoá cứng thì
   không nở ra, nên nó TRÀN ĐÈ sang cột Nguồn (đo 01/09: lấn 40pt vào chữ "Trip").
   overflow-wrap:anywhere cho phép bẻ giữa từ khi không còn chỗ. */
.pr-tbl td:nth-child(5){ white-space:normal; overflow-wrap:anywhere; }
/* Chiều rộng cột CỐ ĐỊNH (Kan 01/09/2026). Trước dùng table-layout auto nên bảng tự co theo
   nội dung: bản 6 đơn và bản 14 đơn của CÙNG một toà nhà ra hai bố cục khác nhau, và hạ cỡ
   chữ 11→10pt chỉ dời chỗ chật (5 tên khách hết bẻ 2 → nhưng tiêu đề "THANH TOÁN" lại bẻ).
   Khoá cột thì mọi báo cáo giống hệt nhau, không phụ thuộc tháng đó có mấy đơn. Tổng = 100%. */
.pr-tbl{ table-layout:fixed; }
.pr-tbl th:nth-child(1), .pr-tbl td:nth-child(1){ width:13%; }
.pr-tbl th:nth-child(2), .pr-tbl td:nth-child(2){ width:13%; }
.pr-tbl th:nth-child(3), .pr-tbl td:nth-child(3){ width:8%; }   /* 5% quá hẹp cho tiêu đề ĐÊM-P → đè cột PHÒNG */
.pr-tbl th:nth-child(4), .pr-tbl td:nth-child(4){ width:10%; }
.pr-tbl th:nth-child(5), .pr-tbl td:nth-child(5){ width:21%; }
.pr-tbl th:nth-child(6), .pr-tbl td:nth-child(6){ width:9%; }
.pr-tbl th:nth-child(7), .pr-tbl td:nth-child(7){ width:10%; }
.pr-tbl th:nth-child(8), .pr-tbl td:nth-child(8){ width:16%; }  /* 14% làm tiêu đề THANH TOÁN bẻ đôi */
.pr-tbl tr{ break-inside:avoid; }
.pr-tbl .num{ text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }
.pr-tbl td.cur{ color:#B5916A; font-weight:700; }
.pr-tbl--zebra{ font-size:10pt; }
.pr-tbl--zebra th{ font-size:9.5pt; padding:4px 8px; }
.pr-tbl--zebra td{ padding:4px 8px; }
.pr-tbl--zebra tr:nth-child(even) td{ background:#faf7f1; }
.pr-tbl tfoot td{ border-top:1.4px solid #B5916A; border-bottom:0; padding:8px; font-weight:600; color:#2b2622; background:none !important; }
.pr-tbl tfoot tr{ break-inside:avoid; }
/* Kan 01/09/2026 — CSS paged media LẶP thead+tfoot trên MỌI trang khi bảng bị chẻ. Bản
   "904+1104+1004" tháng 8 vì thế in `Tổng · 6 đơn · 35,54 tr` ngay dưới 2 dòng đơn ở
   trang 1 rồi in lại ở trang 2 → chủ đầu tư đọc trang 1 tưởng mình cộng sai. Hàng tổng
   phải xuất hiện ĐÚNG MỘT LẦN, ở cuối bảng. (thead vẫn nên lặp — đó là hành vi đúng.) */
.pr-tbl tfoot{ display:table-row-group; }
.pr-fn{ margin:13px 0 0; padding-left:17px; font-size:9.5pt; color:#7a7167; line-height:1.5; }
.pr-fn li{ margin-top:3px; }
.pr-fn b{ color:#4a4238; font-weight:600; }
.pr-conf{ color:#a89f93; }
.pr-closing{ font-family:'EB Garamond',serif; font-style:italic; font-size:12pt; color:#B5916A; text-align:center; letter-spacing:.3px; margin:11px 0 0; }
.pr-foot{ margin-top:11px; padding-top:8px; border-top:1px solid #e3d8c6; font-size:10pt; color:#7a7167; display:flex; justify-content:space-between; gap:16px; break-inside:avoid; }
.pr-foot-r{ text-align:right; white-space:nowrap; }

/* ── TRỐNG: phòng còn trống theo ngày và theo LOẠI ───────────────────────────
   Hai bố cục KHÁC HẲN nhau chứ không phải một bố cục co giãn (Kan 03/09/2026):
   · điện thoại — DỌC, mỗi ngày một dòng, loại phòng là chip;
   · desktop    — NGANG, ngày là cột, loại phòng là hàng; quét ngang thấy ngay loại nào
     hết trước, đó mới là quyết định đóng/mở bán trên OTA.
   Bỏ kiểu gom-khoảng-ngày cũ: đọc nhanh nhưng phải tự dịch ra "vậy hôm 27 còn mấy phòng". */
.av-head{ background:var(--paper); border:1px solid var(--line); border-radius:10px;
  padding:11px 13px; margin-bottom:11px; box-shadow:var(--shadow); }
.av-cap{ color:var(--muted); font-size:13px; }
.av-cap b{ color:var(--ink); font-size:15px; }
.av-ok{ margin-top:4px; color:var(--muted); font-size:13px; }
.av-ok b{ color:var(--cognac); }
.av-warn{ margin-top:4px; color:#b3493a; font-weight:600; font-size:13px; }
.av-note{ margin-top:6px; padding-top:6px; border-top:1px dashed var(--line);
  color:var(--muted); font-size:11.5px; font-style:italic; }
.av-lab{ color:var(--muted); font-size:13px; margin-right:2px; }
.av-range{ font:inherit; font-size:13px; padding:5px 11px; border-radius:8px;
  border:1px solid var(--line); background:var(--paper); color:var(--muted); cursor:pointer; }
.av-range.on{ background:var(--cognac); border-color:var(--cognac); color:#fff; font-weight:600; }

/* điện thoại — dọc */
.av-list{ display:flex; flex-direction:column; gap:4px; }
.av-mon{ margin:12px 0 3px; font-size:12px; letter-spacing:.08em; text-transform:uppercase;
  color:var(--muted); font-weight:700; }
.av-mon:first-child{ margin-top:0; }
.av-row{ display:grid; grid-template-columns:auto 1fr auto; align-items:center; gap:8px;
  background:var(--paper); border:1px solid var(--line); border-radius:9px;
  padding:7px 10px; cursor:pointer; }
.av-row:active{ background:var(--cream); }
.av-when{ font-variant-numeric:tabular-nums; font-size:13px; white-space:nowrap; }
.av-when b{ color:var(--ink); font-weight:600; }
.av-when i{ color:var(--muted); font-style:normal; font-size:12px; margin-left:2px; }
.av-chips{ display:flex; gap:4px; flex-wrap:wrap; }
.av-chip{ background:var(--cream); border:1px solid var(--line); border-radius:6px;
  padding:2px 6px; font-size:12px; color:var(--muted); white-space:nowrap; }
.av-chip b{ color:var(--ink); font-variant-numeric:tabular-nums; font-size:12.5px; }
.av-chip.xlow{ background:#fdf3e3; border-color:#e6cf9f; color:#8a6a2a; }
.av-chip.xlow b{ color:#8a6a2a; }
.av-chip.x0{ background:#fdecea; border-color:#e0b4ad; color:#b3493a; }
.av-chip.x0 b{ color:#b3493a; }
.av-n{ font-variant-numeric:tabular-nums; font-weight:700; color:var(--cognac);
  font-size:14px; white-space:nowrap; min-width:46px; text-align:right; }
.av-n small{ font-weight:400; color:var(--muted); font-size:12px; }
.av-row.full{ border-color:#e0b4ad; background:#fdf5f3; }
.av-row.full .av-n{ color:#b3493a; }

/* Lưới ngang cần CẢ bề ngang màn hình — .pane bó 760px làm bảng nằm giữa với hơn 400px
   trống mỗi bên trên màn 1280 (đo 03/09/2026), tức mất gần một nửa số ngày nhìn được một
   lần. Điện thoại không đụng tới vì màn vốn đã hẹp hơn 760. */
@media (min-width:760px){ .pane.av{ max-width:none; } }

/* desktop — ngang, cột đầu đóng băng để kéo ngang không mất tên loại phòng.
   Dải tháng + tô cuối tuần + đánh dấu hôm nay: kéo ngang 90 cột mà không có mốc nào thì
   lạc chỗ ngay (Kan 03/09: "desktop còn nhiều không gian"). */
.avg-wrap{ overflow-x:auto; border:1px solid var(--line); border-radius:10px;
  background:var(--paper); box-shadow:var(--shadow); }
.avg{ border-collapse:separate; border-spacing:0; font-variant-numeric:tabular-nums; }
.avg th, .avg td{ border-bottom:1px solid var(--line2); }
.avg-corner, .avg-lab{ position:sticky; left:0; z-index:2; background:var(--paper);
  text-align:left; padding:7px 12px; font-size:12.5px; color:var(--muted);
  font-weight:600; white-space:nowrap; border-right:1px solid var(--line); }
.avg-day{ padding:5px 0 6px; min-width:44px; cursor:pointer; font-weight:400;
  border-right:1px solid var(--line2); }
.avg-day i{ display:block; font-style:normal; font-size:11px; color:var(--muted); }
.avg-day b{ display:block; font-size:12px; color:var(--ink); font-weight:600; }
.avg-day:hover{ background:var(--cream); }
.avg-day.full b{ color:#b3493a; }
.avg-c{ text-align:center; font-size:13px; color:var(--ink); padding:6px 0;
  border-right:1px solid var(--line2); }
.avg-c.xlow{ background:#fdf3e3; color:#8a6a2a; font-weight:600; }
.avg-c.x0{ background:#fdecea; color:#b3493a; font-weight:700; }
.avg-tot .avg-c{ font-weight:700; color:var(--cognac); }
.avg-tot .avg-lab, .avg-tot .avg-c{ border-bottom:1px solid var(--line); }
.avg-months th{ padding:4px 0 3px; font-size:12px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--muted); font-weight:700; border-bottom:1px solid var(--line);
  border-right:1px solid var(--line); background:var(--cream); }
.avg-months .avg-corner{ background:var(--cream); }
.avg-c.we, .avg-day.we{ background:#faf6ec; }
.avg-c.we.xlow, .avg-day.we.xlow{ background:#fdf3e3; }
.avg-c.we.x0, .avg-day.we.x0{ background:#fdecea; }
.avg-day.now b{ color:#fff; background:var(--cognac); border-radius:5px; padding:1px 4px; }
.avg-c.now{ box-shadow:inset 2px 0 0 var(--cognac), inset -2px 0 0 var(--cognac); }
.avg-pct .avg-c{ font-size:12px; color:var(--muted); }
.avg-pct .avg-lab{ color:var(--muted); }
.avg tbody tr:hover .avg-c{ background:var(--cream); }
.avg tbody tr:hover .avg-c.x0{ background:#fdecea; }

/* Theo ngày: occupied-only filter chip */
.dfilter{ display:block; width:fit-content; margin:0 auto 14px; min-height:40px; padding:0 16px; border:1px solid var(--line); background:#fff; border-radius:20px; font:inherit; font-size:13px; color:var(--ink); cursor:pointer; }
.dfilter.on{ background:var(--cognac); color:#fff; border-color:var(--cognac); font-weight:600; }
.droomwrap{ display:grid; grid-template-columns:1fr; gap:8px; }
.droom{ display:flex; gap:11px; align-items:center; padding:12px; background:var(--paper); border:1px solid var(--line); border-radius:12px; box-shadow:var(--shadow); }
.droom .chip{ width:11px; height:11px; border-radius:50%; flex:0 0 auto; }
/* phân cấp chữ (Kan 2026-06-12): SỐ PHÒNG + TÊN to rõ để quét nhanh, chữ phụ giữ nhỏ */
.droom .rm{ font-weight:700; font-size:16px; min-width:58px; } .droom .rm small{ display:block; color:var(--muted); font-weight:400; font-size:11.5px; }
.droom .who{ flex:1; font-size:15px; font-weight:600; } .droom .who small{ color:var(--muted); font-weight:400; font-size:12px; }
/* Day cards: note + its chip at 11px — .76em of the 12px line gave 9px, the chip 8,4px (26/09/2026). */
.droom .booknote, .droom .nt-more{ font-size:12px; }
.droom.free .who{ font-weight:400; }
.droom.free{ background:transparent; box-shadow:none; border-style:dashed; }
.droom.free .who{ color:#b3a892; }
.droom.free .chip{ background:transparent !important; box-shadow:inset 0 0 0 2px #d8cfbe; }

/* ---- table (desktop) ---- */
.tbl{ width:100%; border-collapse:separate; border-spacing:0; font-size:13.5px; background:var(--paper); border-radius:12px; box-shadow:var(--shadow); } /* separate: Safari sticky thead bug với collapse (header bleed) */
.tbl th{ text-align:left; font-size:12px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); padding:10px 12px; border-bottom:2px solid var(--line); background:var(--cream); white-space:nowrap; }
.tbl th[data-sort]{ cursor:pointer; user-select:none; }
.tbl th[data-sort]:hover{ color:var(--cognac); }
/* padding dọc 9→6px (Khoa 20/09: "khoảng trống 1 dòng trên dưới cũng nhiều"). Hàng không
   ghi chú xuống 37→31px, nằm giữa dải "dày" (28–32) và "vừa" (36–40) của chuẩn bảng dữ liệu
   — đúng nhóm người dùng ở lì trong bảng cả ngày. Ngang giữ 12px: siết thêm thì cột Khách
   vốn đã phải cắt tên sẽ còn hẹp hơn. */
.tbl td{ padding:6px 12px; border-bottom:1px solid var(--line2); white-space:nowrap; }
/* Ghi chú trong BẢNG: đúng MỘT dòng, cắt bằng … — KHÁC card mobile (2 dòng), nên luật này
   phải nằm trong `.tbl`.
   Đo 20/09 trước khi sửa: hàng có BA chiều cao 37 / 67 / 93px. Thủ phạm không phải padding
   mà là khối ghi chú nằm trong cột Khách rộng 199px: ở 10,26px nó xuống 2–4 dòng. Bản vá
   `shortNote` cùng ngày cắt còn 90 ký tự — vừa 2 dòng trên card 402px, nhưng vẫn 4 dòng
   trong cột hẹp này. Carbon Design System: chỉ dùng hàng cao khi dữ liệu LUÔN 2 dòng; trộn
   1 với 4 trong cùng bảng chính là thứ gây cảm giác lộn xộn.
   Đã cân nhắc và LOẠI: dành sẵn một dòng trống cho mọi hàng (cao đều tuyệt đối) — trên sổ
   thật chỉ 24% đơn có ghi chú, nên nó thêm đúng cái khoảng trống Khoa vừa phàn nàn và làm
   bảng dài hơn 26%. Và bỏ hẳn ghi chú thành dấu 📝 — mất chữ mà chỉ ngắn thêm 2%. */
.tbl .booknote{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:100%;
  margin-top:1px; }
/* chip "+N mục" ẩn trong bảng: cột Khách chỉ 199px, thêm chip là ăn mất chữ. Dấu … của
   text-overflow đã báo còn nữa, và `title` + popup vẫn cho đọc đủ. Card mobile giữ chip. */
.tbl .nt-more{ display:none; }
.tbl tr{ cursor:pointer; }
.tbl tbody tr.alt{ background:rgba(231,223,206,.32); } /* sọc reset mỗi tháng (JS gắn .alt) — header chen vào ko lệch */
.tbl tbody tr:hover{ background:var(--cream); }
.tbl .past td{ opacity:.5; }
.tbl .gname{ font-weight:600; }
/* cột Ăn sáng (✓/✗) Bảng desktop — canh giữa, icon màu */
.tbl th.bf-th, .tbl td.bf-c{ text-align:center; }
.bf-y{ color:#3E6F59; font-weight:700; } /* có ăn sáng */
.bf-n{ color:#b3493a; font-weight:600; } /* không */
/* sọc DỌC nhẹ giữa cột (Kan 13/06) + cột Loại phòng + select xếp phòng tại chỗ */
.tbl td, .tbl th{ border-right:1px solid var(--line2); }
.tbl td:first-child, .tbl th:first-child{ padding-left:18px; }   /* gutter trái */
.tbl td:last-child, .tbl th:last-child{ border-right:0; padding-right:18px; } /* gutter phải đối xứng khi cuộn cột (Kan 13/06) */
.tbl .rtype{ color:var(--muted); }
/* Ghi chú người (đã lọc mã máy) — DÙNG CHUNG ô Khách Bảng desktop + card mobile + popup
   (Kan 14/06: đồng bộ desktop & mobile). Chỉ hiện khi có chữ thật → đa số đơn không có. */
.booknote{ display:block; margin-top:3px; font-size:.76em; font-weight:400; line-height:1.3;
  color:#7c7058; white-space:normal; }
.booknote::before{ content:"📝 "; opacity:.6; }
/* Chip "+N mục" — báo ghi chú còn phần bị giấu. KHÔNG phải nút: bấm bất cứ đâu trên
   card/dòng đều mở popup có bản đầy đủ, nên chip chỉ cần nhìn thấy được, đừng mời bấm
   riêng (một đích chạm 20px trong dòng chữ 11px là bẫy ngón tay trên máy cầm tay). */
.nt-more{ font-style:normal; margin-left:5px; padding:0 5px; border-radius:7px;
  background:rgba(124,112,88,.13); color:#6b6150; font-size:.92em; white-space:nowrap; }
/* Canh GIỮA theo chiều dọc (Khoa 20/09: "nên cho cái tên canh giữa"). Từ khi đơn
   nhiều phòng làm hàng cao 2–3 ô, tên dính mép trên trông lệch hẳn so với cụm ô
   phòng bên cạnh. Tên + ghi chú căn giữa cả khối. */
.tbl td.gname{ vertical-align:middle; }
/* Cột Khách ĐÒI chỗ, không để thuật toán bảng chia đều: bỏ cột Cọc rỗng giải phóng ~50px
   nhưng nó rải cho 14 cột khác, tên chỉ nhích 200→208. Ghim sàn thì phần dôi về đúng chỗ
   cần. Đo sổ 20/09: p50 tên 21 ký tự, p90 43, dài nhất 74; ở 200px 44% tên bị cắt, ở
   248px còn ~27%. Cao hơn nữa là bảng vượt khung 1440 và cuộn ngang quay lại. */
.tbl th[data-col="guest"], .tbl td.gname{ min-width:240px; }
/* SELECT inline trong ô Bảng (Phòng/Nguồn/Thanh toán) — trông như chữ, 1 chạm ra droplist
   (Kan 14/06: hết 2 lớp). ▾ nhỏ báo hiệu chọn được; hover/focus mới nổi viền. */
.tbl .cell-sel{ appearance:none; -webkit-appearance:none; border:0; background-color:transparent; color:inherit;
  font:inherit; width:100%; max-width:100%; margin:-3px 0; padding:3px 16px 3px 5px; border-radius:6px; cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='6'%3E%3Cpath d='M0 0l4 5 4-5z' fill='%23b0a890'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 5px center; }
.tbl .cell-sel:hover{ background-color:#0000000a; }
.tbl .cell-sel:focus{ outline:2px solid var(--cognac); outline-offset:-1px; background-color:#fff; }
.tbl .cell-sel.unas{ color:#b3493a; font-weight:500; }
.tbl .cell-sel.pillsel{ width:auto; max-width:100%; font-weight:600; font-size:12px; border-radius:20px; padding:3px 20px 3px 11px; background-position:right 8px center; }
.tbl .cell-sel.bfsel{ width:auto; max-width:100%; font-weight:700; padding:3px 17px 3px 5px; }
/* Ô SỬA-TẠI-CHỖ ở Bảng: nội dung vẽ như ô KHOÁ, <select> phủ TRONG SUỐT lên toàn ô.
   Khoa 20/09 gửi ảnh: "✗ Kh ▾", "☕ C ▾", "Trip.co ▾", "Bookin ▾", "Chưa than ▾" — ba cột
   Ăn sáng / Nguồn / Thanh toán cụt chữ ở MỌI đơn tương lai, vì <select> nhìn thấy được có
   hộp riêng + chỗ chừa cho mũi tên nên hẹp hơn ô khoá 21–31px. Đo: 244/321 ô bị cắt. Bỏ
   riêng mũi tên KHÔNG đủ (`Trip.com` cần 59 vẫn chỉ có 58) — phải bỏ cả cái hộp.
   Gợi ý sửa được chuyển sang HOVER: nền ô sáng lên + con trỏ tay. Bảng chỉ có ở desktop
   (mobile là card) nên hover là tín hiệu dùng được. Bàn phím vẫn thấy nhờ :focus-within. */
.tbl td.edcell{ position:relative; }
.tbl td.edcell:hover{ background-color:#0000000f; }
.tbl td.edcell:focus-within{ outline:2px solid var(--cognac); outline-offset:-1px; border-radius:6px; }
.tbl .cell-sel.ghost{ position:absolute; inset:0; width:100%; height:100%; margin:0; padding:0;
  border:0; opacity:0; background:none; cursor:pointer; font:inherit; color:inherit; }
/* `.unas` từng nằm trên chính <select>; nay ô phòng chưa xếp là <span> thường. */
.tbl .unas{ color:#b3493a; font-weight:500; }
/* Ô Phòng / Loại phòng NHIỀU HÀNG. Khoa 20/09: "ko cần thu nhỏ chữ lại đâu, nó nhiều
   phòng mà size ô phải x lần lên chứ có gì đâu" ⇒ chữ giữ NGUYÊN cỡ, ô cao lên theo số
   hàng. Mỗi ô phòng chiếm đúng 24px (22 + 2 khoảng cách), và nhãn loại phòng cao
   `--n × 24px` nên "2 Twin" nằm ngang đúng hai ô phòng của nó — trông như một ô gộp.
   Chỉ áp khi CÓ nhiều hàng (`.nhieu`); 236/437 đơn là "1 Double" một phòng, giữ nguyên. */
.tbl td.rmcell.nhieu, .tbl td.rtype.nhieu{ white-space:normal; vertical-align:top;
  padding-top:5px; padding-bottom:5px; }
/* `white-space:nowrap` BẮT BUỘC: ô cha `.nhieu` đặt `normal` để các ô nhỏ xuống dòng
   được, nhưng bên TRONG một ô nhỏ thì chữ phải giữ một dòng — "406-408" gãy thành
   "406-" / "408" là nó ĐÈ lên ô dưới trong khung cao cố định (Khoa 20/09 gửi ảnh). */
/* Trông y hệt chữ ô thường: KHÔNG nền, KHÔNG bo góc, canh TRÁI như mọi ô khác.
   Khoa 20/09: "vì sao phải thêm cái ô mờ phủ bên ngoài và canh giữa cho những dòng đặc
   biệt nhiều đơn ở cột phòng mà ko thống nhất style giống xung quanh luôn, nhìn nó dị
   lắm". Đúng — viên thuốc xám là thứ tôi tự thêm, không có ở chỗ nào khác trong bảng.
   Gợi ý sửa được giữ NGUYÊN cách của mọi ô sửa được: nền sáng lên khi rê chuột.
   `white-space:nowrap` vẫn bắt buộc — "406-408" gãy dòng là đè lên ô dưới. */
.tbl .rmchip{ position:relative; display:block; height:22px; line-height:22px;
  padding:0; margin:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.tbl .rmchip:hover{ background:rgba(0,0,0,.06); }
.tbl .rmchip:focus-within{ outline:2px solid var(--cognac); outline-offset:0; }
/* ô chưa xếp phòng trong đơn nhiều hàng — phải NHÌN THẤY là còn thiếu, nhưng bằng MÀU CHỮ
   (cùng cách `.unas` đang dùng), không bằng một mảng nền lạ mắt */
.tbl .rmchip.trong{ color:#b3493a; }
/* Nhãn loại phòng: căn giữa theo chiều dọc trong khoảng nó trải, để đọc ra "cái nhãn này
   nói về mấy ô kia". 12px (không phải 13,5) vì "1 Family Connecting" cần ~125px mà cả
   bảng chỉ còn chừng đó chỗ — nới nữa là bảng cuộn ngang trở lại. */
.tbl .rtype.nhieu .tychip{ display:flex; align-items:center; line-height:1.2;
  min-height:calc(var(--n,1) * 24px - 2px); }
.tbl .rtype.nhieu .tychip + .tychip{ border-top:1px solid var(--line2); }
/* breakfast select trong POPUP (ngoài .tbl) — select thường có viền, gọn (Kan 14/06: sửa ăn sáng) */
.bfsel{ font:inherit; padding:4px 26px 4px 9px; border:1px solid var(--line); border-radius:8px; background:var(--paper); font-weight:600; cursor:pointer; }
.empty{ text-align:center; color:var(--muted); padding:36px; }
/* status pill */
.st{ font-size:12px; padding:3px 10px; border-radius:20px; font-weight:600; white-space:nowrap; }
/* Trong BẢNG, pill trạng thái tràn 4px ra ngoài ô và đè lên vạch cột — lỗi CÓ SẴN, đo
   20/09: "Chưa thanh toán" cần 99px, lòng ô chỉ 95px (bảng `width:100%` nên cột bị ép,
   không tự nới). 298 ô dính, cả dòng khoá lẫn dòng sửa được. Siết đệm ngang 10→7px là vừa;
   nới cột thì phải lấy của cột khác và cả bảng lại chật thêm chỗ khác. */
.tbl .st{ padding:3px 7px; }
.td-dash{ color:var(--muted); }
/* Đặt cọc (Kan 05/07): td.tdp mặc định .tbl td{white-space:nowrap} sẽ nhét "còn X" dính
   liền giá trên 1 dòng dài — bật lại wrap CHỈ ô này để td-sub xuống dòng riêng, amber đậm
   khớp ngôn ngữ "còn thu" đã dùng ở card/popup (UNPAID #c8902f). */
.tbl td.tdp{ white-space:normal; }
.td-sub{ display:block; font-size:12px; font-weight:600; color:#c8902f; margin-top:1px; }

/* ---- booking cards (mobile table) ---- */
/* card Bảng mobile 3 tầng dọc: tên+pill / meta / tiền (Kan 2026-06-12) */
.bcard{ display:block; padding:11px 13px 12px; background:var(--paper); border:1px solid var(--line); border-radius:12px; margin-bottom:8px; box-shadow:var(--shadow); cursor:pointer; }
.bc-top{ display:flex; align-items:flex-start; justify-content:space-between; gap:10px; }
.bc-name{ font-size:16px; line-height:1.3; min-width:0; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; } /* tên = khoá quét chính → to nhất card; dài kẹp 2 dòng, đầy đủ xem popup */
.bc-top .st{ flex:none; margin-top:2px; white-space:nowrap; }
/* chip nguồn + thanh toán: gọn về phải, xuống hàng đều khi tên dài (Kan 14/06: hết lệch/dup) */
.bc-tags{ display:flex; flex-wrap:nowrap; justify-content:flex-end; align-items:flex-start; gap:4px 6px; flex:none; max-width:52%; min-width:0; }
/* nowrap: cho wrap thì thẻ nào có 2 chip sẽ cao thêm 25px, danh sách so le và chừa
   một khoảng rỗng ngay dưới tên khách (đo 18/09: 39/80 thẻ cao 114px vs 41 thẻ 89px). */
.bc-tags > *{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.bc-tags .src-tag{ margin-top:3px; }
/* chip NGUỒN trên card Bảng mobile (Kan 12/06): Trip đã thu sau này thành Đã
   thanh toán vẫn biết đơn từ đâu; TA hiện tên đại lý */
.src-tag{ display:inline-block; font-size:11.5px; font-weight:700; letter-spacing:.02em; padding:2px 7px; border-radius:7px; white-space:nowrap; }
.bc-meta{ color:var(--muted); font-size:12.5px; line-height:1.45; margin-top:3px; }
.bc-warn{ color:#a8741a; font-weight:600; }
.bc-money{ font-size:14px; font-weight:600; margin-top:3px; }
.bc-money small{ color:var(--muted); font-weight:400; font-size:12px; }
.bcard.past{ opacity:.55; }
/* Đơn ĐÃ HUỶ (Khoa 18/09, ảnh màn Bảng tháng 11). Chốt sau khi render 4 phương án ở 402px
   bằng CSS này và ĐO tương phản trên nền thẻ:
     mờ 55%          → chữ "Đã huỷ" còn 1,25 · tên 8,30
     mờ 70% + gạch   → 1,60 · 10,57   ← chọn
     viền trái + 75% → 1,71 · 11,32
     nền xám         → 2,60 · 2,60 (tên khách không đọc nổi ⇒ loại)
   Gạch ngang là cách DUY NHẤT mang nghĩa bằng hình dạng, không phụ thuộc độ tương phản —
   ngoài nắng thì 55% với 70% gần như nhau. Xem ca ⑨ của ops/tests/table_ui.py. */
.bcard.huy{ opacity:.7; }
.bcard.huy .bc-name{ text-decoration:line-through; text-decoration-thickness:1.5px; }
.tbl tr.huy td{ opacity:.7; }
.tbl tr.huy td.gname{ text-decoration:line-through; text-decoration-thickness:1.5px; }

/* ---- modals ---- */
.modal{ position:fixed; inset:0; z-index:50; background:rgba(30,22,14,.55); display:flex; align-items:center; justify-content:center; padding:16px; padding-bottom:calc(16px + env(safe-area-inset-bottom, 0px)); }
.modal .card{ background:var(--paper); border-radius:18px; padding:22px; max-width:380px; width:100%; position:relative; max-height:90vh; overflow:auto; box-shadow:0 20px 60px rgba(0,0,0,.3); }
/* iOS Safari: vh KHÔNG trừ thanh công cụ → đáy card (nút Lưu) từng bị toolbar đè.
   dvh = viewport động, luôn nhìn thấy trọn card (Kan 12/06 'fix dứt điểm'). */
@supports (height: 1dvh){ .modal .card{ max-height:calc(100dvh - 28px); } }
.modal .x{ position:absolute; top:2px; right:4px; border:0; background:none; font-size:26px; cursor:pointer; color:var(--muted); line-height:1; min-width:44px; min-height:44px; padding:6px 12px; border-radius:50%; }
.modal .x:hover{ background:var(--cream); color:var(--ink); }
.modal h3{ font-family:"EB Garamond",serif; margin:2px 0 14px; font-size:22px; padding-right:24px; }
.row{ display:flex; justify-content:space-between; gap:14px; padding:8px 0; border-top:1px solid var(--line2); font-size:14px; align-items:center; }
.row span{ color:var(--muted); } .row b{ text-align:right; }
.row a{ color:var(--cognac); text-decoration:none; font-weight:600; }
/* status editor in popup */
.editbar{ margin-top:14px; border-top:1px solid var(--line); padding-top:13px; }
.eb-lab{ font-size:12px; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin-bottom:9px; }
.eb-btns{ display:flex; align-items:stretch; gap:0; border:1px solid var(--line); border-radius:12px; overflow:hidden; transition:opacity .2s; }
.eb-btns button{ flex:1; display:flex; align-items:center; justify-content:center; text-align:center; border:0; border-right:1px solid var(--line); border-radius:0; background:#fff; color:var(--ink); min-height:44px; padding:8px 6px; font:inherit; font-size:12px; line-height:1.2; cursor:pointer; transition:background-color .18s ease; }
.eb-btns button:last-child{ border-right:0; }
.eb-btns button.on{ background:var(--c); color:#fff; font-weight:600; }
.eb-msg{ font-size:13px; min-height:1em; margin-top:9px; } .eb-msg.ok{ color:#5a8a4a; } .eb-msg.err{ color:#bf5b4b; }

/* ---- form ---- */
.form form{ display:flex; flex-direction:column; gap:11px; min-height:0; flex:1; }
/* DỨT ĐIỂM nút Lưu (Kan 12/06): KHÔNG sticky nữa — iOS coi sticky-bottom trong
   scroller như phần tử thường (vuốt cuối mới ló, vuốt lên là mất). Giờ .fbody là
   vùng cuộn RIÊNG, nút nằm ngoài → ghim cứng đáy card vĩnh viễn. */
.modal .card.form{ overflow:hidden; display:flex; flex-direction:column; }
.fbody{ display:flex; flex-direction:column; gap:11px; overflow-y:auto; min-height:0; flex:1; -webkit-overflow-scrolling:touch; margin:0 -6px; padding:2px 6px 14px; }
.form label{ display:flex; flex-direction:column; gap:4px; font-size:12px; color:var(--muted); font-weight:500; }
.form input, .form select{ font:inherit; font-size:16px; color:var(--ink); padding:11px; border:1px solid var(--line); border-radius:10px; background:#fff; width:100%; box-sizing:border-box; }
/* iOS date input phình intrinsic → ép cùng khổ các ô khác (Kan 12/06: lọt ra ngoài) */
.form input[type=date]{ -webkit-appearance:none; appearance:none; min-width:0; max-width:100%; }
.form .two{ display:flex; gap:10px; } .form .two label{ flex:1; min-width:0; }
/* room field: label + live availability count, then a compact dropdown */
.fld{ display:flex; flex-direction:column; gap:4px; }
.flab{ font-size:12px; color:var(--muted); font-weight:500; display:inline-flex; align-items:center; gap:7px; }
.flab .avail{ display:inline-block; font-weight:600; font-size:12px; padding:2px 9px; border-radius:12px; background:#e3eddc; color:#4d7a3e; border:1px solid #cfe0c2; }
.flab .avail:empty{ display:none; }
.flab .avail.none{ background:#fbe4e0; color:#b3493a; border-color:#eebfb6; }
.form select:disabled{ opacity:.5; }
/* nút tĩnh, flush đáy card (card đo dvh nên luôn trong màn hình) */
.form .submit{ flex:none; margin:6px -22px -22px; border:0; background:var(--cognac); color:#fff; border-radius:0 0 18px 18px; padding:15px; font:inherit; font-weight:600; font-size:15px; cursor:pointer; box-shadow:0 -10px 18px -8px var(--paper); }
.form .submit:disabled{ opacity:.5; }
.form-msg{ margin:0; font-size:13px; min-height:1em; } .form-msg.err{ color:#bf5b4b; } .form-msg.ok{ color:#5a8a4a; }

/* ---- desktop ---- */
@media(min-width:760px){
  .top{ padding:13px 28px; } .top h1{ font-size:24px; }
  /* MỘT hàng: logo · tên · tab · ⟳ · Thêm. `display:contents` thả con của .top ra làm
     flex item của .hdr, nhờ đó ⟳ và Thêm nằm được BÊN PHẢI tab chứ không kẹt giữa. */
  .hdr{ display:flex; align-items:center; gap:10px; padding:6px 28px;
        border-bottom:1px solid var(--line); }
  .top{ display:contents; }
  .top .mono{ order:1; }
  /* `.modes` là `flex:1 1 auto` nên nó giành hết chỗ trống và BÓP khối tên — "Phòng Phú Quốc"
     rơi xuống hai dòng ("Phòng Phú" / "Quốc") ngay giữa desktop rộng. `.ttl` có `min-width:0`
     (cần cho khổ hẹp) nên không tự chống được. Chốt: tên KHÔNG co, KHÔNG xuống dòng; chỗ
     trống còn lại vẫn của dãy tab như cũ. Không đụng cỡ chữ hay màu. */
  /* ⚠️ ĐỪNG GỠ HAI DÒNG NÀY vì tưởng "đã vá gốc rồi". Gốc (bỏ `.modes` khỏi luật
     `width:100vw` của chế độ Bảng) đã xử, nhưng đo cặp 18/09 cho kết quả NGƯỢC ĐỜI: tiêm lại
     bộ chọn cũ mà vẫn giữ hai dòng này thì tiêu đề VẪN một dòng, header vẫn 57px. Phải tiêm
     lại CẢ BA mới ra 2 dòng / 66px. Nghĩa là hai dòng này mới là thứ ĐANG CHỊU LỰC; vá gốc
     đúng về nguyên nhân nhưng hôm nay không phải cái giữ bố cục. Gỡ là lỗi quay lại ngay. */
  .ttl{ order:2; flex:0 0 auto; min-width:auto; }
  .top h1{ white-space:nowrap; }
  /* `flex:1 1 0` + `min-width:0` = dãy tab ĐƯỢC PHÉP CO. Bản trước để `flex:1 1 auto` và
     `overflow:visible`, nên khi hết chỗ nó không co cũng không cắt mà TRÀO ĐÈ lên nút bên
     phải: đo 18/09 ở 820px (iPad dựng đứng) tab "Tài chính" đè nút 💬 **54px**, ảnh đọc ra
     "T💬nh". Lỗi này CÓ TỪ TRƯỚC bản vá tiêu đề; vá tiêu đề chỉ làm nặng thêm 16px.
     Cho cuộn ngang như khổ điện thoại — ở ≥920px nội dung vừa nên không ai thấy gì đổi. */
  .modes{ order:3; flex:1 1 0; min-width:0; padding:0; border-bottom:0;
          overflow-x:auto; overflow-y:visible; scrollbar-width:none; }
  .modes::-webkit-scrollbar{ display:none; }
  .chat-btn{ order:4; margin-left:0; } .rf-btn{ order:5; margin-left:0; } .add-btn{ order:6; }
  .legend{ padding:2px 28px 10px; }
  /* Thanh ngày + dải số hôm nay về MỘT hàng: thao tác bên trái, số liệu bên phải. */
  .subbar{ display:flex; align-items:center; gap:16px; padding:5px 28px 4px; }
  .subbar .ctrlbar{ order:1; flex:0 0 auto; padding:0; }
  .subbar .summary{ order:2; flex:1 1 auto; margin-left:auto; justify-content:flex-end;
                    padding:0; background:none; }
  .tapewrap{ padding:0 28px 16px; } /* padding-top 2px = khe hở trên header dính (Kan) */ .pane{ padding:8px 28px 30px; }
  .modal .card{ max-width:440px; }
  /* landscape → roomier rows + bigger, more legible labels (mobile keeps the compact base) */
  .rc-corner, .gh{ height:41px; }
  .rc-floor, .floorspacer{ height:30px; }
  body{ --rowh:46px; }
  .rc-room b{ font-size:16px; } .rc-room small{ font-size:12px; }
  /* toolbar reads as a left-aligned cluster, not a stretched date field in a thin strip */
  .ctrlbar{ padding:10px 28px; justify-content:flex-start; }
  .ctrlbar input[type=date]{ flex:0 0 auto; width:172px; }
  /* Bảng table + Theo-ngày fill the width instead of a centered narrow card */
  .pane.wide, .pane.day{ max-width:none; }
  .droomwrap{ grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); align-items:start; }
  /* PQ: mỗi tầng đúng 4 phòng → 4 cột = mỗi hàng là 1 tầng (Kan 2026-06-12) */
  body[data-hotel="pq"] .droomwrap{ grid-template-columns:repeat(4, minmax(220px, 1fr)); } /* card rộng hơn trên desktop, tên dài không kéo giãn cả hàng */
  .tbl td:nth-child(2){ min-width:80px; } /* Phòng "406+408" không bị bóp */
  .tbl th:last-child, .tbl td:last-child{ padding-right:12px; }
  /* dashboard strip under the Lịch grid uses the space a sparse 8-room grid leaves below */
  .tapestats{ display:flex; align-items:center; flex-wrap:wrap; gap:8px; padding:14px 28px 8px; margin:2px 28px 0; border-top:1px solid var(--line); }
  .tapestats .sum-lab{ margin-right:3px; }
}
@media(max-width:759px){ .tapestats{ display:none; } }
/* ---- mobile: stack form 2-col rows ---- */
@media(max-width:520px){
  .form .two{ flex-direction:column; gap:11px; }
}
@media(max-width:759px){ .modes{ overflow-x:auto; -webkit-overflow-scrolling:touch; } }

/* ====== PER-HOTEL: Phú Quốc — sea-green identity + denser tape (25 rooms) ======
   Đà Nẵng keeps the default cognac/gold. Theme is a retint of the brand accent so staff know
   which hotel they're in at a glance; the engine is shared. */
body[data-hotel="pq"]{ --cognac:#3E8E7E; --gold:#4FA08D; }
body[data-hotel="pq"] .gh.today{ background:rgba(62,142,126,.26); }
body[data-hotel="pq"] .cell.today{ background:rgba(62,142,126,.12); }
body[data-hotel="pq"] .modes button.on{ background:#2f5d54; border-color:#2f5d54; }
/* PQ shows only the room number (no Double/Twin) → single line, so rows go shorter (more rooms
   on screen). roomcol height MUST equal its grid cell at each breakpoint (iOS two-pane align). */
.rc-room.nolabel{ justify-content:center; }
.rc-room.nolabel b{ font-size:15px; }
body[data-hotel="pq"]{ --rowh:30px; }
@media(min-width:760px){
  body[data-hotel="pq"]{ --rowh:34px; }
  body[data-hotel="pq"] .rc-corner, body[data-hotel="pq"] .gh{ height:38px; }
  body[data-hotel="pq"] .rc-floor, body[data-hotel="pq"] .floorspacer{ height:26px; }
}

/* ---- staff login overlay ---- */
.card.login{ max-width:320px; text-align:center; padding:28px 24px 24px; }
.card.login h3{ padding-right:0; margin:10px 0 2px; }
.login-mono{ height:40px; width:auto; opacity:.92; }
.login-sub{ margin:0 0 14px; color:var(--muted); font-size:13px; }
.card.login .form-msg{ margin-top:10px; }
.g-btn{ display:flex; justify-content:center; margin-top:14px; }
.g-btn:empty{ display:none; }

/* edit-booking entry in the popup */
.eb-edit{ display:block; width:100%; margin-top:12px; border:1px solid var(--cognac); background:none; color:var(--cognac); border-radius:12px; padding:11px; font:inherit; font-weight:600; font-size:13px; cursor:pointer; min-height:44px; }
.eb-edit:hover{ background:var(--cream); }

/* manual refresh in the header — gom về sát ＋Thêm (Kan: lơ lửng giữa nhìn kỳ) */
.rf-btn{ margin-left:auto; border:1px solid var(--line); background:var(--paper); color:var(--muted); border-radius:50%; width:36px; height:36px; min-height:36px; padding:0; font-size:17px; line-height:1; cursor:pointer; flex:none; }
.rf-btn:hover{ color:var(--ink); border-color:var(--cognac); }
#refresh-btn{ color:var(--ink); } #refresh-btn svg{ display:block; margin:auto; }
.rf-btn.spin{ animation:rfspin .8s linear infinite; pointer-events:none; opacity:.6; }
@keyframes rfspin{ to{ transform:rotate(360deg); } }

/* PIN row under the Google button (parallel login methods) */
#login-form{ display:flex; gap:8px; margin-top:14px; }
#login-pin{ flex:1; min-width:0; text-align:center; font-size:17px; letter-spacing:4px; padding:10px; border:1.5px solid var(--line); border-radius:12px; background:var(--cream); font-family:inherit; color:var(--ink); }
#login-pin:focus{ outline:none; border-color:var(--cognac); }
.card.login .submit{ border:0; background:var(--cognac); color:#fff; border-radius:12px; padding:0 22px; font:inherit; font-weight:600; font-size:14px; cursor:pointer; min-height:44px; }

/* 5 status buttons (thêm Đã nhận/trả phòng) — wrap thành 2 hàng trên mobile */
.eb-btns{ flex-wrap:wrap; }
.eb-btns button{ flex:1 1 32%; }

/* Bảng anchors at today (full history above — swipe up); clear the sticky chrome */
/* Cuộn tới dòng phải trừ ĐÚNG chiều cao phần dính (JS đo, xem syncStickyTops) — số
   cứng 104px cũ nhỏ hơn thực tế 173-213px nên dòng đích nằm khuất sau thanh. */
.tdy-anchor{ scroll-margin-top:var(--row-top, 104px); }
.tbl tbody tr{ scroll-margin-top:var(--row-top, 104px); }   /* nút 'Nhảy tới ngày' cũng cuộn tới dòng */

/* Bảng: heading dính khi cuộn 1000+ dòng (top = .top 48px + .modes ~51px).
   .tbl phải bỏ overflow:hidden — ancestor có overflow là sticky chết. */
.tbl{ overflow:visible; }
/* Lá chắn che TOÀN BỘ khoảng hở phía trên thead (KHÔNG chỉ 4px). DỨT ĐIỂM bug Bảng
   Desktop (Kan 15/06): khi bảng rộng hơn viewport → trang cuộn ngang, thanh .top
   (rộng = viewport) không phủ tới 2 cột phải (Giá/Giá-đêm) → ô giá của dòng cuộn lên
   "lọt" qua khe 0→thead-top. Shadow cream cao kín khe này che mọi cột, kể cả vùng tràn. */
/* offsetY ÂM = spread DƯƠNG → khối cream phủ kín từ thead LÊN trên (mọi cột, kể cả vùng
   tràn bên phải), dừng đúng đáy thead nên KHÔNG đè tbody. (Trước dùng '-100vh 0' thiếu
   spread → chỉ dịch bản sao mỏng ra ngoài màn, vô tác dụng — Kan 15/06 bug còn.) */
/* Khiên cream đặt trên HÀNG (1 phần tử, ko có anh-em bên trái) → ko đè chữ cột trái.
   Trước (264893a) để trên từng <th>: mỗi th là 1 sticky stacking-context, shadow spread
   1500px của th phải vẽ ĐÈ chữ mọi th bên trái → chỉ cột cuối (Giá/đêm) còn hiện. */
.tbl thead tr{ position:sticky; top:var(--thead-top, 99px); z-index:4; box-shadow:0 -1500px 0 1500px var(--cream); }
.tbl thead th{ position:sticky; top:var(--thead-top, 99px); z-index:5; }   /* cột vẽ đè lên khiên hàng (nền cream riêng ở .tbl th) */

/* sửa kiểu Excel trên Bảng desktop */
.tbl td[data-f]{ cursor:pointer; }
.tbl td[data-f]:hover{ box-shadow:inset 0 0 0 1.5px var(--cognac); border-radius:7px; }
.cell-ed{ font:inherit; font-size:13px; padding:4px 7px; border:1.5px solid var(--cognac); border-radius:7px; background:#fff; max-width:150px; }
select.cell-ed{ max-width:170px; }

/* multi-room rows in the add/edit form */
.roomrow{ display:flex; gap:6px; margin-top:6px; }
.roomrow select{ flex:1; min-width:0; }
.rr-x{ border:1px solid var(--line); background:var(--paper); color:var(--muted); border-radius:10px; min-width:44px; min-height:44px; font-size:18px; cursor:pointer; }
.add-room{ border:0; background:none; color:var(--cognac); font:inherit; font-size:12.5px; font-weight:600; padding:8px 0 0; cursor:pointer; }

/* month/year dividers in the mobile booking list */
.mdiv{ position:sticky; top:var(--thead-top, 97px); z-index:4; margin:14px -2px 6px; padding:5px 12px; background:var(--cream); color:var(--muted); font-size:11.5px; font-weight:600; text-transform:uppercase; letter-spacing:.07em; border-bottom:1px solid var(--line); display:flex; justify-content:space-between; align-items:baseline; gap:10px; }
/* Mỗi tháng một khối. Dải `.mdiv` ghim trong khối của chính nó nên hết tháng là nó tự
   nhường chỗ — trước đây mọi dải đều là con của `.pane` nên cùng ghim một mốc, xếp chồng
   lên nhau và đè mất ~20px đầu thẻ ngay dưới (đúng chỗ tên khách). */
.msec{ display:block; }
.mdiv-agg{ text-transform:none; letter-spacing:0; color:var(--ink); font-weight:600; white-space:nowrap; } /* tổng tháng (manager): doanh thu nổi hơn nhãn tháng */
/* Mốc tháng Bảng desktop — heading editorial khớp brand (EB Garamond, như .dayhd/.modal h3),
   viền cognac 2px trên mỗi cụm = ngăn cách nổi bật mà hài hoà (Kan 13/06: chữ to, nổi bật). */
.tbl tr.mrow{ cursor:default; }
/* Hàng mốc tháng = dải BEIGE ẤM nổi hẳn khỏi nền cream (R1, Kan chốt 13/06) — GIỮ 1 BẢNG
   (header dính tự đồng bộ + cột tự canh; thẻ-rời = anti-pattern lệch header). border-top
   dày màu paper = khe hở nhẹ giữa 2 tháng. */
/* KHÔNG khe hở trắng (seam ngứa mắt — Kan 13/06): band beige CAO hơn (tăng padding trên),
   tách tháng bằng MÀU + hairline 2 cạnh, dính liền rows (không gap paper). */
.tbl tbody tr.mrow td{ background:#ece0c9; color:var(--ink); font-family:"EB Garamond",serif; font-size:17px; font-weight:600; letter-spacing:.01em; padding:14px 15px 14px; border-top:1px solid #cdbd98; border-bottom:1px solid #ddcfb0; white-space:nowrap; }
.tbl tbody tr.mrow:hover td{ background:#ece0c9; }
/* subtotal sát ngay sau tên tháng (inline) — hết khoảng trống xa lạ; mảnh & muted */
.mrow-agg{ margin-left:14px; font-family:"Be Vietnam Pro",sans-serif; font-size:11.5px; font-weight:500; color:var(--muted); letter-spacing:0; }
/* second line of a month strip: cancelled cards, fee, money Finance books in another month (Khoa 28/09/2026) */
.mnote{ margin:-2px 12px 8px; text-align:right; font-size:12px; color:var(--muted); }
.strip-note{ opacity:.85; }

/* Bảng desktop polish (Kan 2026-06-12 screenshot: full-width stretch ở màn rộng →
   cột Khách nuốt nửa bảng, data dạt hết về phải). Recenter + cap; tiền canh phải. */
@media (min-width: 760px) {
  .pane.wide{ max-width:none; } /* Kan 2026-06-12: tận dụng hết bề ngang màn rộng */
  /* Bảng rộng hơn màn → trang cuộn ngang. .top/.modes dính left:0 NHƯNG bề rộng chỉ bằng
     khung nhìn ban đầu, nên cuộn phải bao nhiêu là hụt bấy nhiêu ở mép phải: lộ mảng trắng
     và bảng lòi lên trên thanh công cụ (Kan 29/07 gửi ảnh). Cho body rộng bằng nội dung
     → 2 thanh này trải hết bề ngang thật và dính trái đúng nghĩa. */
  /* ⚠️ CHỈ áp cho chế độ BẢNG (Kan 05/08 gửi ảnh). Trước đây đặt trên body trần nên ăn
     sang cả LỊCH: lịch vốn tự đóng băng cột phòng bằng cách cho .tape cuộn TRONG khung
     riêng, trang không bao giờ được cuộn ngang. min-width:max-content làm TRANG cuộn
     ngang ⇒ cột tên phòng + thanh công cụ trôi mất, và cột ngày bị kéo giãn to bất
     thường (max-content làm hỏng minmax(46px,1fr)). Một dòng CSS, hai lỗi. */
  body[data-mode="table"]{ min-width: max-content; }
  /* ⚠️ CÒN 28px cuộn ngang thừa ở khổ rộng, ĐÃ BIẾT và CỐ Ý để yên. Đó đúng bằng lề trái+phải
     của `.pane`, bị cộng vào `max-content` của body. Đã thử `overflow-x: clip` (18/09): KHÔNG
     ăn, vẫn cuộn được 28px — đã gỡ, đừng thử lại. Hai cách còn lại đều đụng vào cơ chế mà
     Khoa đã bị hỏng hai lần (29/07 và 07/08, xem ghi chú ngay dưới): bỏ lề `.pane` thì mất
     lề thật, còn dời `min-width` sang `.hdr/.subbar` thì chính là `min-width:100vw` đã hỏng.
     `ops/tests/table_ui.py` chốt CỨNG con số 28 — thêm cột mà nó nhúc nhích là ca kiểm đỏ. */
  /* ⚠️ min-width:100vw KHÔNG ghim được (Kan 07/08 ảnh: tiêu đề + nút chế độ trôi mất khi
     cuộn phải). Là block con của body nên chúng rộng bằng CẢ nội dung (đo: 1576px) ⇒ đầy
     khung cha ⇒ sticky không còn chỗ trượt ⇒ left:0 vô nghĩa, nội dung trôi theo trang
     (tiêu đề ra x=-444, nút Lịch x=-472). min-width chỉ làm cái NỀN trải rộng, đó là lý do
     bản vá 29/07 trông như đã xong. Phải ghim đúng bề ngang khung nhìn — giống .fbar. */
  /* 11/09/2026: .top và .modes gộp vào .hdr, và .hdr mới là thứ sticky — nên PHẢI ghim
     chính .hdr. Để nguyên luật cũ thì .hdr rộng bằng cả bảng (đo: 1683px trên khung
     1280px) ⇒ hết chỗ trượt ⇒ đúng lại lỗi 07/08: cuộn phải 400px là nút "Lịch" ra
     x=-165. Giữ hai vế cũ cho bản điện thoại, nơi .top vẫn là block thật. */
  /* ⚠️ CHỈ ghim `.hdr`. Khoa 18/09 chỉ ra "mấy tab khác lại không bị như Lịch, Theo ngày" —
     và đó chính là manh mối: luật này chỉ áp cho `data-mode="table"`, nên chỉ tab Bảng hỏng.
     Trên desktop `.modes` là FLEX ITEM nằm cạnh khối tên; ép nó `width:100vw` là nó đòi trọn
     bề ngang màn hình, khối tên hết chỗ, "Phòng Phú Quốc" rơi xuống hai dòng.
     `.top` thì `display:contents` ở khổ này nên width vô nghĩa với nó.
     Ghim `.hdr` là đủ: nó mới là thứ `position:sticky`, và nó bọc cả hai. */
  body[data-mode="table"] .hdr{ width: 100vw; max-width: 100vw; }
  /* Thanh tìm/lọc phải GHIM CẢ CHIỀU NGANG (Kan 07/08: "header Bảng sao không cố định
     như Lịch"). Nó vốn chỉ sticky theo TRỤC DỌC, nên khi bảng rộng hơn màn và cuộn sang
     phải thì ô tìm kiếm trôi khỏi khung nhìn (đo tại chỗ: cuộn phải 500px → mép trái
     thanh ở x=-472). left:0 để dính ngang như .top/.modes.
     ⚠️ Phải là width/max-width:100vw, KHÔNG phải min-width: sticky chỉ trượt được TRONG
     khung cha, mà min-width để nội dung đẩy thanh rộng bằng đúng khung cha ⇒ hết chỗ
     trượt, left:0 vô tác dụng (đo: vẫn ở x=-472). Ghim đúng bề ngang khung nhìn thì nó
     mới có chỗ dịch. flex-wrap sẵn có lo phần xuống dòng khi hẹp. */
  /* trừ 28px = đệm ngang của .pane (14px mỗi bên): sticky chỉ trượt được trong HỘP NỘI
     DUNG của cha, nên thanh rộng đúng 100vw sẽ bị đẩy lệch 28px ở cuối hành trình cuộn
     (đo: cuộn 576 → thanh ở -28→972, mất 28px đầu ô tìm kiếm). Hụt 28px thì luôn vừa. */
  body[data-mode="table"] .fbar{ left:0; width:calc(100vw - 28px); max-width:calc(100vw - 28px); }
  /* Khe 28px ở MÉP PHẢI (Khoa 20/09 gửi ảnh: mảnh "…08" của một ô NGÀY ĐẶT nổi cạnh nút
     "Hôm nay"). Thanh rộng đúng `100vw − 28` vì lý do ngay trên, nên ở CUỐI hành trình cuộn
     còn một dải 28px không ai phủ — dòng bảng trượt phía sau lộ qua đúng đó. Đo bằng phép
     chạm điểm: tại x = vw−2 phần tử trên cùng là `TD.bkcol`, không phải thanh.
     Bịt bằng một mảng NỐI chạy theo thanh. KHÔNG đụng bề rộng, KHÔNG đụng cơ chế sticky —
     hai thứ đó đã làm hỏng màn của Khoa ba lần (29/07 · 07/08 · 18/09, ghi ngay trên), và
     `::after` định vị tuyệt đối thì không tham gia layout ⇒ body không rộng thêm, con số 28
     mà `table_ui.py` chốt cứng giữ nguyên. `top:-6px` để nối liền với `box-shadow:0 -6px 0`
     sẵn có của thanh, nếu không sẽ hở một sợi 6px ở đúng chỗ vừa bịt. */
  body[data-mode="table"] .fbar::after{
    content:""; position:absolute; left:100%; top:-6px; width:28px;
    height:calc(100% + 6px); background:var(--cream); }
  /* KHÔNG đặt `pointer-events:none`: mảng vá sẽ che về mặt SƠN nhưng biến mất với
     `elementFromPoint`, tức thước đo và thứ mắt thấy tách đôi — ca kiểm đỏ trong khi màn
     đã lành (dính đúng lượt đầu). Để nó nhận chạm cũng đúng về hành vi: dải này là thanh
     công cụ, chạm xuyên qua nó xuống dòng bảng bên dưới vốn là thứ nên chặn. */
  /* ⚠️ KHỐI SIẾT CỘT (29/07) ĐÃ GỠ 20/09/2026. Khoa: "giới hạn chiều rộng do bạn đặt thật
     sự ko có tác dụng vì việc vuốt qua xem thêm thông tin là bình thường, mà sao phải siết
     chiều rộng cột nhiều vậy".
     Lý do cũ ĐÃ HẾT HIỆU LỰC: 29/07 bảng rộng hơn màn làm thanh công cụ dính ĐÈ lên bảng,
     nên phải ép bảng vừa khung. Nay chính cái đè đó đã chữa đúng chỗ — `.hdr` ghim 100vw,
     `.fbar` ghim 100vw−28 kèm mảng nối bịt khe 28px (xem ngay trên). Cuộn ngang giờ chỉ
     là cuộn ngang, không kéo theo lỗi nào.
     Giữ lại đúng hai thứ: canh lề theo TÊN cột, và một trần RỘNG RÃI cho cột Khách để một
     cái tên 74 ký tự không nuốt nửa bảng. Cột khác tự nở theo nội dung. */
  .tbl td.gname{ max-width:420px; overflow:hidden; text-overflow:ellipsis; }
  /* Canh lề bám TÊN cột (Kan 29/07). Trước dùng nth-child(9)/(10) — vị trí cố định, mà
     DN/PQ khác số cột nên quy tắc canh-phải rơi nhầm cột. Thêm/bớt cột không vỡ nữa. */
  .tbl th[data-col="checkOut"], .tbl td:nth-child(5){ text-align:center; }
  .tbl th[data-col="price"], .tbl td.tdp,
  .tbl th[data-col="pernight"], .tbl td.pncol{ text-align:right; }
}

/* delete booking (edit form only) */
.del-btn{ display:block; width:100%; margin-top:10px; border:1px solid #bf5b4b; background:none; color:#bf5b4b; border-radius:12px; padding:11px; font:inherit; font-weight:600; font-size:13px; cursor:pointer; min-height:44px; }
.del-btn:hover{ background:#bf5b4b14; }

/* Đặt cọc (Kan 05/07, sửa lần 2 review 05/07): viền chấm ĐEN (--ink) đứt = "đã cọc, còn
   thu thêm" trên thanh Lịch. Lịch sử màu: (1) amber ban đầu trùng hệt màu fill UNPAID
   #c8902f nên vô hình; (2) đổi sang trắng đứt — vô hình LẦN 2 khi cùng thanh có class
   .inhouse (box-shadow trắng LIỀN cùng băng 2px, trắng-trên-trắng không phân biệt được dù
   khác property/nét — review 05/07 render thật xác nhận .inhouse.dep == .inhouse hệt nhau).
   Đen tương phản MỌI màu fill (amber/xanh/xám) VÀ tương phản với viền trắng liền của
   .inhouse khi chồng nhau (đen đứt đè lên trắng liền vẫn thấy rõ 2 tín hiệu khác nhau).
   Khai TRƯỚC .unas để đơn vừa chưa xếp phòng vừa có cọc (hiếm) ưu tiên cảnh báo
   chưa-xếp-phòng (cascade: khai sau thắng). */
.bar.dep{ outline:2px dashed rgba(42,36,29,.85); outline-offset:-2px; }
/* seated/moved by the machine, not yet reviewed (Khoa 18/10/2026) — a cognac ring OUTSIDE the bar: the inside
   (pay colour) and the outline (deposit / unassigned) already carry meaning */
.bar.review{ box-shadow:0 0 0 2px #fff, 0 0 0 4px var(--cognac), 0 1px 6px rgba(0,0,0,.28); z-index:2; }
.bcard.review, .droom.review{ box-shadow:0 0 0 2px var(--cognac); }
.droom.review .who::before{ content:'🔁 '; }
.pp-review{ margin:8px 0 4px; padding:8px 10px; border-left:3px solid var(--cognac); background:var(--cream); border-radius:6px;
  font-size:13.5px; display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.pp-review .pp-seen{ margin-left:auto; border:1px solid var(--cognac); background:#fff; color:var(--cognac); border-radius:16px;
  min-height:32px; padding:0 14px; font:inherit; font-weight:600; cursor:pointer; }
/* booking chưa xếp phòng: bar viền đứt trên Lịch + cảnh báo ở Theo ngày */
.bar.unas{ outline:2px dashed rgba(60,40,20,.45); outline-offset:-2px; opacity:.92; }
.legend i.dep-dot{ border-radius:3px; background:#c8902f; outline:2px dashed rgba(42,36,29,.85); outline-offset:-2px; }

/* Nhận/Trả hôm nay trong Theo ngày */
.io-in{ color:#3E6F59; margin-right:6px; } .io-out{ color:#8a5a3a; margin-right:6px; }

/* batch tối ưu theo audit 2026-06-12 */
.rc-floor{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } /* "NHÀ SỐ CHẴN" hết bị cắt chữ */
body[data-hotel="dn"] .modes button.on{ background:var(--cognac); border-color:var(--cognac); } /* DN tab active = cognac đồng bộ brand */
/* (padding chống đè đã chuyển vào .fbody) */
.mdiv{ margin-top:20px; } /* ngắt tháng tách rõ khỏi card trên */
/* Mobile Board starts at today's first booking; the past is one tap away (28/09/2026 — see boardPast in app.js). */
.past-btn{ display:block; width:100%; min-height:44px; margin:8px 0 2px; border:1px dashed var(--line); border-radius:10px;
  background:transparent; color:var(--muted); font:inherit; font-size:13px; cursor:pointer; }
.dfilter{ display:block; margin:10px 0 4px auto; padding:7px 14px; font-size:12.5px; min-height:36px; border-radius:18px; background:none; border:1px solid var(--line); color:var(--muted); }
.dfilter.on{ border-color:var(--cognac); color:var(--ink); background:var(--paper); }

/* thanh filter Bảng desktop */
/* Thanh lọc/tìm DÍNH luôn khi cuộn (Kan 14/06: trước chỉ hiện ở đầu trang, UX kém) —
   nằm dưới .modes, trên thead. bg cream che dòng cuộn dưới nó. */
.fbar{ display:flex; gap:8px; align-items:center; flex-wrap:wrap;
  position:sticky; top:var(--bar-top, 99px); z-index:6; background:var(--cream);
  margin:0 0 6px; padding:8px 0 9px; box-shadow:0 -6px 0 var(--cream); }
/* góc phải fbar: nhảy-tới-ngày + Hôm nay (Kan 13/06) */
.fbar .fb-right{ margin-left:auto; display:flex; gap:8px; align-items:center; }
.fbar #fb-jump{ padding:6px 8px; border:1px solid var(--line); border-radius:9px; font:inherit; font-size:12.5px; background:var(--paper); color:var(--ink); }
.fb-today-btn{ padding:7px 14px; border:1px solid var(--cognac); border-radius:9px; background:var(--cognac); color:#fff; font:inherit; font-size:12.5px; font-weight:500; cursor:pointer; white-space:nowrap; }
.fbar input{ flex:1; min-width:200px; padding:8px 12px; border:1px solid var(--line); border-radius:10px; background:var(--paper); font:inherit; font-size:13px; }
.fbar select{ padding:8px 10px; border:1px solid var(--line); border-radius:10px; background:var(--paper); font:inherit; font-size:12.5px; color:var(--ink); }
.fbar .fb-count{ font-size:12px; color:var(--muted); margin-left:2px; white-space:nowrap; }
.fbar #fb-x{ border:0; background:none; color:#bf5b4b; font:inherit; font-size:12.5px; font-weight:600; cursor:pointer; }

.iochip.warn{ border-color:#e3c992; background:#fdf3e3; }

/* card chưa-xếp-phòng trong Theo ngày (Kan 2026-06-12) */
.unas-hd{ color:#a8741a !important; }
.droom.unas-card{ border-color:#e3c992; background:#fdf3e3; }

/* nút xoá booking: hạ cấp thành link đỏ tách biệt — đỡ nhầm với Lưu */
.del-btn{ width:auto; margin:16px auto 2px; border:0; background:none; text-decoration:underline; text-underline-offset:3px; padding:8px 16px; font-weight:500; }
.del-btn:hover{ background:none; color:#9c4536; }

/* tape render hint */
/* KHÔNG will-change:transform trên .grid — nó KHÔNG bao giờ transform (cuộn bằng overflow
   của .tape), nhưng will-change ép promote NGUYÊN grid (rộng tới 12000px+, lớn dần không
   trim) thành 1 layer GPU khổng lồ thường trực → raster/upload nặng = giật khi cuộn trên
   màn 120Hz (đo 2026-06-26). Bỏ đi → Chrome chỉ raster phần thấy được. */
.tape{ -webkit-overflow-scrolling:touch; }

/* dropdown xếp phòng ngay trên card chưa-xếp */
.unas-pick{ flex:0 0 auto; max-width:128px; padding:8px 8px; border:1px solid #d8b56a; border-radius:10px; background:#fff; font:inherit; font-size:12.5px; font-weight:600; color:#7a5a16; min-height:40px; }
.droom.free{ cursor:pointer; }
.droom.free:active{ background:var(--cream); }

/* section Check-in / Check-out trong Theo ngày */
.floorhd.io-in{ color:#3E6F59 !important; }
.floorhd.io-out{ color:#8a5a3a !important; }
.io-none{ margin:10px 2px 2px; font-size:13px; color:var(--muted); }


/* cân xuống hàng: các cụm ngày/trạng thái/tiền không gãy giữa chừng */
.nw{ white-space:nowrap; }
.pst{ font-weight:600; }
.droom .who small{ display:block; margin-top:2px; line-height:1.6; white-space:normal; }

/* popup dễ đọc khi check-in: chữ nhỉnh hơn, TIỀN IN TO */
#popup-body .row span{ font-size:13.5px; }
#popup-body .row b{ font-size:15px; }
.pp-money{ font-size:21px !important; font-weight:700; letter-spacing:.2px; }

/* state Chờ check-in + nút check-in trong popup */
/* Chờ TRẢ phòng (sắp đi, lễ tân chuẩn bị dọn) = hổ phách */
.stay.stay-wait-out{ background:#fdf3e3; color:#a8741a; border:1px solid #e3c992; }
/* Chờ NHẬN phòng (khách sắp đến) = xanh dương dịu — phân biệt rõ với trả phòng (Kan 14/06) */
.stay.stay-wait-in{ background:#e7f0f7; color:#2c6b94; border:1px solid #bcd7e8; }
.ck-btn{ display:block; width:100%; margin-top:12px; border:0; background:#3E6F59; color:#fff; border-radius:12px; padding:13px; font:inherit; font-weight:700; font-size:14.5px; cursor:pointer; min-height:46px; }
.ck-btn.undo{ background:none; border:1px solid var(--line); color:var(--muted); font-weight:500; }
.ck-btn.out{ background:#8a5a3a; } /* trả phòng = nâu, khớp tông section Check-out */
/* Dọn phòng sau check-out (Kan 15/06). Bảng màu: HỔ PHÁCH = cần dọn (cần chú ý) ·
   TEAL = đã dọn / sẵn sàng (tích cực). Nút hành động Dọn phòng = teal (việc làm sạch). */
.ck-btn.clean{ background:#2f8f86; }                 /* nút "🧹 Đã dọn phòng" = teal sạch sẽ */
.ck-btn.clean.on{ background:none; border:1px solid #2f8f86; color:#2f8f86; font-weight:600; }
.su.clean b{ color:#a8741a; }                        /* số "cần dọn" trên dải Theo ngày = hổ phách */
.stay.stay-need{ background:#fdf3e3; color:#a8741a; border:1px solid #e3c992; }  /* badge Cần dọn = hổ phách */
.stay.stay-ready{ background:#e2efed; color:#2f7d74; border:1px solid #bfe0da; } /* badge Đã dọn/sẵn sàng = teal */
/* Khoá hoàn tác khi phòng đã có khách mới vào ở (chống bấm nhầm trùng phòng, Kan 15/06) */
.ck-locked{ margin-top:12px; padding:11px 13px; border:1px solid #e3c992; background:#fdf3e3; color:#8a6a2a; border-radius:12px; font-size:13px; line-height:1.45; }
/* hộp hỏi lý do TRẢ SỚM (Kan 13/06) */
.eco-box{ margin-top:12px; }
.eco-lab{ font-size:12.5px; color:var(--muted); margin-bottom:7px; line-height:1.4; }
.eco-reason{ width:100%; padding:11px; border:1px solid var(--line); border-radius:10px; font:inherit; font-size:15px; box-sizing:border-box; background:#fff; color:var(--ink); }
.eco-btns{ display:flex; gap:8px; margin-top:8px; align-items:stretch; }
.eco-btns .ck-btn.out{ flex:1; margin-top:0; }
.eco-cancel{ padding:0 16px; border:1px solid var(--line); border-radius:12px; background:var(--cream); color:var(--muted); font:inherit; font-weight:500; cursor:pointer; min-height:46px; }
.droom.done{ opacity:.55; } /* đã trả phòng — mờ, vẫn tap để hoàn tác */

/* lưới tape mới: rowline + colov (thay rooms×days cells) */
.rowline{ height:var(--rowh); border-bottom:1px solid var(--line2); pointer-events:none; }
/* sọc chia ngày ĐẬM hơn (Kan 12/06: mờ quá khó dóng bar theo ngày);
   hết Chủ nhật = vạch tuần đậm hẳn cho mắt bắt nhịp tuần */
.colov{ border-right:1px solid #d9cdb4; pointer-events:none; }
.colov.wk{ border-right-color:#bfae8e; }
.colov.today{ background:rgba(198,163,104,.10); box-shadow:inset 2px 0 0 var(--gold); }
.colov.we{ background:rgba(191,91,75,.022); }
.colov.hol{ background:rgba(168,50,45,.045); }
body[data-hotel="pq"] .colov.today{ background:rgba(62,142,126,.12); }

/* ăn sáng */
.bf{ padding:3px 10px; border-radius:9px; font-size:13px; }
.bf-yes{ background:#e6f0e4; color:#2e5c47; font-weight:800; font-size:14px; }
.bf-no{ background:none; color:var(--muted); font-weight:400; text-decoration:line-through; text-decoration-thickness:1.5px; padding:0; }
.bf-mini-yes{ color:#2e5c47; font-weight:800; }
.bf-mini-no{ color:var(--muted); font-weight:400; text-decoration:line-through; }

/* hàng nút nguy hiểm trong form Sửa: Huỷ | Xoá */
.danger-row{ display:flex; gap:6px; justify-content:center; }
.danger-row .del-btn, .danger-row .cxl-btn{ width:auto; margin:14px 0 2px; border:0; background:none; padding:8px 14px; font:inherit; font-size:13px; font-weight:500; cursor:pointer; text-decoration:underline; text-underline-offset:3px; min-height:44px; }
.cxl-btn{ color:#a8741a; }
.danger-row .del-btn{ color:#bf5b4b; }

/* fix vỡ card khi tên khách dài (PUSHKARSKII…): dropdown Xếp phòng xuống nguyên
   hàng dưới, nội dung canh đầu hàng thay vì giữa */
.droom{ flex-wrap:wrap; }
.droom .who{ min-width:0; }
.droom.unas-card{ align-items:flex-start; }
.droom.unas-card .chip{ margin-top:5px; }
.droom .unas-pick{ flex:1 1 100%; max-width:none; margin-top:8px; }

/* hiệu ứng trượt khi vuốt đổi ngày */
.pane.day{ transition:transform .15s ease, opacity .15s ease; }
.pane.day.day-out-l{ transform:translateX(-36px); opacity:0; }
.pane.day.day-out-r{ transform:translateX(36px); opacity:0; }
.pane.day.day-in-r{ transform:translateX(36px); opacity:0; transition:none; }
.pane.day.day-in-l{ transform:translateX(-36px); opacity:0; transition:none; }

/* popup: tên to + badge dưới tên + mục Chi tiết đơn gập */
.pp-name{ font-size:27px !important; line-height:1.15; margin:2px 26px 4px 0 !important; }
.pp-sub{ margin:0 0 10px; }
.pp-more{ margin-top:4px; }
.pp-more summary{ cursor:pointer; font-size:13px; color:var(--cognac); font-weight:600; padding:9px 0; list-style:none; }
.pp-more summary::before{ content:'▸ '; }
.pp-more[open] summary::before{ content:'▾ '; }
.pp-more summary::-webkit-details-marker{ display:none; }

.pp-ref{ font-variant-numeric:tabular-nums; letter-spacing:.4px; user-select:all; } /* dễ đối chiếu/copy */

.pp-paid{ font-size:16px; font-weight:700; } /* đã thu/OTA thu: vẫn nhỏ hơn Cần thu (21px) nhưng rõ hơn hẳn dòng TB/đêm 12px (Khoa 30/09) */

.pp-dep-total,.pp-dep-paid{ display:block; font-size:12.5px; font-weight:500; color:var(--muted); }
.pp-dep-paid{ color:#3f7d54; }
/* Dòng phụ dưới 'Cần thu' — id+2 class để thắng `#popup-body .row span` (13,5px), nếu
   không nó to ngang nhãn. Phải kể cả span CON, vì `#popup-body .row span` với tay được
   xuống mọi tầng con chứ không riêng nhãn. */
#popup-body .row .pp-owe-sub, #popup-body .row .pp-owe-sub span{ font-size:12px;
  font-weight:500; color:var(--muted); line-height:1.35; }
#popup-body .row .pp-owe-sub{ display:block; margin-top:2px; }
#popup-body .row .pp-owe-sub span{ display:block; }
/* NHÃN không được gãy đôi. `> span` chỉ chạm nhãn, không chạm span trong ô giá trị. Ở 402px
   ô giá trị của dòng 'Cần thu' rộng ~268px nên nhãn bị bóp còn 44px và 'Cần thu' rơi xuống
   hai dòng — trông như lỗi render ngay tại dòng cần đáng tin nhất (đo 18/09/2026). */
#popup-body .row > span{ white-space:nowrap; flex-shrink:0; } /* đã cọc = tiền thật đã về, xanh nhẹ như "Đã về" trong báo cáo */

/* Nguồn tiền trong popup */
.mt-btns{ display:flex; gap:8px; margin-bottom:4px; }
.mt-btns button{ flex:1; border:1px solid var(--line); background:#fff; border-radius:12px; min-height:42px; padding:8px; font:inherit; font-size:13px; color:var(--ink); cursor:pointer; }
/* chọn rồi = tô đặc, mỗi loại 1 màu riêng (Kan: nhìn là biết cái nào đang chọn) */
.mt-btns button[data-mt="Tiền mặt"].on{ background:#3E6F59; border-color:#3E6F59; color:#fff; font-weight:700; }
.mt-btns button[data-mt="Chuyển khoản"].on{ background:#3a6ea5; border-color:#3a6ea5; color:#fff; font-weight:700; }
.mt-static{ padding:9px 4px 6px; font-size:13.5px; font-weight:600; color:var(--muted); }
.mt-static small{ font-weight:400; }
.editbar .eb-lab + .mt-btns, .editbar .eb-lab + .mt-static{ margin-top:4px; }
.mt-lab{ margin-top:18px; padding-top:2px; } /* tách hẳn khỏi hàng nút trạng thái — hết lẹm chữ */

.io-done{ margin:10px 2px 2px; padding:9px 12px; background:#e6f0e4; border:1px solid #c4d8c2; border-radius:12px; font-size:13px; font-weight:600; color:#2e5c47; }

/* popup xác nhận cho Staff: thay đổi được ghi nhận kèm tên */
.sg-card{ max-width:330px !important; }
.sg-txt{ font-size:14px; line-height:1.5; color:var(--ink); margin:0 0 16px; }
.sg-btns{ display:flex; gap:8px; }
.sg-btns button{ flex:1; min-height:44px; border-radius:11px; font:inherit; font-size:14px; cursor:pointer; }
.sg-no{ border:1px solid var(--line); background:#fff; color:var(--muted); }
.sg-yes{ border:0; background:#a8741a; color:#fff; font-weight:700; }

/* slot phòng + BẢN ĐỒ PHÒNG (4 ô/hàng = 1 tầng) trong popup */
/* slot NẰM NGANG, thiếu chỗ wrap, nút đều min-width cho thẳng hàng (Kan 12/06) */
.rm-slots{ display:flex; flex-wrap:wrap; gap:6px; justify-content:flex-end; }
.pp-rs-btn{ font:inherit; font-weight:700; font-size:15px; color:var(--ink); border:1px solid var(--line); border-radius:9px; padding:7px 10px; min-width:76px; text-align:center; background:var(--paper); cursor:pointer; }
.rm-map{ margin:8px 0 2px; border:1px solid var(--line); border-radius:11px; background:var(--cream); padding:8px 12px 12px; }
.rm-map .gl{ font-size:11.5px; text-transform:uppercase; letter-spacing:.07em; color:var(--muted); margin:9px 0 5px; }
.rm-map .cells{ display:grid; grid-template-columns:repeat(4, 1fr); gap:6px; }
.cell-btn{ min-height:44px; border:1px solid var(--line); background:#fff; border-radius:9px; font:inherit; font-size:15px; font-weight:700; color:var(--ink); cursor:pointer; padding:2px; }
.cell-btn.taken{ opacity:.32; }
.cell-btn.cur{ border-color:var(--cognac); background:rgba(181,145,106,.18); color:#7a5d39; box-shadow:inset 0 0 0 1px var(--cognac); }
.cell-btn.fam{ border:1.5px dashed var(--cognac); color:var(--cognac); background:none; font-size:13.5px; }
.cell-btn.fam.cur{ border-style:solid; background:rgba(181,145,106,.18); }
.rm-map-rm{ display:block; width:100%; margin-top:11px; border:0; background:none; color:#bf5b4b; font:inherit; font-size:12.5px; text-decoration:underline; cursor:pointer; padding:4px 0; }

/* Cột Tháng BC (Kan 29/07): ô trống = tháng hệ thống tự tính → hiện mờ để phân biệt
   với giá trị ghi đè tay (đậm). Hẹp, canh giữa cho gọn. */
.rmcol{ text-align:center; white-space:nowrap; font-size:12px; }
.rmcol .td-auto{ color:#a89e91; }
.rmcol b{ color:#8a6a2a; font-weight:600; }

/* ---- Bảng tính giá kênh (tab Tối ưu, manager-only) — Kan 05/09/2026 ---- */
.cc{margin-top:18px;border-top:1px solid var(--line,#e3ded6);padding-top:16px}
.cc-head{display:flex;flex-direction:column;gap:2px;margin-bottom:12px}
.cc-head b{font-size:15px}
.cc-sub{font-size:12px;opacity:.65}
.cc-rows{display:flex;flex-direction:column;gap:10px}
.cc-card{border:1px solid var(--line,#e3ded6);border-radius:9px;padding:11px 12px;background:var(--card,#fff)}
.cc-best{border-color:#3f6b4a;box-shadow:inset 3px 0 0 #3f6b4a}
.cc-top{display:flex;justify-content:space-between;align-items:baseline;gap:8px;margin-bottom:8px}
.cc-top b{font-size:14.5px}
.cc-kind{font-size:12px;opacity:.6;text-align:right}
.cc-in{display:flex;gap:8px;margin-bottom:8px}
.cc-in label{flex:1;display:flex;flex-direction:column;gap:3px;font-size:12px;opacity:.75}
.cc-in input{width:100%;padding:6px 8px;font-size:15px;font-variant-numeric:tabular-nums;
  border:1px solid var(--line,#e3ded6);border-radius:6px;background:transparent;color:inherit}
.cc-in label:last-child{max-width:104px}
.cc-pc{display:flex;align-items:center;gap:3px;font-size:12px;opacity:.75}
.cc-pc input{width:52px;text-align:right}
.cc-promos{display:flex;flex-direction:column;gap:4px;margin-bottom:9px}
.cc-promo{display:flex;align-items:center;gap:7px;font-size:12.5px;opacity:.5;cursor:pointer}
.cc-promo.on{opacity:1}
.cc-promo input[type=checkbox]{margin:0;flex:none}
.cc-pn{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cc-promo .cc-pc input{padding:2px 4px;border:1px solid var(--line,#e3ded6);border-radius:4px;
  background:transparent;color:inherit;font-size:12px}
.cc-out{display:flex;gap:10px;border-top:1px dashed var(--line,#e3ded6);padding-top:8px}
.cc-out span{flex:1;display:flex;flex-direction:column;gap:1px}
.cc-out i{font-style:normal;font-size:11.5px;opacity:.6;text-transform:uppercase;letter-spacing:.04em}
.cc-out b{font-size:15px;font-variant-numeric:tabular-nums}
.cc-out .cc-net b{color:#3f6b4a}
.cc-bf{margin-top:7px;font-size:12px;opacity:.8}
.cc-bf b{font-variant-numeric:tabular-nums;color:#3f6b4a}
.cc-bf[hidden]{display:none}
.cc-foot{margin-top:12px;display:flex;flex-direction:column;gap:9px;align-items:flex-start}
.cc-note{margin:0;font-size:12.5px;opacity:.8}
.cc-reset{font-size:12px;padding:5px 11px;border:1px solid var(--line,#e3ded6);border-radius:6px;
  background:transparent;color:inherit;cursor:pointer}
@media (min-width:760px){
  .cc-rows{flex-direction:row}
  .cc-card{flex:1;min-width:0}
}

/* Bảng giá + phòng mở theo NGÀY trong tab Tối ưu. Ba tín hiệu màu, mỗi cái một việc:
   vàng = còn phòng chưa kênh nào mở (thứ đáng nhìn nhất) · mờ = kín phòng, không làm gì được ·
   nền nhạt = cuối tuần. Ô "nhận" đậm = kênh trả cao nhất đêm đó. */
.cc-split{margin-top:18px;border:1px solid var(--line,#dcdcdc);border-radius:6px;padding:12px 14px;background:var(--card,#fff)}
.cc-split .cc-h{font-weight:700;margin-bottom:2px}
.cc-split .cc-note{font-size:12px;opacity:.72;margin-bottom:10px;line-height:1.55}
.cc-kinds{display:flex;gap:6px;margin-bottom:10px;flex-wrap:wrap;align-items:center}
.cc-kinds button{font:inherit;font-size:13px;padding:5px 12px;border:1px solid var(--line,#ccc);
  border-radius:999px;background:transparent;color:inherit;cursor:pointer;white-space:nowrap}
.cc-kinds button.on{background:var(--ink,#222);color:var(--card,#fff);border-color:var(--ink,#222)}
.cc-kinds button.cc-filter{font-size:12px;opacity:.85}
.cc-sum{margin-top:10px;font-size:13px;padding:8px 10px;border-radius:4px;background:rgba(120,120,120,.10)}
.cc-warn{margin-top:8px;font-size:12px;padding:8px 10px;border-radius:4px;
  background:rgba(200,140,40,.15);line-height:1.5}
.cc-warn code{font-size:12px}
.cc-kinds.cc-sub2{margin-top:-4px}
.cc-kinds.cc-sub2 button{font-size:12px;padding:4px 10px}
.cc-gap{flex:0 0 10px}

/* Lịch NGANG của tab Tối ưu (desktop): ngày là CỘT, mỗi hàng trả lời một câu hỏi —
   bán bao nhiêu · trống bao nhiêu · từng kênh mở bao nhiêu và trả về bao nhiêu ·
   còn mở thêm được bao nhiêu. Cột nhãn dính trái để kéo ngang 90 ngày vẫn biết đang đọc hàng nào. */
/* Cuộn theo TỪNG CỘT và dừng ngay sau cột nhãn: một cột bị che nửa vẫn hiện đủ chữ số để
   đọc nhầm — soi 06/09/2026 đọc ".168k" trong khi số thật là "1.168k", lệch 7 lần. Bóng đổ
   thôi không đủ, phải làm cho cột không bao giờ nằm nửa dưới cột nhãn. */
.cg-wrap{ overflow-x:auto; border:1px solid var(--line,#e7dfce); border-radius:8px;
  -webkit-overflow-scrolling:touch; background:var(--card,#fff);
  scroll-snap-type:x mandatory; }   /* scroll-padding-left do JS đo, xem fit() */
.cg-day{ scroll-snap-align:start; }
.cg{ border-collapse:separate; border-spacing:0; font-variant-numeric:tabular-nums; }
.cg th, .cg td{ border-bottom:1px solid var(--line2,#efe8d8); }
/* Cột nhãn NỔI lên trên các cột ngày khi kéo ngang, nên phải trông đúng như thế: thiếu bóng
   thì cột ngày bị che nửa trông y hệt một cột đủ mà sai số (soi ảnh 06/09/2026 đọc ra ".168k"
   trong khi số thật là "1.168k"). */
/* Cột nhãn KHOÁ BỀ RỘNG: để nó tự nở theo chữ dài nhất thì một nhãn dài (khoảng ngày tạm
   dừng) ăn mất 194px bề ngang của chính cái lịch, và kéo lệch cả điểm snap. */
.cg-corner, .cg-lab{ position:sticky; left:0; z-index:2; background:var(--paper,#fffdf8);
  text-align:left; font-size:12px; font-weight:600; padding:5px 10px; white-space:nowrap;
  border-right:1px solid var(--line,#e7dfce);
  width:132px; min-width:132px; max-width:132px; overflow:hidden; text-overflow:ellipsis;
  box-shadow:7px 0 7px -7px rgba(40,30,20,.30); }

.cg-lab small{ display:block; font-weight:400; font-size:11px; color:var(--muted,#6d6450); }
.cg-corner small{ font-weight:400; color:var(--muted,#6d6450); }
.cg-months th{ padding:4px 0 3px; font-size:12px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--muted,#6d6450); background:var(--cream,#F6EFE2); font-weight:600;
  border-bottom:1px solid var(--line,#e7dfce); }
.cg-months .cg-corner{ background:var(--cream,#F6EFE2); }
.cg-day{ padding:5px 0 6px; min-width:48px; cursor:pointer; font-weight:400; }
.cg-day i{ display:block; font-style:normal; font-size:11px; color:var(--muted,#6d6450); }
.cg-day b{ display:block; font-size:12px; font-weight:600; }
.cg-day:hover{ background:var(--cream,#F6EFE2); }
.cg-day.full b{ color:#b3493a; }
.cg-day.now b{ color:#fff; background:var(--cognac,#B5916A); border-radius:5px; padding:1px 4px; }
.cg-c{ text-align:center; font-size:13px; padding:5px 3px; }
.cg-c.we{ background:#faf6ec; }
.cg-c.now{ box-shadow:inset 2px 0 0 rgba(42,36,29,.42), inset -2px 0 0 rgba(42,36,29,.42); }
.cg-c.xlow{ background:#fdf3e3; color:#8a6a2a; font-weight:600; }
.cg-c.x0{ background:#fdecea; color:#b3493a; font-weight:700; }
.cg-sold .cg-c{ color:var(--muted,#6d6450); }
.cg-free .cg-c{ font-weight:600; }
.cg-ch .cg-c{ line-height:1.12; padding:4px 3px 5px; }
.cg-ch .cg-c b{ display:block; font-size:13px; font-weight:700; }
/* Hai dòng giá: TRÊN có ăn sáng, DƯỚI không. Dòng dưới nhạt hơn một bậc để mắt bám được dòng
   nào là dòng nào khi quét ngang 150 cột. */
.cg-p{ display:block; font-style:normal; font-variant-numeric:tabular-nums; }
.cg-bf{ font-size:11px; color:var(--muted,#6d6450); }
.cg-ro{ font-size:11px; color:var(--muted,#6d6450); opacity:.68; }
.cg-win{ color:#166b40; font-weight:700; opacity:1; }
/* ĐÓNG = đêm đó kênh này không mang về đồng nào ⇒ ĐỎ, không phải xám trung tính (Kan 06/09). */
.cg-ch .cg-c.cg-shut{ background:#fbeae7; }
.cg-ch .cg-c.cg-shut b{ font-size:11.5px; font-weight:600; color:#b3493a; letter-spacing:.02em; }
/* Cũng là "đóng", nhưng vì cả KÊNH đang tạm dừng theo lệnh — chuyện cố ý, không phải chỗ cần
   chữa ⇒ xám. Lý do ghi ở NHÃN HÀNG, không lặp trong từng ô. */
.cg-ch.cg-ch-off .cg-lab{ color:var(--muted,#6d6450); }
.cg-pz{ font-size:12px; color:#b3493a; opacity:1; }
/* Kênh bị khoá theo lệnh: xám hẳn, và KHÔNG bao giờ mang dấu "trả cao nhất". */
.cg-ch .cg-c.cg-pause{ background:rgba(120,120,120,.10); }
.cg-ch .cg-c.cg-pause b{ font-size:11.5px; font-weight:500; letter-spacing:.02em;
  color:var(--muted,#6d6450); }
/* Viền đứt phải THẤY được mới có nghĩa: để màu var(--line2) thì đo ra (239,232,216) trên
   nền thẻ (238,235,226) — nét đứt có trong DOM mà mắt không thấy, tức code chết. */
.cn-c.cn-pause{ background:rgba(120,120,120,.09); border-style:dashed;
  border-color:rgba(109,100,80,.55); }
.cn-c.cn-pause b{ font-size:11.5px; font-weight:500; }
.cc-pause-so{ display:block; margin-top:4px; font-size:11.5px; opacity:1; }
.cc-pause-so b{ font-variant-numeric:tabular-nums; }

.cg-ch .cg-c.cg-na{ opacity:.35; font-style:italic; }
.cg-tot .cg-lab, .cg-tot .cg-c{ border-bottom:1px solid var(--line,#e7dfce); }
.cg-tot .cg-c{ color:var(--muted,#6d6450); }
.cg-idle .cg-c{ font-weight:600; color:var(--muted,#6d6450); }
.cg-idle .cg-c.cg-gap{ background:rgba(200,140,40,.16); color:#8a6a2a; }
.cg-idle .cg-c.cg-overs{ background:#b3493a; color:#fff; font-weight:800; }
.cg-idle .cg-c.cg-overs.we, .cg tbody tr:hover .cg-c.cg-overs{ background:#b3493a; }
.cg tbody tr:hover .cg-c{ background:var(--cream,#F6EFE2); }
.cg tbody tr:hover .cg-c.cg-overs{ background:#fdecea; }

/* Câu trả lời NGẮN đứng trên lưới: mở thêm được bao nhiêu, lấy theo đêm ÍT NHẤT. */
/* Một DÒNG, không phải một khối: cái lịch mới là trang này. */
.cg-key{ margin:0 0 8px; padding:6px 10px; border-radius:6px; background:rgba(40,150,90,.10);
  border-left:3px solid #1c7a4a; }
.cg-key .cg-kchips{ margin:0; align-items:center; }
.cg-klab{ font-size:12px; color:#1c7a4a; margin-right:2px; }
.cg-ktight{ font-size:11.5px; color:var(--muted,#6d6450); }
.cg-ktight b{ color:var(--ink,#2a241d); font-variant-numeric:tabular-nums; }
.cg-key.flat{ background:rgba(120,120,120,.10); border-left-color:var(--muted,#6d6450); }
.cg-key.flat b{ color:var(--ink,#2a241d); }
.cg-key.bad{ background:#fdecea; border-left-color:#b3493a; }
.cg-key.bad b{ color:#b3493a; }
.cc-pause{ background:rgba(120,120,120,.12); }
.cc-pause small{ display:block; margin-top:3px; opacity:.75; font-size:12px; line-height:1.5; }
.avg-sold .avg-c{ color:var(--muted); font-size:12px; }
.avg-sold .avg-lab{ color:var(--muted); }

/* Bốn chip (Double/Twin/Premier/Fam) đo được 227px trong ô rộng 226px ở 402pt — hụt ĐÚNG
   1px nên chip cuối rớt xuống hàng hai, thẻ cao gấp rưỡi và lệch với cột số bên phải. */
@media (max-width:430px){
  .av-row{ gap:6px; }
  .av-chip{ padding:2px 6px; font-size:11.5px; gap:2px; }
  .av-chip b{ font-size:11.5px; }
  .av-chips{ gap:2px; }
}

/* KHÔNG có dải mờ báo-cuộn ở lưới ngang. Đã thử: bóng là NỀN của khung nên nó tối cả ô dữ
   liệu nằm dưới — soi ảnh render thấy cột cuối (02/10) nhạt hẳn so với các cột khác, tức đổi
   một tín hiệu điều hướng lấy việc làm mờ số thật. Ở đây không cần: lưới đã cuộn SNAP theo
   từng cột (không bao giờ có cột hiện nửa) và dải tháng ở đầu bảng cho thấy còn tiếp. Bảng
   cuộn ngang trên điện thoại — chỗ thật sự cần tín hiệu đó — đã bỏ hẳn, thay bằng thẻ. */

/* ĐIỆN THOẠI — mỗi đêm một THẺ. Bảng 10 cột của desktop nhét vào 402pt thì luôn tràn và cắt
   giữa con số; ở đây không có gì cuộn ngang. Dòng trên = đêm này thế nào, dòng dưới = ai
   đang bán và được bao nhiêu. Ba chip đo vừa 372px lòng trong của .cc-split ở 402pt. */
.cn-list{ display:flex; flex-direction:column; gap:6px; }
.cn-mon{ margin:10px 0 2px; font-size:12px; letter-spacing:.07em; text-transform:uppercase;
  color:var(--muted); font-weight:600; }
.cn-mon:first-child{ margin-top:0; }
.cn{ border:1px solid var(--line); border-radius:8px; padding:7px 9px 8px; background:var(--paper); }
.cn.we{ background:#faf6ec; }
.cn.full{ border-color:#e0b4ad; background:#fdf5f3; }
.cn-top{ display:grid; grid-template-columns:auto 1fr auto; align-items:center; gap:8px; }
.cn-d{ font-variant-numeric:tabular-nums; white-space:nowrap; }
.cn-d b{ font-size:13.5px; font-weight:700; }
.cn-d i{ font-style:normal; font-size:12px; color:var(--muted); }
.cn-s{ font-size:11.5px; color:var(--muted); white-space:nowrap; }
.cn-s b{ color:var(--ink); font-variant-numeric:tabular-nums; }
.cn-g{ display:flex; flex-direction:column; align-items:flex-end; line-height:1.05; }
.cn-g b{ font-size:14px; font-variant-numeric:tabular-nums; color:var(--muted); }
.cn-g i{ font-style:normal; font-size:10px; letter-spacing:.04em; text-transform:uppercase; color:var(--muted); }
.cn-g.cn-gap b{ color:#8a6a2a; }
.cn-g.cn-overs b{ color:#b3493a; font-weight:800; }
.cn-ch{ display:flex; gap:5px; margin-top:6px; }
.cn-c{ flex:1 1 0; min-width:0; display:flex; flex-direction:column; align-items:center;
  gap:1px; padding:4px 2px 5px; border:1px solid var(--line2); border-radius:6px;
  background:var(--card,#fff); }
.cn-c i{ font-style:normal; font-size:10.5px; letter-spacing:.05em; text-transform:uppercase;
  color:var(--muted); }
.cn-c b{ font-size:14px; font-weight:700; font-variant-numeric:tabular-nums; }
.cn-c i u{ display:block; text-decoration:none; font-size:10px; color:#b3493a; letter-spacing:0; }
.cn-p{ display:block; font-style:normal; font-size:12px; color:var(--muted);
  font-variant-numeric:tabular-nums; }
.cn-ro{ opacity:.68; }
.cn-win{ color:#166b40; font-weight:700; opacity:1; }
.cn-c.cn-shut{ background:#fbeae7; border-color:#e9c7c0; }
.cn-c.cn-shut b{ font-weight:600; font-size:11.5px; color:#b3493a; }
.cn-c.cn-pause b{ font-weight:500; font-size:11.5px; color:var(--muted); }
/* Không canh giữa: ô "tối thiểu 2 đêm" canh giữa thì nhãn BOOKING tụt thấp hơn TRIP/AGODA
   nửa dòng, hàng nhãn nhìn ra là lệch ngay. */
.cn-c.cn-na em{ margin-top:2px; font-size:11px; opacity:.7; text-align:center; line-height:1.2; }

/* Lịch gộp ĐỦ hạng (desktop): mỗi hạng một khối hàng, dưới cùng là khối "Cả nhà". Cao ~30 hàng
   nên khung phải khoá dòng ngày lại — cuộn dọc mà mất dải ngày thì đọc ô nào cũng đoán. */
.cg-wrap{ max-height:76vh; overflow:auto; }
.cg thead th{ position:sticky; z-index:3; }
.cg-months th{ top:0; height:22px; }
.cg-days th{ top:22px; }
.cg thead .cg-corner{ left:0; z-index:5; }
.cg-days .cg-corner{ background:var(--paper); }
/* Dải ngày DÍNH mà chưa bao giờ có NỀN — chỉ hai ô góc có. Cuộn dọc là chữ của hàng bên dưới
   chạy xuyên qua dải ngày (Kan 07/09/2026 chụp lại: "nó bị chồng khó nhìn"). Lỗi có sẵn từ
   khi dựng lưới, chỉ lộ ra khi lịch đủ dài để phải cuộn. Sắc cuối tuần trong dải ngày trước
   giờ THẬT RA là màu của hàng dữ liệu lọt lên — nay vẽ hẳn cho đúng, cùng tông với `.cg-c.we`. */
.cg-months th{ background:var(--cream,#F6EFE2); }
.cg-days th{ background:var(--paper,#fffdf8); }
.cg-days th.we{ background:#faf6ec; }
.cg-days th.cg-day:hover{ background:var(--cream,#F6EFE2); }
.cg-kind th, .cg-kind td{ border-bottom:1px solid var(--line); }
.cg-kindlab{ background:var(--cream) !important; font-size:12.5px; z-index:2; }
.cg-kindlab small{ display:block; font-weight:400; font-size:11px; color:var(--muted); }
/* Nội dung dải phải DÍNH khi kéo ngang, không thì cuộn vài cột là dải hạng trống trơn trở
   lại. `position:sticky` đặt trên chính <td colspan> không ăn (đo 06/09/2026: cuộn 1400px thì
   chip nằm ở x=−1247) — phải là một khối con bên trong. */
.cg-kindbar{ background:var(--cream); height:26px; padding:0; text-align:left; }
.cg-kbin{ position:sticky; left:134px; display:inline-flex; align-items:center; gap:2px;
  padding:0 12px; }
.cg-kindbar .cg-kchip{ padding:1px 8px; font-size:12px; border-radius:999px; margin-right:8px; }
.cg-kindbar .cg-kchip b{ font-size:12px; }
.cg-kindbar i{ font-style:normal; font-size:11.5px; color:var(--muted); }
/* 8px viền màu NỀN = khe trắng thật giữa hai khối (border-spacing không dùng được vì lưới
   đang border-collapse:separate với spacing 0 cho cả bảng), cộng một sợi kẻ mảnh bên trên. */
.cg-kind th, .cg-kind td{ border-top:8px solid var(--paper); box-shadow:inset 0 1px 0 var(--line); }
.cg-kind-tot .cg-kindlab, .cg-kind-tot .cg-kindbar{ background:#f0e7d4; }
.cg-tot .cg-c{ font-weight:700; color:var(--cognac); }
.cg-pct .cg-c{ font-size:12px; color:var(--muted); }
.cg-c.cg-nd{ color:var(--line); }
.cn-c.cn-nd{ flex:1; border-style:dashed; }
.cn-c.cn-nd em{ font-size:11.5px; color:var(--muted); text-align:center; }

/* Câu chốt: một con số cho MỖI hạng, tất cả lấy theo đêm ít nhất. */
.cg-kchips{ display:flex; flex-wrap:wrap; gap:6px; margin:5px 0 3px; }
.cg-key .cg-kchip{ padding:2px 9px; font-size:11.5px; }
.cg-kchip{ display:inline-flex; align-items:baseline; gap:5px; padding:3px 10px; border-radius:999px;
  background:var(--paper); border:1px solid var(--line); font-size:12px; color:var(--muted); }
.cg-kchip b{ font-size:13.5px; font-variant-numeric:tabular-nums; color:var(--ink); }
/* Chip ÂM = đang mời bán nhiều hơn phòng có thật ⇒ tô ĐẶC. Chip dương là tin tốt, để yên.
   Bản đầu làm ngược: chip "+2" tô xanh đặc át hết ba chip âm chỉ có viền nhạt. */
.cg-kchip.ok b{ color:#166b40; }
.cg-kchip.bad{ background:#b3493a; border-color:#8f3628; color:#fdece9; font-weight:600; }
.cg-kchip.bad b{ color:#fff; }
.av-more{ margin:10px 0 2px; padding:9px; text-align:center; font-size:12px; color:var(--muted);
  border:1px dashed var(--line); border-radius:8px; }

/* Chú giải ký hiệu: tách khỏi đoạn mở đầu (trước nhồi 6 ý vào 3 dòng, đọc không vào). */
.cg-legend{ display:flex; flex-wrap:wrap; gap:6px 16px; margin-top:9px; font-size:11.5px;
  color:var(--muted); align-items:center; }
.cg-legend span{ display:inline-flex; align-items:center; gap:5px; }
.cg-legend .lg{ width:15px; height:13px; border-radius:3px; border:1px solid var(--line); flex:none; }
.lg-shut{ background:#fbeae7; border-color:#e9c7c0; }
.cg-legend .lg-win{ color:#166b40; font-weight:700; font-variant-numeric:tabular-nums;
  font-size:11.5px; }
.lg-gap{ background:rgba(200,140,40,.16); border-color:#e6cf9f; }
/* Ô mẫu phải là MÀU THẬT trong lưới. Ô 'vượt tồn' đã đổi sang đỏ đặc mà mẫu còn hồng nhạt
   thì chú giải chỉ cho ĐÚNG cái nó không giải thích. */
.lg-over{ background:#b3493a; border-color:#8f3628; }
.lg-pause{ background:rgba(120,120,120,.10); }

/* Hai hộp TẠM DỪNG từng chiếm gần trọn màn hình đầu, đẩy lịch xuống dưới nếp gấp. Thu còn
   một dòng: cái cần biết ngay (kênh nào, tới bao giờ, chênh bao nhiêu) nằm trên dòng đó. */
.cc-pause > summary{ cursor:pointer; list-style:none; font-size:12.5px; }
.cc-pause > summary::-webkit-details-marker{ display:none; }
.cc-pause > summary::before{ content:'▸ '; opacity:.55; }
.cc-pause[open] > summary::before{ content:'▾ '; }
.cc-pause[open] > summary{ margin-bottom:5px; }

/* Tab Tối ưu = CÁI LỊCH. Chú giải và bảng tính giá sổ ra từ nút, không chiếm chỗ thường trực
   (Kan 06/09/2026: "mấy cái kia có thực sự xài nhiều đâu"). */
.cc-kinds button.cc-pop{ font-size:12px; padding:4px 11px; border-style:dashed;
  color:var(--muted); background:transparent; }
.cc-kinds button.cc-pop::before{ content:'⋯ '; opacity:.6; }
.cc-push{ flex:1 1 auto; }

.cg-legend-pop{ flex-direction:column; align-items:flex-start; gap:8px; font-size:13px; }
.cg-legend-pop span{ align-items:flex-start; }
.cg-legend-pop .lg{ margin-top:2px; }
/* Khối lịch tự cao hết phần còn lại của màn hình (JS đo tại chỗ) — bỏ trần vh đoán trước. */
.cg-wrap{ max-height:none; }
.cc-split{ padding:10px 12px 12px; }
@media (min-width:760px){
  .pane.av{ padding-left:14px; padding-right:14px; }
  .cc-split{ padding:10px 10px 12px; }
}
.cc-pause > summary{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.cc-pause > summary i{ font-style:normal; opacity:.75; }
.cc-pause-one{ margin-top:7px; padding-top:7px; border-top:1px dashed var(--line); }
.cc-pause-one > b{ display:block; margin-bottom:2px; }
@media (min-width:760px){ .pane.av{ padding-bottom:8px; } }

/* Chip "mở thêm an toàn" và dòng TẠM DỪNG chung MỘT dải — hai dải riêng ăn thêm 35px của
   chính cái lịch, ở 1280 là mất đúng một hạng phòng. */
.cg-bar{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-bottom:8px; }
.cg-bar .cg-key{ margin:0; flex:0 1 auto; }
.cg-bar .cc-pause{ margin:0; flex:1 1 260px; min-width:0; padding:6px 10px; }
.cg-ch-none .cg-c{ text-align:left; padding-left:10px; font-size:11.5px; font-style:italic; }

/* Câu phán quyết một dòng trên đầu tab Tối ưu (Kan 07/09/2026: không muốn job canh định kỳ,
   "lâu lâu vào kiểm tra th" ⇒ mở tab ra là phải TỰ trả lời, đừng bắt đọc từng chip rồi tự cộng). */
.cc-verdict {
  margin: 6px 0 8px; padding: 7px 11px; border-radius: 7px; font-size: 13px; line-height: 1.5;
  border: 1px solid var(--line); background: var(--card);
}
.cc-verdict b { font-weight: 650; }
.cc-verdict.bad { border-color: #e6b4b4; background: #fdf3f3; color: #7d2020; }
@media (prefers-color-scheme: dark) {
  .cc-verdict.bad { border-color: #6b3030; background: #2a1616; color: #f0b8b8; }
}
.cc-verdict .cc-vsoft { opacity: .85; }

/* Thu/mở từng hạng phòng (Kan 07/09/2026: "trong lịch bấm thu mở từng loại phòng được thì
   tối ưu hơn"). Bảng cần 1.209px trong khung 626px — thu hạng không cần xem là cách duy nhất
   lấy lại chỗ mà KHÔNG bỏ bớt thông tin. */
.cg-kind[data-fold]{ cursor:pointer; }
.cg-kind[data-fold]:hover .cg-kindlab{ background:#f3ead7 !important; }
.cg-caret{ display:inline-block; width:12px; margin-right:4px; font-size:12px; vertical-align:1px;
           color:var(--gold,#9a7b3f); font-weight:700; }
.cg-kind[data-fold]:hover .cg-caret{ color:var(--ink,#2a241d); }
.cg-shutkind .cg-kindlab{ opacity:.82; }
.cg-shutkind th, .cg-shutkind td{ border-bottom:1px solid var(--line); }

/* Phán quyết khi KHÔNG có sự cố: một chip trong thanh, không phải dải ngang 44px riêng. */
.cg-vchip{ display:inline-flex; align-items:baseline; gap:6px; padding:6px 11px; border-radius:6px;
           font-size:12px; border:1px solid #b7dcc0; background:#f2faf4; color:#1f4d2e;
           border-left:3px solid #2f8a52; }
.cg-vchip b{ font-weight:650; }
.cg-vchip i{ font-style:normal; font-size:12px; opacity:.75; }
.cg-vchip.warn{ border-color:#e0c69a; background:#fdf8ef; color:#6b4a12; border-left-color:#c69a3a; }
@media (prefers-color-scheme: dark){
  .cg-kind[data-fold]:hover .cg-kindlab{ background:#2a2419 !important; }
  .cg-vchip{ border-color:#2c5738; background:#14251a; color:#b8e3c4; border-left-color:#3f9a62; }
  .cg-vchip.warn{ border-color:#6a5526; background:#262013; color:#e8cf9a; border-left-color:#c69a3a; }
}
/* Tiêu đề nhập vào hàng nút trên máy tính — giữ chữ, trả lại ~42px cho lịch (Kan 07/09/2026:
   "khung trên dưới lại thiếu hiển thị"). */
.cc-hin{ font-family:var(--serif, Georgia, serif); font-size:14.5px; font-weight:600;
         color:var(--ink,#2a241d); margin-right:6px; white-space:nowrap; }
/* Danh sách kênh đang dừng: tóm tắt CHỈ tên kênh + mức chênh, ngày tháng bung ra khi bấm
   (Kan 07/09/2026: "tối ưu cái này ngắn gọn lại"). Trước đây cả hai khoảng ngày nằm trong
   summary nên dòng này dài gần một màn, đẩy chính nó xuống hàng riêng. */
.cc-pause summary i{ font-style:normal; font-size:12px; color:var(--muted,#6d6450); }
.cc-pause-heads{ margin:6px 0 2px; font-size:12px; line-height:1.7; }
.cg-ktight i{ font-style:normal; opacity:.75; }

/* ---- Chat WhatsApp (Kan 18/09/2026) ---------------------------------------------
   Đọc-để-hiểu, không phải hộp thư trả lời: trang này KHÔNG gửi được tin (token gửi chỉ
   nằm trên Mac mini). Nên bố cục ưu tiên ĐỌC NHANH — ai đang chờ, chờ bao lâu, nói gì. */
/* Nút hộp thư nằm ở THANH TIÊU ĐỀ, không nằm trong thanh tab cuộn ngang — lý do đo được
   ghi ở index.html. `.rf-btn` cho sẵn hình tròn 36px; chỉ thêm chỗ neo cho huy hiệu. */
.chat-btn{ position:relative; margin-left:auto; }
.chat-btn ~ .rf-btn{ margin-left:6px; }        /* hai margin auto liền nhau sẽ chia đôi khoảng hở */
.chat-btn.on{ background:var(--cognac); border-color:var(--cognac); color:#fff; }
.mbadge{ position:absolute; top:-5px; right:-4px; min-width:17px; padding:0 4px; border-radius:9px;
  background:#c0392b; color:#fff; font-size:11.5px; font-weight:700; line-height:17px;
  text-align:center; box-shadow:0 0 0 2px var(--paper); }

/* Dải tổng kết DÙNG LẠI `.daystat` (xem markup), không chép lại kích thước sang đây —
   bản đầu chép tay và đã lệch sẵn (13px vs 12px), tức sửa một bên là hai bên trôi khỏi nhau. */
.daystat .su.due, .daystat .su.due b{ color:#bf5b4b; }

/* Tiêu đề nhóm: cùng lối với tiêu đề mục trong báo cáo (chữ hoa, giãn chữ, xám) — các màn
   khác đều chia nhóm, danh sách trôi nổi không nhóm là thứ làm màn này trông trống. */
.ch-grp{ margin:14px 0 7px; font-size:11.5px; letter-spacing:2px; text-transform:uppercase;
  color:var(--muted); font-weight:600; }
.ch-grp:first-child{ margin-top:0; }

.ch-list{ display:flex; flex-direction:column; gap:8px; }
.ch-row{ border:1px solid var(--line); background:var(--paper); border-radius:12px;
  padding:11px 13px 12px; box-shadow:var(--shadow); cursor:pointer; }
.ch-row:hover{ border-color:var(--cognac); }
.ch-row.wait{ border-left:3px solid #bf5b4b; }
.ch-head{ display:flex; align-items:baseline; gap:8px; }
/* Tên = khoá quét chính ⇒ cùng cỡ với tên khách ở màn Bảng/Theo ngày (16px), không nhỏ hơn. */
.ch-who{ font-weight:600; font-size:16px; line-height:1.3; min-width:0; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; }
.ch-num{ font-size:12px; color:var(--muted); white-space:nowrap; min-width:0; overflow:hidden; text-overflow:ellipsis; }
.ch-when{ margin-left:auto; padding-left:10px; font-size:12px; color:var(--muted); white-space:nowrap; }
/* Chip SỐ PHÒNG của khách đang ở — ghép từ số máy trong đơn (xem chGuest trong app.js).
   Chỉ hiện khi khớp CHẮC CHẮN; hội thoại không khớp thì không có chip nào, vì một chip
   "không rõ" ở mọi hàng chỉ dạy người ta thôi nhìn chip.
   Đặt NGAY SAU tên và TRƯỚC `.ch-when` (cái này `margin-left:auto` đẩy sang phải), nên chip
   dính liền tên — đọc một mạch "Oleg 402" thay vì mắt phải nhảy ngang qua khoảng trống. */
.ch-room{ flex:0 0 auto; font-size:12px; font-weight:600; letter-spacing:.02em;
  padding:1px 7px; border-radius:999px; white-space:nowrap;
  color:var(--ink); background:var(--paper); border:1px solid var(--cognac); }
.ch-room.unas{ color:#8a5a3b; background:#fdf0e4; border-color:#e0a878; }
/* Ba trạng thái, ba mức NỔI khác nhau — và thứ tự nổi là thứ tự cần nhìn. Số phòng của
   khách ĐANG Ở là thứ lễ tân hành động ngay nên giữ viền cognac; "đến dd/mm" nhạt hơn;
   "đã ở …" nhạt nhất, vì đó chỉ là ngữ cảnh, không có việc gì phải làm.
   ⚠️ KHÔNG để màu chở nghĩa một mình: mỗi trạng thái có CHỮ riêng (số phòng trần · "đến …"
   · "đã ở …"), nên ngoài nắng hay mắt kém vẫn đọc ra, không phải đoán theo sắc độ. */
.ch-room.soon{ color:var(--muted); background:var(--paper); border-color:var(--line); font-weight:500; }
.ch-room.past{ color:var(--muted); background:transparent; border-color:var(--line);
  font-weight:400; opacity:.85; }
/* "502?" — the guest NAMED the room, the number is not on the booking yet (Khoa 05/10/2026): dashed, in the unassigned
   amber, the "?" in the text carries the meaning, not the colour */
.ch-room.guess{ color:#8a5a3b; background:#fdf0e4; border:1px dashed #e0a878; }
.ch-bar .ch-room{ align-self:center; }

.ch-prev{ margin-top:4px; font-size:13px; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ch-prev b{ color:var(--ink); font-weight:600; }

/* Trạng thái dùng lại chính `.st` của màn Bảng (markup ghi `class="st ch-win …"`), ở đây
   chỉ khai MÀU. Trạng thái bình thường (còn giờ) để trung tính — chỉ tô cái cần người ta
   dừng lại, tô hết thì màu mất giá.
   ⚠️ Nền phải là `--paper`, KHÔNG phải `--cream`: cream trùng đúng nền trang nên ở header
   hội thoại cái pill TÀNG HÌNH, đọc ra chữ xám trần. Thêm viền để nó là pill ở mọi nền. */
.ch-win{ display:inline-block; margin-top:7px;
  background:var(--paper); border:1px solid var(--line); color:var(--muted); }
.ch-win.shut{ background:#f7e6e2; border-color:#e8c9c1; color:#a8493a; }
.ch-win.unk{ font-weight:500; font-style:italic; }

/* Hội thoại: bong bóng hai chiều, khách bên trái, mình bên phải — cùng quy ước với mọi
   app nhắn tin, để không ai phải học lại cách đọc. */
/* Thanh tên DÍNH TRÊN: cuộn giữa chừng một hội thoại dài mà mất tên khách lẫn trạng thái
   24h thì không biết mình đang nói với ai, và cửa sổ 24h là thứ quyết định có gửi được hay
   không. `top` khớp chiều cao khối header dính sẵn của app. */
/* `top` phải TRỪ chiều cao khối header dính sẵn của app (logo + thanh tab), không phải 0.
   Đo 18/09: dính ở 0 thì thanh tên nằm TRỌN dưới header (header cao 100px, thanh tên
   0..51px) — vẫn "trong khung nhìn" nên phép đo cũ báo xanh, nhưng mắt không thấy gì.
   Chiều cao đó đổi theo khổ màn nên `syncStickyTops()` đo lúc chạy và publish
   `--bar-top` / `--chbar-top` — DÙNG LẠI biến sẵn có của app, đừng đẻ biến mới. */
.ch-bar{ display:flex; align-items:center; gap:9px; position:sticky; z-index:5;
  top:var(--bar-top, 99px); margin:0 -14px; padding:5px 14px; background:var(--cream);
  border-bottom:1px solid var(--line); }
.ch-back{ flex:0 0 auto; white-space:nowrap; border:1px solid var(--line); background:#fff;
  border-radius:18px; min-height:44px; padding:0 13px; font:inherit; font-size:13px; cursor:pointer; }
.ch-bar{ flex-wrap:wrap; }
/* `flex-basis:0` chứ KHÔNG phải `auto`: với `flex-wrap:wrap` trình duyệt xếp dòng theo
   cỡ GỐC của item rồi mới co — để `auto` thì tên dài giữ nguyên 163px, đẩy "số máy"
   xuống dòng hai (đo được 108px). Về 0 thì tên nhận phần còn lại và tự cắt bằng "…". */
.ch-bar .ch-who{ flex:1 1 0; font-size:15px; }
/* The NAME keeps at least 56 px (27/09/2026: it was squeezed to 22–30 px, "O…"). When the row really can't fit,
   the last items wrap to a second row instead of eating the name. */
.ch-bar .ch-who{ min-width:56px; }
/* The name doubles as the "show phone number" button (27/09/2026) — looks like the name, dotted underline hints tap. */
.ch-bar .ch-whobtn{ flex:1 1 0; min-width:56px; min-height:44px; display:flex; align-items:center; padding:0;
  border:0; background:none; color:inherit; font:inherit; text-align:left; cursor:pointer; }
.ch-bar .ch-whobtn .ch-who{ flex:1 1 auto; min-width:0; text-decoration:underline dotted var(--line); text-underline-offset:4px; }
/* Header look, variant A of 3 rendered side by side at 402px (27/09/2026). Khoa: "2 cái nút to quá chữ bé kèm dòng kia
   bé". The 44px TOUCH height stays (chat_ui checks it); only the visible frame goes — text carries the weight, like
   iOS navigation. B (32px pills) and C (bigger text in 44px pills) both wrapped "Bỏ đánh dấu" to a second row. */
.ch-bar .ch-back{ border:0; background:none; padding:0 4px 0 0; font-size:16px; color:#3b7bc8; }
.ch-bar .ch-who{ font-size:16px; }
.ch-bar .ch-done{ border:0; background:none; padding:0 2px; font-size:14px; }
.ch-bar .ch-done.undo{ border:0; font-size:13.5px; }
.ch-bar .ch-room{ font-size:12.5px; padding:3px 9px; }
.ch-sub{ flex:1 1 100%; margin-top:3px; font-size:12.5px; color:var(--cognac);
  font-weight:600; text-decoration:none; letter-spacing:.02em; }
/* Chip "đã xử" sống trong thanh tên, không còn dải riêng — xem lý do ở renderChat. */
.ch-done{ flex:0 0 auto; margin:0 0 0 6px; border:1px solid var(--cognac);
  background:var(--paper); color:var(--cognac); border-radius:20px; min-height:44px;
  padding:0 11px; font:inherit; font-size:11.5px; font-weight:600; white-space:nowrap; cursor:pointer; }
.ch-done:disabled{ opacity:.55; cursor:default; }
.ch-done.bad{ border-color:#bf5b4b; color:#bf5b4b; }
/* Trạng thái HOÀN TÁC: cùng một nút, nhưng phải nhìn là biết nó không còn là hành động chính.
   Nhạt đi bằng nét đứt + chữ mờ hơn, KHÔNG đổi màu — màu trong bảng này Khoa đã chốt. */
/* Nút soạn nháp AI: cùng dáng nút Gửi nhưng VIỀN RỖNG — nó không phải hành động chính,
   và nhầm hai nút này là gửi cho khách một câu chưa ai đọc lại. */
.ch-ai{ flex:0 0 auto; min-height:44px; padding:0 12px; border-radius:20px; font:inherit;
  font-size:12.5px; font-weight:600; white-space:nowrap; cursor:pointer;
  border:1px solid var(--cognac); background:var(--paper); color:var(--cognac); }
.ch-ai:disabled{ opacity:.5; cursor:default; }
.ch-done.undo{ border-style:dashed; color:var(--muted); border-color:var(--muted);
  font-weight:500; }
/* Đệm đáy = chỗ ô soạn chiếm. Thiếu nó thì ô soạn (sticky) đè lên bong bóng CUỐI và câu
   trả lời mới nhất bị cắt ngang — đúng cái Kan chụp lại ngày 18/09. Chú thích ở `.ch-comp`
   từng nói "đã chừa đệm" trong khi chưa hề chừa. */
.ch-thread{ display:flex; flex-direction:column; gap:7px; margin-top:12px; padding-bottom:12px; }
/* Nhóm một ngày. Vạch ngày dính TRONG nhóm này, nên nó trôi đi cùng ngày của nó thay vì
   chồng lên vạch ngày sau — xem renderChat. Bố cục y hệt `.ch-thread` để bong bóng bên
   trong vẫn canh trái/phải như cũ. */
.ch-daygrp{ display:flex; flex-direction:column; gap:7px; }
.ch-msg{ max-width:78%; padding:8px 11px; border-radius:13px; font-size:13.5px; line-height:1.45; white-space:pre-wrap; overflow-wrap:anywhere; }
.ch-msg.in{ align-self:flex-start; background:var(--paper); border:1px solid var(--line); border-bottom-left-radius:4px; }
.ch-msg.out{ align-self:flex-end; background:var(--cognac); color:#fff; border-bottom-right-radius:4px; }
/* Bản dịch: cùng bong bóng, tách bằng vạch mảnh — đủ để mắt biết đây không phải lời
   khách viết, mà không đẩy nó thành một khối riêng làm hội thoại dài gấp đôi. */
.ch-vi{ margin-top:6px; padding-top:5px; border-top:1px solid rgba(0,0,0,.09);
  font-size:12.5px; line-height:1.45; opacity:.82; font-style:italic; }
.ch-msg.out .ch-vi{ border-top-color:rgba(255,255,255,.28); }
/* Giờ nằm GÓC DƯỚI PHẢI, trôi cùng dòng chữ cuối nếu còn chỗ — đúng lối WhatsApp, và
   tiết kiệm nguyên một dòng cho MỖI tin so với bản cũ để nó thành khối riêng. */
.ch-msg .t{ float:right; margin:3px 0 0 10px; font-size:11.5px; opacity:.72; }
/* italic like its translation (Khoa preferred it), but its box is widened: the italic last digit overhangs the box and
   Safari on the iPhone clipped it under the block's opacity ("16:4|0", 10/10/2026) */
.ch-vi .t{ padding-right:3px; margin-right:-1px; }
/* Read = the same white ticks at full strength; delivered/sent stay at the time's .72 (Khoa 10/10/2026 chose "A" from
   4 options rendered side by side: no new colour — blue on a white chip "nhìn ghê", light blue vanished on DN cognac). */
.ch-tick{ margin-left:4px; letter-spacing:-3px; padding-right:3px; font-weight:700; }
.ch-msg .t:has(.ch-tick.read), .ch-msg .t:has(.ch-tick.failed){ opacity:1; }
.ch-msg .t:has(.ch-tick:not(.read):not(.failed)){ opacity:.6; }
.ch-tick.failed{ letter-spacing:0; cursor:help; }
/* Trên nền cognac, trắng 72% ra #C9DFDB = 2,8:1 — dưới ngưỡng. Mở hết opacity được 3,9:1.
   KHÔNG tới 4,5:1 được nếu không đổi màu nền, mà nền là màu thương hiệu Khoa đã chọn —
   nên đây là hết mức làm được ở phía chữ, ghi ra để sau này khỏi tưởng đã đạt chuẩn. */
.ch-msg.out .t{ opacity:1; }
/* Zalo look (Khoa 16/10/2026, option A of 4 rendered at 402 px): ours light blue, theirs white, as Zalo itself — the PQ
   green bubble hid Zalo's red/green text. Zalo only (.zl on the thread); WhatsApp keeps the hotel colour. Ticks go dark
   on the light bubble: the white ones (10/10) only work on a coloured one. */
.ch-thread.zl .ch-msg.out{ background:#E5EFFF; color:#081B3A; border:1px solid #cfdcf5; }
.ch-thread.zl .ch-msg.in{ background:#fff; }
.ch-thread.zl .ch-msg.out .t, .ch-thread.zl .ch-msg.out .ch-tick{ color:#5a6b85; }
.ch-thread.zl .ch-msg.out .ch-vi{ border-top-color:rgba(8,27,58,.12); }
.ch-thread.zl .ch-msg.out .ch-orig{ color:#1a5fb4; }
.ch-thread.zl .ch-msg.out .ch-map, .ch-thread.zl .ch-msg.out .ch-file{ background:#fff; }
.zf-b{ font-weight:700; } .zf-i{ font-style:italic; } .zf-s{ text-decoration:line-through; } .zf-u{ text-decoration:underline; }
.zf-s.zf-u{ text-decoration:underline line-through; }
.zf-do{ color:rgb(219,52,46); } .zf-cam{ color:rgb(242,120,6); } .zf-vang{ color:rgb(196,140,0); } .zf-xanhla{ color:rgb(21,168,95); }
.zf-den{ color:rgb(8,27,58); }
.zf-to{ font-size:15.5px; } .zf-nho{ font-size:12px; }
.ch-msg::after{ content:''; display:block; clear:both; }
.ch-car{ display:block; margin:7px 0 2px; font:inherit; font-size:12.5px; line-height:1.2; white-space:normal; }
button.ch-car{ min-height:36px; padding:0 12px; border-radius:18px; border:1px solid var(--cognac); background:#fff; color:#7a5a36;
  font-weight:600; cursor:pointer; }
button.ch-car:active{ background:var(--cream); }
button.ch-car.fail{ border-color:#c0392b; color:#a5281b; }
.ch-car.req{ color:var(--muted); }
.ch-car.done{ color:#2e7d32; font-weight:600; }
.car-cap{ margin:12px 0 6px; font-size:12px; color:var(--muted); }
.car-prev{ padding:10px 12px; border-radius:12px; background:var(--cream); border:1px solid var(--line); font-size:14px; line-height:1.45; }
.car-prev .z-do{ color:#d32f2f; } .car-prev .z-xanhla{ color:#2e7d32; }
.car-warn{ margin:8px 0 0; font-size:12.5px; color:#a5281b; }
.car-sheet .car-note{ margin-top:4px; }
.car-note-add{ display:block; margin:2px 0 6px; padding:8px 2px; border:0; background:none; font:inherit; color:var(--cognac); }
.car-sheet .car-prev{ margin-top:6px; }
.ch-msg.has-react{ position:relative; margin-bottom:15px; }
.ch-react{ position:absolute; bottom:-16px; left:8px; padding:2px 6px; border-radius:999px; font-size:16px; line-height:1.2;
  background:var(--paper); color:var(--ink); border:1px solid var(--line); box-shadow:0 1px 2px rgba(0,0,0,.08); }
.ch-msg.out .ch-react{ left:auto; right:8px; }
/* A Zalo reaction the Mac could not click (the message scrolled away, the text matched twice…) — said, never silent. */
.ch-rerr{ margin-top:4px; font-size:12px; color:#bf5b4b; }
.ch-msg.in, .ch-msg.hold{ cursor:pointer; -webkit-user-select:none; user-select:none; -webkit-touch-callout:none;
  transition:transform .18s ease; }
.ch-msg.in.press, .ch-msg.hold.press{ transform:scale(.96); }
.ch-msg.reacting{ position:relative; z-index:31; }
.ch-rback{ position:fixed; inset:0; z-index:30; background:rgba(28,22,16,.16); animation:ch-fadein .18s ease-out; }
.ch-rback.out{ animation:ch-fadeout .13s ease-in forwards; }
@keyframes ch-fadein{ from{ opacity:0; } }
@keyframes ch-fadeout{ to{ opacity:0; } }
.ch-msg .ch-rbar{ position:absolute; left:-6px; bottom:calc(100% + 8px); margin:0; white-space:nowrap; width:max-content;
  transform-origin:24px 100%; animation:ch-rpop .26s cubic-bezier(.2,.9,.25,1.2); }
.ch-msg .ch-rbar.below{ bottom:auto; top:calc(100% + 8px); transform-origin:24px 0; }
.ch-msg.out .ch-rbar{ left:auto; right:-6px; transform-origin:calc(100% - 24px) 100%; }   /* our line sits on the right */
.ch-msg.out .ch-rbar.below{ transform-origin:calc(100% - 24px) 0; }
.ch-msg .ch-rbar.out{ animation:ch-rgone .13s ease-in forwards; }
@keyframes ch-rpop{ from{ opacity:0; transform:scale(.55) translateY(8px); } to{ opacity:1; transform:none; } }
@keyframes ch-rgone{ to{ opacity:0; transform:scale(.85); } }
.ch-react.pop{ animation:ch-reactpop .34s cubic-bezier(.2,.9,.25,1.4); }
@keyframes ch-reactpop{ 0%{ transform:scale(0); } 70%{ transform:scale(1.25); } 100%{ transform:scale(1); } }
.ch-rcopy{ min-height:40px; margin-left:4px; padding:0 12px; border:0; border-left:1px solid var(--line); background:transparent;
  font:inherit; font-size:14px; color:var(--ink); white-space:nowrap; }
.ch-rbar{ display:flex; align-items:center; gap:2px; padding:4px 6px; border-radius:999px;
  background:color-mix(in srgb, var(--paper) 88%, transparent); -webkit-backdrop-filter:blur(14px); backdrop-filter:blur(14px);
  border:1px solid var(--line); box-shadow:0 6px 22px rgba(0,0,0,.16); }
.ch-rpick{ width:38px; height:40px; padding:0; border:0; border-radius:999px; background:transparent;
  font-size:24px; line-height:1; display:grid; place-items:center; }
.ch-rpick:active{ transform:scale(1.18); }
.ch-rpick.on{ background:var(--line); }
/* Tin liên tiếp cùng người gửi: thu khoảng cách và bo lại góc phía trong, để một lượt nói
   nhiều câu đọc ra thành MỘT khối chứ không phải nhiều lượt rời rạc. */
.ch-msg.grp{ margin-top:-4px; }
.ch-msg.in.grp{ border-top-left-radius:5px; }
.ch-msg.out.grp{ border-top-right-radius:5px; }

/* Vạch ngày, kiểu WhatsApp: chip nhỏ canh giữa. Không có nó thì hội thoại dài trôi thành
   một dải liền, không biết câu nào hôm nay câu nào tuần trước. */
/* Vạch ngày DÍNH. Bắt buộc, không phải trang trí: giờ trong bong bóng nay chỉ còn HH:MM
   (ngày đã giao cho vạch này), nên nó trôi mất là cuộn giữa luồng dài không còn mốc ngày.
   HÌNH DẠNG theo tiền lệ `.mdiv` (vạch tháng ở danh sách booking) chứ không tự chế: DẢI KÍN
   full-width, nền cream, chữ hoa giãn. Bản đầu làm viên pill nhỏ canh giữa và nó ĐÈ lên
   bong bóng 6–9px khi dính (Kan chụp 18/09) — pill nổi trên chữ đọc ra như lỗi, còn dải kín
   thì tin nhắn trôi qua dưới nó sạch sẽ. Tiền lệ có sẵn tránh đúng cái bẫy này. */
/* A small centred pill, not a full-width band (Khoa 27/09/2026: the band "cắt ngang luôn tạo thành thêm 1 ô").
   Still STICKY — the day must stay visible mid-thread (Kan 18/09). An opaque pill with a soft shadow reads as a floating
   label (WhatsApp) rather than text overlapping by mistake, which is what the 18/09 pill looked like. */
.ch-day{ position:sticky; z-index:4; top:calc(var(--chbar-top, 152px) + 8px); align-self:center; transition:opacity .25s;
  box-shadow:0 1px 3px rgba(40,30,20,.14);
  margin:10px auto 6px; padding:3px 11px; border:1px solid var(--line); border-radius:999px;
  background:var(--paper); color:var(--muted); font-size:12px; font-weight:500; text-align:center; }
.ch-day.stuck{ opacity:0; }
body.ch-scrolling .ch-day.stuck{ opacity:1; }
.ch-img{ display:block; width:100%; max-width:260px; height:auto; border-radius:9px;
  margin:0 0 6px; background:var(--cream); }
/* An album: small squares like Zalo's grid (10 full-width photos made a column longer than the screen). */
.ph-grid{ display:grid; grid-template-columns:repeat(3, 1fr); gap:3px; max-width:260px; margin:0 0 6px; }
.ph-grid.n2{ grid-template-columns:1fr 1fr; }
.ph-grid .ch-img{ width:100%; aspect-ratio:1; object-fit:cover; margin:0; border-radius:6px; cursor:zoom-in; }
.ph-big{ position:fixed; inset:0; z-index:200; display:flex; align-items:center; justify-content:center;
  background:rgba(0,0,0,.92); }
.ph-big img{ max-width:100%; max-height:100%; }
.ch-img-cho{ width:180px; height:120px; border-radius:9px; margin:0 0 6px;
  background:var(--cream); color:var(--muted); font-size:11.5px;
  display:flex; align-items:center; justify-content:center; }
/* Vùng chạm 44px NHƯNG không được biến thành khoảng trống. Cái hộp 44px quanh dòng chữ
   11,5px tự chừa ~15px trên/dưới; soi ảnh thấy bong bóng ảnh loãng hẳn ra (khe 28px và
   27px, cái giờ trôi lơ lửng). Lề ÂM kéo phần nhìn thấy sát lại mà hộp vẫn 44px — HIG
   nói về vùng chạm, không bắt phải chừa ngần ấy chỗ trống.
   `width:fit-content` để hộp chỉ rộng bằng chữ: `display:flex` trần là block trải hết
   bề ngang, cộng lề âm đáy thì nó trùm lên cái giờ ở góc phải, chạm vào giờ lại mở Drive. */
.ch-orig{ display:flex; width:fit-content; align-items:center; min-height:44px;
  margin:-9px 0 -11px; font-size:11.5px; font-weight:600;
  color:var(--cognac); text-decoration:underline dotted; text-underline-offset:3px; }
.ch-msg.out .ch-orig{ color:#fff; }
.ch-msg.pend{ opacity:.6; }
/* Tin MỚI trôi vào — chuyển động DUY NHẤT của màn này, và nó có việc: trong một hộp thư
   sống, tin khách tới giữa lúc đang đọc mà hiện ra lặng lẽ thì rất dễ trôi qua mắt. Chỉ
   chạy cho tin CHƯA TỪNG vẽ (xem CHAT.seen), nên một lượt vẽ lại nền không làm cả luồng
   nhấp nháy. Nhịp .18s ease là nhịp app đang dùng nhiều nhất, không tự chế nhịp mới. */
@keyframes ch-in { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:none; } }
.ch-msg.in-new{ animation:ch-in .18s ease both; }
/* KHÔNG viền đứt: trong app này `dashed` đang mang nghĩa "phòng TRỐNG" (.droom.free),
   mượn lại cho ghi chú là làm lẫn nghĩa. */
.ch-note{ margin:14px 0 0; font-size:11.5px; color:var(--muted); text-align:center; line-height:1.6; }

/* Khung hội thoại CĂNG HẾT chiều cao còn lại, để ô soạn luôn nằm ở ĐÁY kể cả khi luồng
   chỉ có hai tin. `sticky` một mình không đủ: nó chỉ ghim khi nội dung TRÀN màn, còn luồng
   ngắn thì nó đậu đúng chỗ tự nhiên — tức trôi lên giữa màn, chừa một mảng kem trống bên
   dưới (Kan chụp lại 18/09, luồng +44 có 2 tin).
   `dvh` để không bị thanh địa chỉ của Safari iOS ăn mất một khúc; `vh` là đường lùi. */
.ch-pane{ display:flex; flex-direction:column;
  min-height:calc(100vh - var(--top-h, 99px));
  min-height:calc(100dvh - var(--top-h, 99px)); padding-bottom:0; }
.ch-pane .ch-thread{ flex:1 1 auto; }     /* khoảng trống dồn vào giữa, đẩy ô soạn xuống đáy */

/* Ô soạn trả lời. Dính đáy màn hình để ngón tay luôn với tới mà không phải cuộn hết hội
   thoại — và `.ch-thread` chừa đệm đáy để nó không che mất bong bóng cuối. */
.ch-comp{ position:sticky; bottom:0; margin:12px -14px 0; padding:9px 14px 10px;
  background:var(--cream); border-top:1px solid var(--line); }
.ch-comprow{ display:flex; align-items:flex-end; gap:8px; }
.ch-ta{ flex:1 1 auto; min-width:0; resize:none; font:inherit; font-size:14px; line-height:1.45;
  color:var(--ink); background:var(--paper); border:1px solid var(--line); border-radius:14px;
  padding:9px 12px; max-height:132px; overflow-y:auto; }
.ch-ta:focus{ outline:none; border-color:var(--cognac); }
/* iPhone Safari zooms the WHOLE page when an input under 16px gets focus (Khoa's screenshot 26/09/2026: bubbles
   cut off, send button off screen). 16px on phones only; desktop keeps 14px. */
@media (max-width:759px){ .ch-ta{ font-size:16px; line-height:1.3; padding-top:8px; padding-bottom:8px; } }
/* Same zoom trap on the date/month pickers (live sweep 26/09/2026 at 402px: #day-input, #tape-start, #rep-month
   were the only other controls under 16px). */
@media (max-width:759px){ .ctrlbar input[type=date], .ctrlbar input[type=month], #rep-month{ font-size:16px; } }
.ch-ta:disabled{ opacity:.6; }
.ch-send{ flex:0 0 auto; border:0; background:var(--cognac); color:#fff; border-radius:20px;
  min-height:44px; padding:0 18px; font:inherit; font-size:13.5px; font-weight:600; cursor:pointer; }
.ch-send:disabled{ opacity:.4; cursor:default; }
/* 😊 + its emoji panel (Zalo's composer) — tap targets 44 px */
.ch-emo{ flex:0 0 auto; width:44px; height:44px; border:0; border-radius:50%; background:transparent; font-size:24px;
  line-height:1; cursor:pointer; filter:grayscale(.35); }
.ch-emo.on{ filter:none; background:color-mix(in srgb, var(--cognac) 14%, transparent); }
.ch-emopanel{ display:grid; grid-template-columns:repeat(8, 1fr); gap:2px; margin:0 0 8px; padding:6px;
  border-radius:14px; background:var(--paper); border:1px solid var(--line); max-height:200px; overflow-y:auto; }
.ch-emopanel[hidden]{ display:none; }
.ch-emopanel button{ height:44px; border:0; border-radius:10px; background:transparent; font-size:24px; line-height:1;
  cursor:pointer; }
.ch-emopanel button:active{ background:color-mix(in srgb, var(--cognac) 14%, transparent); }
/* Dòng trấn an: cần LẦN ĐẦU, thành rác từ lần thứ hai. Chỉ hiện khi đang thật sự có tin
   nằm trong hàng đợi — lúc đó nó trả lời đúng câu người ta đang thắc mắc ("gửi chưa?"). */
.ch-hint{ margin:7px 0 0; font-size:12px; color:var(--muted); text-align:center; }
.ch-senderr{ margin:7px 0 0; font-size:12px; color:#bf5b4b; }
/* Tin ĐÃ gửi nhưng một lượt ghi sổ hụt — vàng (cần xử), không đỏ (không phải hỏng). */
.ch-hold{ margin:7px 0 0; }
/* 💡 suggestion + 📋 summary (WA AI plan 27/09/2026, step 4) */
.ch-sug{ display:flex; gap:8px; align-items:flex-start; margin:8px 0 4px; padding:8px 10px; border-radius:10px;
  background:var(--paper); border:1px dashed var(--line); }
.ch-sugtext{ flex:1; min-width:0; font-size:14px; line-height:1.4; white-space:pre-wrap; }
.ch-suguse{ flex:none; min-height:44px; padding:0 12px; border-radius:8px; border:1px solid var(--line);
  background:#fff; color:var(--ink); font:inherit; font-size:13px; }
.ch-sum{ margin:0 0 6px; padding:0 2px; }
.ch-sumhead{ min-height:44px; background:none; border:0; padding:0 4px; color:var(--muted); font:inherit; font-size:13px; }
.ch-sumtext{ font-size:13px; line-height:1.45; white-space:pre-wrap; padding:6px 8px; border-radius:8px; background:var(--paper); }
.ch-sumta{ width:100%; box-sizing:border-box; font:inherit; font-size:14px; padding:8px; border-radius:8px;
  border:1px solid var(--line); background:#fff; color:var(--ink); }
.ch-sumrow{ display:flex; gap:8px; margin-top:6px; }
.ch-sumbtn{ min-height:44px; padding:0 14px; border-radius:8px; border:1px solid var(--line); background:var(--paper);
  color:var(--ink); font:inherit; font-size:13px; margin-top:4px; }
.ch-sumbtn.ghost{ background:none; }
.ch-sumerr{ color:#b3493a; font-size:13px; margin:4px 0 0; }
.ch-send.held{ background:#8a6a1f; }   /* held: same button, warning ink = the warning box below */
.ch-sendwarn{ margin:7px 0 0; font-size:12px; line-height:1.45; color:#8a6a1f;
              background:#fdf6e3; border:1px solid #ecdcae; border-radius:8px; padding:7px 9px; }
/* Cửa sổ 24h SẮP hết — nói ở ngay chỗ gõ, và CHỈ khi sắp hết. "Còn 22h" nói mỗi lần mở
   là thứ người ta học cách không nhìn; còn "còn 3h" thì đáng dừng lại. Ngưỡng ở renderChat. */
.ch-soon{ margin:0 0 7px; font-size:12px; font-weight:600; color:#a8493a; }
.ch-shut{ margin:0; font-size:12.5px; color:var(--muted); line-height:1.6; text-align:center; }

/* ── Đề xuất của Claude → Khoa duyệt (đầu tab Tối ưu) ────────────────────────
   Viền trái đậm + nền hơi ấm để tách khỏi bảng số bên dưới mà không phải dựng
   thêm một cái card nữa — tab này đã nhiều khung rồi, thêm khung là phẳng hết. */
.dx-box { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
.dx-card { border-left: 3px solid #a6455a; background: rgba(166, 69, 90, .055);
           border-radius: 6px; padding: 10px 12px; }
.dx-card.ok { border-left-color: #15705a; background: rgba(21, 112, 90, .06); }
.dx-top { display: flex; justify-content: space-between; align-items: baseline;
          gap: 10px; margin-bottom: 4px; }
.dx-tag { font-size: 11.5px; letter-spacing: .09em; text-transform: uppercase;
          font-weight: 700; color: #a6455a; }
.dx-card.ok .dx-tag { color: #15705a; }
.dx-when { font-size: 12px; opacity: .6; }
.dx-tt { font-weight: 600; line-height: 1.35; }
.dx-so { margin-top: 5px; font-size: 13px; font-variant-numeric: tabular-nums; opacity: .85; }
.dx-chi { margin-top: 6px; }
.dx-chi summary { cursor: pointer; font-size: 12.5px; opacity: .75; }
.dx-rows { max-height: 260px; overflow-y: auto; margin-top: 6px;
           font-variant-numeric: tabular-nums; }
.dx-r { display: flex; justify-content: space-between; gap: 12px; font-size: 12.5px;
        padding: 3px 0; border-bottom: 1px solid rgba(128, 128, 128, .16); }
.dx-r .dx-n i { font-style: normal; opacity: .55; }
.dx-btn { display: flex; gap: 8px; margin-top: 10px; }
.dx-btn button { font: inherit; font-size: 13px; font-weight: 600; padding: 7px 16px;
                 border-radius: 999px; border: 0; background: #15705a; color: #fff;
                 cursor: pointer; }
.dx-btn button.no { background: transparent; color: inherit;
                    border: 1px solid rgba(128, 128, 128, .45); font-weight: 500; }
.dx-btn button[disabled] { opacity: .45; cursor: default; }

/* ── Hàng "Đề xuất" trong lưới kênh ──────────────────────────────────────────
   Khoa 19/09/2026: đề xuất phải đứng NGAY cạnh tồn thật và số đang mở của đêm
   đó, chứ không nằm ở một khối rời trên đầu trang. Dùng viền trái + chữ đậm ở
   số ĐÍCH thay vì tô nền cả ô: lưới này đã có nền cho cuối tuần / hôm nay /
   hết phòng, thêm một lớp nền nữa là ba tín hiệu tranh nhau cùng một chỗ. */
tr.cg-dxrow th.cg-lab { border-left: 3px solid #a6455a; }
td.cg-dx { font-variant-numeric: tabular-nums; white-space: nowrap; color: #8794a0; }
td.cg-dx i { font-style: normal; opacity: .5; margin: 0 1px; }
td.cg-dx b { font-weight: 700; }
/* ĐỀ XUẤT vẽ THẲNG trong ô của kênh, làm dòng thứ tư dưới "số phòng · giá BF · giá RO".
   Không tách ra hàng riêng: thứ cần so là giá CŨ và giá MỚI của cùng một ô, mà hàng riêng
   thì mắt phải đi xuống qua hàng của kênh khác. Ô vốn đã là chồng dọc nên đây là idiom sẵn
   có, và chiều dọc là chiều còn chỗ — cột ~40px trên máy cầm tay không nhét nổi "cũ→mới".
   ⚠️ KHÔNG để màu chở nghĩa một mình: mũi tên ▲▼ nói hướng, nên ngoài nắng vẫn đọc được. */
.cg-dxc { display:block; }
u.cg-dxb { display:block; text-decoration:none; font-variant-numeric:tabular-nums;
  font-size:11px; line-height:1.25; font-weight:700; white-space:nowrap; }
u.cg-dxb.up   { color:#b8382c; }     /* tăng — cùng họ đỏ với "đóng", nhưng có mũi tên */
u.cg-dxb.down { color:#2f7d54; }
/* Viền trái mảnh: liếc cả tháng là thấy NGAY cột nào đang có đề xuất, không phải dò từng ô. */
td.cg-hasdx { box-shadow: inset 2px 0 0 #a6455a; cursor:pointer; }
/* Nháy khi được nhảy tới từ lịch — không có nó thì cuộn xong vẫn phải dò xem thẻ nào. */
@keyframes dxNhay { 0%,100%{ box-shadow:0 0 0 0 rgba(166,69,90,0); }
                    30%  { box-shadow:0 0 0 4px rgba(166,69,90,.35); } }
.dx-card.nhay { animation: dxNhay 1.6s ease-out; }
/* Nút đổi cách đọc giá. Đủ 44px chiều cao như mọi nút khác trên màn cầm tay. */
.av-gia{ display:inline-flex; align-items:center; min-height:44px; padding:0 12px; margin-top:4px;
  border:1px solid var(--line); border-radius:999px; background:var(--paper); color:var(--ink);
  font:inherit; font-size:12px; font-weight:600; cursor:pointer; }
.av-gia.on{ border-color:var(--cognac); color:#8a5a22; background:#fffaf0; }
td.cg-dx.up b { color: #15705a; }
td.cg-dx.down b { color: #8a6a1f; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) tr.cg-dxrow th.cg-lab { border-left-color: #e08098; }
  :root:not([data-theme="light"]) td.cg-dx.up b { color: #5fd1ac; }
  :root:not([data-theme="light"]) td.cg-dx.down b { color: #e0b461; }
}
:root[data-theme="dark"] tr.cg-dxrow th.cg-lab { border-left-color: #e08098; }
:root[data-theme="dark"] td.cg-dx.up b { color: #5fd1ac; }
:root[data-theme="dark"] td.cg-dx.down b { color: #e0b461; }

/* Tin vị trí: nút mở bản đồ + toạ độ chọn-sao-chép được (Khoa 22/09/2026 — trước đó là
   chuỗi chữ trơ "[location] [vị trí] …" và phải nhờ bóc tay để gửi tài xế). */
/* 44px là chuẩn app tự đặt ở ba chỗ khác; bản đầu của tôi cao 29,2px và `chat_ui.py`
   bắt được — nút này nằm trong bong bóng chat, ngón tay trượt là mở sai thứ. */
.ch-map{display:inline-flex;align-items:center;min-height:44px;margin:.1rem 0 .25rem;
  padding:.3rem .7rem;border-radius:.5rem;
  background:#0000000f;color:inherit;text-decoration:none;font-weight:600}
.ch-msg.out .ch-map{background:#ffffff26}
/* Voice note / video / file button — same shape as the map button (≥44px tap target). */
.ch-file{display:inline-flex;align-items:center;min-height:44px;margin:.1rem 0 .25rem;padding:.3rem .8rem;border-radius:.6rem;background:#0000000d;color:inherit;font-weight:600;text-decoration:none;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ch-file.wait{opacity:.6;font-weight:500}
.ch-file.z-file{display:flex;width:fit-content}   /* a Zalo file sits on its own line between the text parts */
.ch-msg.out .ch-file{background:#ffffff26}
.ch-map:active{opacity:.7}
.ch-toado{font-variant-numeric:tabular-nums;opacity:.75;font-size:.86em;
  user-select:all;-webkit-user-select:all}

/* Gửi ảnh cho khách (Khoa 22/09/2026 — trước đó màn Chat không có cách nào gửi ảnh).
   Nút nằm ở THANH HÀNH ĐỘNG chứ không phải hàng soạn: ở 402px hàng soạn chỉ còn 192px cho
   ô nhập, mà sàn là 170px — thêm một nút 44px vào đó là bóp ô soạn xuống ~148px. */
.ch-anhbtn{min-height:44px;padding:.3rem .7rem;border-radius:.6rem;border:1px solid #0000001f;
  background:#fff;color:inherit;font-weight:600;white-space:nowrap}
.ch-anhbtn:active{opacity:.7}
.ch-anhcho{display:flex;align-items:center;gap:.5rem;margin:0 0 .4rem;padding:.35rem .5rem;
  border:1px dashed #00000033;border-radius:.6rem;background:#0000000a}
.ch-anhthumb{width:44px;height:44px;object-fit:cover;border-radius:.4rem;flex:0 0 auto}
.ch-anhten{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:.86em;opacity:.8}
.ch-anhbo{min-height:44px;padding:.2rem .6rem;border:0;background:transparent;color:inherit;
  font-weight:600;flex:0 0 auto}

/* ── Finance: 3 columns on desktop, stacked on the phone; last year beside every number (Khoa 29/09/2026:
   "cả mobile và desktop tối ưu lại hết … biểu đồ, đối thủ … so sánh cùng kỳ") ── */
.rep-grid{ display:block; }
.rep-col{ min-width:0; }
.rep-kpis .kpi .ly{ display:block; font-style:normal; font-size:12px; color:var(--muted); margin-top:3px; }
.yoy{ display:inline-block; font-size:11.5px; font-weight:700; padding:0 5px; border-radius:6px; margin-left:2px; font-style:normal; }
.yoy.up{ color:#2f6b52; background:#e3efe8; } .yoy.dn{ color:#9a3b2e; background:#f6e3df; }
.yy-legend{ display:flex; gap:14px; font-size:12px; color:var(--muted); margin:0 4px 6px; flex-wrap:wrap; }
.yy-legend i{ display:inline-block; width:10px; height:10px; border-radius:2px; margin-right:5px; vertical-align:-1px; }
.yy-legend .yy-fut{ margin-left:auto; font-style:italic; }
i.yy-now{ background:var(--cognac); } i.yy-ly{ background:#e6dccb; }
.yy-chart{ display:flex; align-items:flex-end; gap:6px; height:150px; margin:2px 2px 6px; }
.yy-col{ flex:1; min-width:0; height:100%; display:flex; flex-direction:column; align-items:center; justify-content:flex-end; gap:3px; cursor:default; }
.yy-bars{ position:relative; width:100%; max-width:34px; flex:1; }
.yy-bars i{ position:absolute; bottom:0; border-radius:4px 4px 0 0; }
.yy-bars i.yy-ly{ left:0; width:100%; }
.yy-bars i.yy-now{ left:22%; width:56%; background:var(--cognac); opacity:.55; }
.yy-col.cur .yy-bars i.yy-now{ opacity:1; }
.yy-col.fut .yy-bars i.yy-now{ opacity:1; background:repeating-linear-gradient(135deg,var(--cognac) 0 3px,transparent 3px 6px); box-shadow:inset 0 0 0 1.5px var(--cognac); }
.yy-col:hover .yy-bars i.yy-now{ outline:2px solid var(--paper); box-shadow:0 0 0 3px #b5916a55; }
.yy-m{ font-size:11.5px; color:var(--muted); line-height:1.1; text-align:center; }
.yy-m small{ display:block; font-size:10px; }
.yy-col.cur .yy-m{ color:var(--ink); font-weight:700; }
/* a January column carries its year in ink (Khoa 06/10/2026; the dashed new-year rule was dropped the same day: "xấu quá") */
.yy-col.yy-ny .yy-m{ color:var(--ink); font-weight:700; }
.pace-t, .cmp-t{ width:100%; border-collapse:collapse; font-size:12.5px; background:var(--paper); border:1px solid var(--line); border-radius:12px; overflow:hidden; }
.pace-t th, .cmp-t th{ font-size:11.5px; font-weight:600; color:var(--muted); text-align:right; padding:7px 10px 5px; }
.pace-t td, .cmp-t td{ padding:6px 8px; text-align:right; border-top:1px solid var(--line2); white-space:nowrap; line-height:1.25; }
.cmp-t.scan td{ white-space:normal; }
.pace-t, .cmp-t{ table-layout:auto; max-width:100%; }
/* ── Finance, Khoa 02/10/2026 ("mobile và desktop luôn"): nothing under 12px, taps ≥ 44px, a tap on a chart column says
   what it is, columns from 760px (1024 was a 640px phone column), chart height that actually applies (the old ≥1100
   rule sat ABOVE the base .yy-chart rule and lost), the spend form floating on a desktop. ── */
.pane.report .floorhd,
.pane.report .rep-lab,
.pane.report .rep-kpis .kpi span,
.pane.report .yoy,
.pane.report .yy-legend,
.pane.report .yy-m,
.pane.report .yy-m small,
.pane.report .rep-split .rps small,
.pane.report .pace-t th,
.pane.report .pace-t small,
.pane.report .cmp-t th,
.pane.report .cmp-t small,
.pane.report .cmp-note,
.pane.report .invb-sub,
.pane.report .rsrc-n{ font-size:12px; }
.rep-est{ font-size:12px; color:var(--muted); margin-top:6px; line-height:1.4; }
.rsrc[data-src]{ min-height:44px; display:flex; flex-direction:column; justify-content:center; }
.exp-add{ min-height:44px; }
.exp-row.sel{ background:var(--cream); box-shadow:inset 3px 0 0 var(--cognac); }
.exp-row, .exp-add{ scroll-margin-top:calc(var(--bar-top, 110px) + 8px); }
.rep-est b{ font-weight:600; white-space:nowrap; }
.yy-m small{ display:inline; }
.yy-tip{ white-space:pre-line; }
.rep-split.rep-fnb + .rep-fnb-pkg{ margin-top:10px; }
.exp-form button[type=submit]{ position:sticky; bottom:10px; z-index:1; box-shadow:0 6px 18px rgba(40,25,10,.18); }
.exp-cancel{ min-height:44px; border:1px solid var(--line); border-radius:22px; background:none; font:inherit; font-size:14px; color:var(--muted); }
.exp-list .exp-form{ margin:0; border-radius:0; border-width:1px 0; }
.yy-col{ cursor:pointer; }
.yy-col.sel .yy-bars{ outline:2px solid #b5916a66; outline-offset:2px; border-radius:4px; }
.yy-tip{ margin:2px 4px 8px; font-size:13px; color:var(--ink); background:var(--cream); border:1px solid var(--line); border-radius:10px; padding:7px 11px; }
/* ── Finance on a desktop: its OWN layout, a fixed grid of whole blocks (Khoa 15/10/2026: "vị trí bố cục chưa tối ưu có
   thể làm riêng bảng desktop mới song song bản mobile hiện tại"). Read top-down like a monthly report: the month's result
   → 12-month trend → months ahead (the pricing question) → where revenue came from → spends → every recorded spend at
   full width. The newspaper columns it replaces (09/10) parted headings from their content: "Doanh thu 12 tháng" at the
   foot of one column, its chart atop the next; the recurring checklist ran on into another column with no heading.
   The phone keeps the stacked layout untouched — the .rb wrappers are plain divs there. ── */
@media(min-width:760px){
  .pane.report{ max-width:none; }   /* edge to edge, level with the month bar above (1760 centred sat 80px right of it at 1920) */
  .rep-grid{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:22px 22px; align-items:start;
    grid-template-areas:"hero kpis" "hero src" "hero fnb" "chart chart" "pace pace" "comp comp" "cat cat" "rec rec" "list list" "inv inv"; }
  .rep-col, .rep-spend .exp-slot{ display:contents; }
  .rb-hero{ grid-area:hero; } .rep-kpis{ grid-area:kpis; margin-bottom:0; } .rb-src{ grid-area:src; } .rb-fnb{ grid-area:fnb; }
  .rb-chart{ grid-area:chart; } .rb-pace{ grid-area:pace; } .rb-comp{ grid-area:comp; }
  .rb-cat{ grid-area:cat; } .rb-rec{ grid-area:rec; } .rb-list{ grid-area:list; } .rb-inv{ grid-area:inv; }
  .rb > .floorhd:first-child{ margin-top:0; }
  /* the 12-item recurring checklist in two columns (one column beside 2 category bars left ~350px blank) */
  .rb-rec .exp-rec{ display:grid; grid-template-columns:1fr 1fr; column-gap:0; }
  .rb-rec .exp-ri + .exp-ri{ border-top:0; }
  .rb-rec .exp-ri{ border-bottom:1px solid var(--line2); }
  .rb-rec .exp-ri:nth-child(odd){ border-right:1px solid var(--line2); }
  .rep-grid .rep-kpis{ grid-template-columns:1fr 1fr; }
  .yy-chart{ height:230px; }
  /* the recorded spends: one wide row each — date · category · who/note in full · amount */
  .rb-list .exp-nm{ display:flex; align-items:baseline; gap:14px; }
  .rb-list .exp-nm small{ flex:1; min-width:0; white-space:normal; overflow:visible; }
  .rb-list .exp-add{ display:block; margin-left:auto; }
  .exp-form:not([hidden]){ position:fixed; z-index:60; top:50%; left:50%; transform:translate(-50%,-50%); margin:0;
    width:min(520px,calc(100vw - 48px)); max-height:calc(100vh - 48px); overflow:auto; border-radius:16px;
    box-shadow:0 0 0 100vmax rgba(20,14,8,.6), 0 18px 60px rgba(40,25,10,.28); }
  .exp-list .exp-form{ border-width:1px; }
}
@media(min-width:1280px){
  /* three columns: the headline spans two rows beside the KPI strip and the wide chart; the months-ahead table sits
     beside sources + breakfast; the competitor block gets its own full row (live 15/10/2026: its 9-column table, ~700px
     tall, left the column beside it empty); the 12-item recurring checklist runs in two columns beside the categories */
  .rep-grid{ grid-template-columns:repeat(3,minmax(0,1fr));
    grid-template-areas:"hero kpis kpis" "hero chart chart" "src pace pace" "fnb pace pace"
                        "comp comp comp" "cat rec rec" "list list list" "inv inv inv"; }
  .rep-grid .rep-kpis{ grid-template-columns:repeat(4,minmax(0,1fr)); }
}
.pace-t td:first-child, .cmp-t td:first-child, .pace-t th:first-child, .cmp-t th:first-child{ text-align:left; color:var(--ink); }
.pace-t small, .cmp-t small{ display:block; color:var(--muted); font-size:11.5px; font-weight:400; }
.cmp-t td.up{ color:#2f6b52; font-weight:700; } .cmp-t td.dn, .cmp-t b.dn{ color:#9a3b2e; }
.cmp-t.scan td{ font-size:12px; }
.cmp-h{ font-size:12px; color:var(--muted); margin:6px 4px 6px; }   /* nothing under 12px (02/10 rule; first measured with real competitor data 15/10/2026) */
.cmp-note{ font-size:12px; color:var(--muted); margin:6px 4px 0; }

/* popup 30/09/2026 (Khoa "giao diện đẹp hơn"): mọi khách CÙNG cỡ, mỗi người một dòng, chỉ gãy sau '/' (bản đầu
   để khách chính to/người đi cùng nhỏ — Khoa: "khách thấy chắc chửi" — và cắt "LIUDMIL|A" giữa chữ); ba nhóm lưu trú ·
   tiền · giấy tờ ngăn bằng vạch mảnh; TB/đêm và ghi chú tháng báo cáo là dòng phụ nhỏ dưới con số. */
.pp-name{ font-size:21px !important; line-height:1.25; overflow-wrap:normal; word-break:normal; padding-right:30px; }
/* break-word: only a token longer than the whole line is cut; ordinary names still break at '/' first */
.pp-gn{ display:block; overflow-wrap:break-word; }
/* The id rule `#popup-body .row span` (13,5px) beat the class rules below — measured 30/09: the paid price rendered at
   13,5px, deposit lines at 13,5px grey instead of 12,5px/green. Same-weight selectors restore the intended scale:
   Cần thu 21 > paid price 16 > TB/đêm 12. */
#popup-body .row .pp-paid{ font-size:16px; }
#popup-body .row .pp-dep-total, #popup-body .row .pp-dep-paid{ font-size:12.5px; }
#popup-body .row .pp-dep-paid{ color:#3f7d54; }
/* multi-line values (Cần thu, Đặt cọc, Báo cáo): the label sits on the first line, not floating mid-block */
#popup-body .row{ align-items:baseline; }
/* plain values one step lighter so the money figures lead */
#popup-body .row b{ font-weight:600; }
#popup-body .row .pp-money, #popup-body .row .pp-paid{ font-weight:700; }
.pp-grp{ border-top:1px solid var(--line); margin-top:10px; padding-top:4px; }
.pp-sub + .pp-grp{ border-top:0; margin-top:0; padding-top:0; }
.pp-grp .row:first-child{ border-top:0; }
#popup-body .row .pp-pn, #popup-body .row .pp-note{ display:block; font-size:12px; font-weight:500;
  color:var(--muted); margin-top:2px; font-style:normal; }
/* long notes: right-aligned bold made a ragged 7-line block (fixture 30/09) — a wrapped note reads left-aligned and
   lighter; a one-line note still sits at the right edge (the value box is only as wide as its text). */
#popup-body .row-note b{ text-align:left; font-weight:500; font-size:14px; }

/* ---- Dọn phòng + Ăn sáng (Khoa 30/09/2026 overnight) — same card/strip language as "Theo ngày" ---- */
.hk-card .who{ min-width:0; }
/* Dọn ở tiếp: sheets + DND as a quiet second line under the room (Khoa 01/10/2026) */
/* five counts must fit 402px: number over its label (Khoa 01/10/2026 asked for counts per job) */
.pane.hk .daystat .su{ display:flex; flex-direction:column; align-items:center; line-height:1.2; padding:2px 7px; font-size:12px; }
.pane.hk .daystat .su b{ font-size:15px; }
.hk-card{ flex-wrap:wrap; }
.hk-quiet{ flex-basis:100%; display:flex; justify-content:flex-end; gap:2px; margin:-8px -6px -10px 0; }
.hk-tags{ display:block; margin-top:3px; }   /* the sheet badge always on its own line under the name */
.ga{ display:inline-block; max-width:100%; padding:1px 7px; border-radius:9px; font-size:11.5px; font-weight:600; white-space:normal; }   /* a reason wraps, never under the button */
.ga-ok{ color:var(--muted); background:none; padding-left:0; }
.ga-warn{ background:#fbf0cf; color:#8a6a00; border:1px solid #efd98a; }
.ga-late{ background:#f8dcd6; color:#9b2c1c; border:1px solid #eeb6aa; }
.stay.stay-dnd{ background:#ecebe8; color:#6b6760; border:1px solid #d8d5cf; }
.hk-card.dnd .rm, .hk-card.dnd .who{ opacity:.6; }
.hk-btn{ flex:none; min-height:44px; min-width:44px; padding:0 14px; border-radius:22px; border:1px solid var(--line);
  background:#fff; font:inherit; font-size:14px; font-weight:600; color:var(--ink); cursor:pointer; }
.hk-btn.go{ background:var(--cognac); border-color:var(--cognac); color:#fff; }
.hk-btn.undo{ color:var(--muted); font-weight:500; }
.hk-btn:disabled{ opacity:.5; }
.hk-err{ margin:6px 4px 10px; font-size:13px; color:#bf5b4b; }
.floorhd.hk-stay{ color:var(--muted); }
.floorhd.owe{ color:#8a6a2a; }
.fnb-week{ position:relative; display:grid; grid-auto-flow:column; grid-auto-columns:calc((100% - 36px) / 6.5); gap:6px; margin:0 0 6px;
  overflow-x:auto; scroll-snap-type:x proximity; overscroll-behavior-x:contain; scrollbar-width:none; }   /* 6½ in view: the cut tile says "swipe" */
.fnb-week::-webkit-scrollbar{ display:none; }
.fnb-week .fnb-day{ scroll-snap-align:start; }
.fnb-day{ display:flex; flex-direction:column; align-items:center; justify-content:center; min-height:52px; padding:4px 0;
  border:1px solid var(--line); border-radius:12px; background:var(--paper); color:var(--ink); font:inherit; cursor:pointer; }
.fnb-day small{ font-size:11.5px; line-height:1.2; color:var(--muted); white-space:nowrap; }
.fnb-day b{ font-size:17px; }
.fnb-day.on{ border-color:var(--cognac); box-shadow:inset 0 0 0 1px var(--cognac); }
.fnb-add{ width:100%; margin:12px 0 16px; min-height:44px; font-size:14px; font-weight:600; color:var(--cognac); border-color:var(--cognac); }
.fnb-form{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:10px; margin:0 0 16px; padding:14px; background:var(--paper);
  border:1px solid var(--line); border-radius:12px; box-shadow:var(--shadow); }
.fnb-form label{ display:flex; flex-direction:column; gap:4px; min-width:0; overflow:hidden; font-size:12.5px; color:var(--muted); }
.fnb-form select{ text-overflow:ellipsis; }
.fnb-form .wide{ grid-column:1 / -1; }
.fnb-form input, .fnb-form select{ width:100%; min-width:0; box-sizing:border-box; min-height:44px; font:inherit; font-size:16px; color:var(--ink); padding:0 10px;
  border:1px solid var(--line); border-radius:10px; background:#fff; }
/* the label clips (overflow:hidden) — the global 2px-offset focus ring was cut at the corners; draw it inside */
.fnb-form input:focus-visible, .fnb-form select:focus-visible{ outline-offset:-2px; border-radius:10px; }
.fnb-sum{ margin:0; font-size:14px; font-weight:600; color:var(--ink); }
.rep-fnb-pkg{ margin:0 4px 10px; font-size:14px; color:var(--ink); }
.rep-fnb-pkg small, .rep-fnb-lab small{ color:var(--muted); font-size:12px; }
.rep-fnb-lab{ margin:0 4px 6px; font-size:12.5px; font-weight:600; color:var(--muted); }
.pane.hk .fnb-week{ margin-bottom:6px; }
.fnb-list{ display:flex; flex-direction:column; border:1px solid var(--line); border-radius:12px; background:var(--paper);
  box-shadow:var(--shadow); overflow:hidden; }
.fnb-room + .fnb-room{ border-top:1px solid var(--line2); }
.fnb-room .who{ display:flex; align-items:center; gap:10px; width:100%; min-height:52px; padding:7px 12px; border:0; background:none;
  font:inherit; color:var(--ink); text-align:left; }
.fnb-room .rm{ flex:none; width:44px; font-size:16px; font-weight:700; }   /* "302" fits; a family room goes on two lines */
.fnb-room .rm.two{ font-size:13.5px; line-height:1.15; }
.fnb-room .nm{ flex:1; min-width:0; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; font-size:14.5px; }
.fnb-room .nm small:empty{ display:none; }
.fnb-room .nm small{ display:block; overflow:hidden; text-overflow:ellipsis; font-size:12px; color:var(--muted); }
.fnb-room .fnb-n{ flex:none; min-width:28px; text-align:right; font-size:20px; color:var(--ink); }
.fnb-room .tg{ font-style:normal; font-weight:600; }
.fnb-room .tg.add{ color:var(--cognac); }
.fnb-room .tg.owe{ color:#8a6a00; }
.fnb-room .tg.paid{ color:#2f6b3c; }
.fnb-room .tg.bad{ color:#b3493a; }
.fnb-room.off .rm, .fnb-room.off .nm, .fnb-room.off .fnb-n{ opacity:.45; }
.fnb-room.open{ background:var(--cream); }
.fnb-acts{ display:flex; align-items:center; justify-content:flex-end; gap:8px; padding:0 12px 10px; }
.fnb-acts .fnb-note{ font-size:12.5px; color:var(--muted); }
.fnb-quick{ display:flex; align-items:center; gap:6px; margin-bottom:6px; font-size:12.5px; color:var(--muted); }
.fnb-quick span{ flex:1; }
.fnb-quick button{ min-height:36px; padding:0 12px; border-radius:18px; border:1px solid var(--cognac); background:none; color:var(--cognac);
  font:inherit; font-size:12.5px; font-weight:600; }
.fnb-chips{ display:flex; flex-wrap:wrap; gap:6px; }
.fnb-chip{ min-height:40px; padding:0 11px; border-radius:20px; border:1px solid var(--line); background:#fff; color:var(--ink);
  font:inherit; font-size:13px; }
.fnb-chip.on{ background:var(--cognac); border-color:var(--cognac); color:#fff; }
.fnb-chip.locked{ opacity:.55; }
.fnb-who{ margin:0; font-size:14px; }
.fnb-hd{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin:4px 0 12px; }   /* same lines every day: no jump */
.fnb-hd .l{ display:flex; flex-direction:column; align-items:flex-start; min-width:0; }
/* "Hôm nay" floats on the strip edge on the side today lies, only while today's tile is out of sight */
.fnb-stripw{ position:relative; }
/* position + size are set from the edge tile it replaces (sync in dayStrip) */
.fnb-stripw > .fnb-today{ position:absolute; z-index:2; min-height:0; padding:0; box-sizing:border-box;
  border-radius:12px; border:1.5px solid var(--cognac); background:var(--paper); white-space:pre-line; line-height:1.3; }
/* a page-coloured cover behind the chip, reaching the gap before the first tile fully in sight (--cover, set on every
   scroll): tiles slid under the chip and showed through its corners and beside it (Khoa 05/10/2026 iPhone screenshot).
   Mandatory snapping was tried instead — it fought the strip's own prepend-on-scroll and jumped a tapped day away. */
.fnb-stripw::before{ content:''; display:none; position:absolute; top:-2px; bottom:0; z-index:1; pointer-events:none;
  width:var(--cover, calc((100% - 36px) / 6.5 + 6px)); background:var(--cream); }
/* on the right the inner edge FADES the cut tile into the page (Khoa 05/10/2026: on the right "nó cắt ngay đúng chỗ này" — a hard
   straight cut through a tile beside the chip); the 18 px are added to the cover width in dayStrip */
.fnb-stripw[data-chip="l"]::before{ display:block; left:-2px; }   /* tiles snap to the left edge: a whole tile there */
.fnb-stripw[data-chip="r"]::before{ display:block; right:-2px;
  background:linear-gradient(to left, var(--cream) calc(100% - 18px), transparent); }
.fnb-empty{ margin:0 0 12px; padding:18px 12px; border:1px dashed var(--line); border-radius:12px; text-align:center;
  font-size:13.5px; color:var(--muted); }
.fnb-hd small{ font-size:12px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--cognac); }
.fnb-hd .l > b{ font-size:30px; line-height:1.15; color:var(--cognac); }
.fnb-hd .r{ margin:0; padding:0; list-style:none; text-align:right; font-size:13.5px; line-height:1.5; color:var(--muted); }
.fnb-hd .r b{ font-size:15px; color:var(--ink); }
.fnb-hd .r .z{ opacity:.55; }
.fnb-hd .part{ font-size:13px; color:var(--muted); }
.fnb-warn{ margin:-4px 0 10px; font-size:12.5px; color:#9b5a1c; }
.fnb-today{ flex:none; min-height:40px; padding:0 12px; border-radius:18px; border:1px solid var(--cognac); background:none;
  color:var(--cognac); font:inherit; font-size:12px; font-weight:700; letter-spacing:0; text-transform:none; }
/* "Không ăn" is rare: a quiet text button, still a 44px target */
.fnb-x{ flex:none; min-height:44px; min-width:44px; padding:0 8px; border:0; background:none; color:var(--muted); font:inherit; font-size:12.5px;
  text-decoration:underline; text-underline-offset:3px; }
.hk-card .rm{ min-width:44px; }   /* one physical room per card ("204"), so "302" needs no family-wide column */
/* WebKit/iOS ignores width:100% on a native date input and sizes it to its own content (measured 30/09: the form
   pushed the page to 433px in WebKit while Chromium was fine) — drop the native look so the grid can size it. */
.fnb-form input[type=date]{ -webkit-appearance:none; appearance:none; display:block; height:44px; line-height:42px; }   /* 44 incl. the 1px borders: level with the text inputs beside it */

.fnb-quick button{ border-style:dashed; }   /* shortcuts that tick chips, not chips themselves */
/* Dọn phòng strip: check-out (heavy clean) in the amber of "dọn check-out", daily clean in the accent of "dọn ở tiếp",
   sheets as a small count under them — never a tile colour (a whole group falling due must not paint the strip) */
.pane.hk .fnb-day .co{ color:#a8741a; }
.pane.hk .fnb-day .hn{ color:var(--cognac); }
.pane.hk .fnb-day .z{ opacity:.3; }
.pane.hk .fnb-day .sep{ margin:0 2px; color:var(--muted); font-weight:400; }
.pane.hk .fnb-day .hk-bar{ display:flex; gap:2px; width:34px; height:4px; margin:3px 0 1px; }
.pane.hk .fnb-day .hk-bar.z{ visibility:hidden; }
.pane.hk .fnb-day .hk-bar i{ flex:1; border-radius:2px; background:color-mix(in srgb, var(--muted) 28%, transparent); }
.pane.hk .fnb-day .hk-bar i.d{ background:var(--cognac); }
.pane.hk .fnb-day .hk-bar.miss i:not(.d){ background:#d9897b; }
.pane.hk .fnb-day .hk-bar.one{ position:relative; border-radius:2px; overflow:hidden; background:color-mix(in srgb, var(--muted) 28%, transparent); }
.pane.hk .fnb-day .hk-bar.one.miss{ background:#d9897b; }
.pane.hk .fnb-day .hk-bar.one i{ position:absolute; left:0; top:0; bottom:0; flex:none; border-radius:2px; background:var(--cognac); }
.pane.hk .fnb-day .ga-n{ font-style:normal; font-size:11.5px; font-weight:700; line-height:1.3; color:#8a6a00; white-space:nowrap; }
.pane.hk .fnb-day .ga-n.late{ color:#b3493a; }
.pane.hk .fnb-day .ga-n.z{ visibility:hidden; }
.hk-hd{ display:flex; align-items:center; justify-content:space-between; gap:8px; min-height:36px; margin:2px 0 8px; }
.hk-hd small{ font-size:12px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--cognac); }
.pane.hk .hk-stat{ width:auto; max-width:none; display:grid; grid-auto-flow:column; grid-auto-columns:1fr; }
/* equal columns: the dividers sit evenly, each count centred in its column (Khoa 02/10 screenshot: "ocd nhìn ngứa mắt") */
.daystat .su.hn b{ color:var(--cognac); }
.daystat .su.ga-warn-s, .daystat .su.ga-warn-s b{ color:#8a6a00; }
.daystat .su.ga-late-s, .daystat .su.ga-late-s b{ color:#b3493a; }
/* the cleaning card's second-line actions read as buttons (Khoa 01/10: the early sheet change needs a button you see) */
.hk-card .hk-quiet{ gap:8px; margin:2px 0 0; }
.hk-mates{ display:block; margin-top:2px; font-size:12px; font-weight:400; color:var(--muted); }
.fnb-hd .r .dt{ font-size:12px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--cognac); }

/* ── "Ăn sáng" / "Dọn phòng" as ONE system (Khoa 01/10: "đồng bộ cả khối … đẹp nhất có thể") ──
   section title: one look, count / amount on the right · lists: one white frame, room column 44px, rows ≥52px ·
   buttons: primary = filled accent pill (.hk-btn.go) · secondary = thin-bordered pill · "add" = accent-bordered pill */
.fnb-sec{ display:flex; align-items:baseline; justify-content:space-between; gap:8px; margin:18px 2px 8px; font-size:12px;
  font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--cognac); }
.fnb-sec b{ letter-spacing:0; text-transform:none; font-size:13px; color:var(--muted); }
.fnb-sec.owe b{ font-size:15px; color:#8a6a2a; }
.fnb-acts .fnb-x, .fnb-due .fnb-edit, .hk-card .hk-act{ min-height:40px; padding:0 16px; border:1px solid var(--line); border-radius:20px;
  background:#fff; color:var(--ink); text-decoration:none; font-size:13.5px; }
.fnb-acts{ flex-wrap:wrap; }
.fnb-due + .fnb-due{ border-top:1px solid var(--line); }
.fnb-due-head{ display:flex; align-items:center; gap:10px; min-height:52px; padding:7px 12px; }
.fnb-due-head .rm{ flex:none; width:44px; font-size:16px; font-weight:700; }
.fnb-due-head .who{ flex:1; min-width:0; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; font-size:14.5px; }
.fnb-due-head .who small{ display:block; font-size:12px; color:var(--muted); }
.fnb-payline{ display:flex; align-items:center; justify-content:space-between; gap:10px; margin-left:66px; padding:6px 12px 6px 0;
  border-top:1px solid var(--line2); font-size:14.5px; }
.fnb-payline .hk-btn{ min-height:40px; min-width:72px; }
.fnb-paytotal{ font-weight:700; color:#8a6a2a; }
/* the confirmation sheet: the app's own modal, its buttons full width */
.modal.ask .card{ padding:20px 18px 14px; }
.modal.ask h3{ margin-bottom:4px; }
.ask-sub{ margin:0 0 14px; font-size:14px; color:var(--muted); }
.ask-chips{ display:flex; flex-wrap:wrap; gap:6px; margin-bottom:8px; }
.ask-input{ width:100%; box-sizing:border-box; min-height:44px; padding:0 12px; border:1px solid var(--line); border-radius:12px;
  font:inherit; font-size:16px; background:#fff; color:var(--ink); }
.ask-btns{ display:flex; gap:8px; margin-top:14px; }
.ask-btns .hk-btn{ flex:1; min-height:48px; font-size:15px; }
.ask-cancel{ display:block; width:100%; min-height:44px; margin-top:6px; border:0; background:none; font:inherit; color:var(--muted); }
@media (max-width: 600px){ .modal.ask{ align-items:flex-end; } .modal.ask .card{ border-radius:18px 18px 12px 12px; } }
/* the "done" note after a write */
.toast{ position:fixed; left:50%; bottom:calc(24px + env(safe-area-inset-bottom, 0px)); transform:translateX(-50%); z-index:60;
  max-width:calc(100vw - 32px); padding:11px 18px; border-radius:22px; background:#2a241d; color:#fff; font-size:14px;
  box-shadow:0 8px 24px rgba(0,0,0,.25); animation:toast-in .18s ease-out; }
@keyframes toast-in{ from{ opacity:0; transform:translate(-50%, 8px); } to{ opacity:1; transform:translate(-50%, 0); } }
/* "Thêm suất ăn sáng" — big but light (Khoa 01/10: "to nhưng không thô … hài hòa"): a soft tint of the accent, not a slab */
.fnb-addbig{ display:flex; align-items:center; justify-content:center; gap:10px; width:100%; min-height:54px; margin:14px 0 4px;
  border:1px solid color-mix(in srgb, var(--cognac) 35%, transparent); border-radius:16px;
  background:color-mix(in srgb, var(--cognac) 9%, var(--paper)); color:var(--cognac); font:inherit; font-size:15.5px; font-weight:700; }
.fnb-addbig .plus{ display:inline-flex; align-items:center; justify-content:center; width:26px; height:26px; border-radius:50%;
  background:var(--cognac); color:#fff; font-size:18px; line-height:1; }
.addsheet .card{ max-height:88vh; }
.add-who{ display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); gap:8px; margin-bottom:6px; }
.add-tile{ display:flex; flex-direction:column; align-items:flex-start; gap:1px; min-height:64px; padding:8px 10px; border:1px solid var(--line);
  border-radius:12px; background:#fff; color:var(--ink); font:inherit; text-align:left; }
.add-tile b{ font-size:16px; }
.add-tile span{ max-width:100%; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; font-size:12px; }
.add-tile i{ font-style:normal; font-size:12px; color:var(--muted); }
.add-tile.pkg{ opacity:.55; }
.add-tile.reg i{ color:var(--cognac); font-weight:700; }
.add-tile.other{ border-style:dashed; justify-content:center; }
.add-chosen{ display:flex; align-items:center; gap:10px; margin:2px 0 10px; padding:8px 0 10px; border-bottom:1px solid var(--line2); }
.add-chosen b{ font-size:18px; }
.add-chosen span{ flex:1; min-width:0; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; color:var(--muted); }
.add-warn{ margin:0 0 8px; font-size:12.5px; color:#9b5a1c; }
.add-name{ margin-bottom:10px; }
.add-lab{ display:flex; align-items:center; justify-content:space-between; margin:4px 0 6px; font-size:12px; font-weight:700;
  letter-spacing:.06em; text-transform:uppercase; color:var(--muted); }
.add-row{ display:flex; align-items:center; gap:8px; margin-top:12px; }
.add-lab-in{ flex:1; font-size:14.5px; }
.add-row .fnb-step{ min-width:44px; font-size:20px; padding:0; border:1px solid var(--line); border-radius:22px; background:#fff;
  color:var(--ink); text-decoration:none; }
.add-row .add-price{ flex:1; min-width:0; outline-offset:-2px; }
.add-notelink{ margin-top:6px; }
.fnb-step-n{ min-width:40px; text-align:center; font-size:18px; }
.add-link{ flex:none; min-height:36px; padding:0 14px; border:1px solid var(--line); border-radius:18px; background:#fff;
  color:var(--ink); font:inherit; font-size:13px; }
.add-notelink{ margin-top:12px; }

/* Tài chính · chi phí tháng (Khoa 01/10/2026 "quản lý chi … ko xót"): same list frame as the breakfast block */
.exp-rec, .exp-list{ display:flex; flex-direction:column; border:1px solid var(--line); border-radius:12px; background:var(--paper); overflow:hidden; margin-bottom:6px; }
.exp-ri, .exp-row{ display:flex; align-items:center; gap:10px; width:100%; min-height:44px; padding:8px 12px; border:0; background:none;
  font:inherit; font-size:14px; color:var(--ink); text-align:left; box-sizing:border-box; }
.exp-ri + .exp-ri, .exp-row + .exp-row{ border-top:1px solid var(--line2); }
button.exp-ri, .exp-row{ cursor:pointer; } button.exp-ri:active, .exp-row:active{ background:var(--line2); }
.exp-mk{ flex:none; width:16px; text-align:center; color:#8a6a2a; }
.exp-ri.done .exp-mk{ color:#3E6F59; }
.exp-ri.done .exp-nm{ color:var(--muted); }
.exp-nm{ flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; }
.exp-nm small{ display:block; font-size:12px; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.exp-ri b, .exp-row b{ flex:none; font-variant-numeric:tabular-nums; font-weight:600; color:var(--ink); }
.exp-ri i{ flex:none; font-style:normal; font-size:12.5px; color:#8a6a2a; font-variant-numeric:tabular-nums; }
.exp-d{ flex:none; width:40px; font-size:12.5px; color:var(--muted); font-variant-numeric:tabular-nums; }
.exp-edit-hd{ margin:0; font-size:13px; font-weight:600; color:var(--ink); }
.exp-del{ min-height:44px; border:1px solid #d9b3ab; border-radius:22px; background:none; font:inherit; font-size:14px; color:#b3493a; }
.hk-btn.go.exp-del-ok{ background:#b3493a; border-color:#b3493a; }
/* "Sửa" sheet (Khoa 01/10 "giao diện hơi kỳ kỳ"): paid mornings as one quiet line, a plain summary above the save */
.add-paid{ margin:0 0 10px; font-size:13px; color:#3E6F59; }
.add-sum{ margin:12px 0 0; font-size:13px; color:var(--muted); }
/* "Chi phí bếp" (Khoa 01/10/2026): the kitchen's spend for the morning + the photo of its hand-written list */
.kit-row{ display:flex; align-items:center; gap:8px; min-height:48px; padding:6px 12px; }
.kit-row + .kit-row{ border-top:1px solid var(--line2); }
.kit-row b{ flex:none; font-variant-numeric:tabular-nums; color:var(--ink); }
.kit-row span{ flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:13px; color:var(--muted); }
.kit-row .fnb-x{ flex:none; }
.kit-row .kit-del{ color:#b3493a; }
.kit-photo{ position:relative; display:flex; align-items:center; justify-content:center; min-height:48px; margin-top:10px; border:1px dashed var(--cognac);
  border-radius:12px; color:var(--cognac); font-size:15px; font-weight:600; cursor:pointer; }
.kit-photo input{ position:absolute; width:1px; height:1px; opacity:0; pointer-events:none; }
.kit-prev{ display:block; width:100%; max-height:34vh; object-fit:contain; margin-top:8px; border-radius:10px; background:var(--line2); }
.kit-prev[hidden]{ display:none; }
.kit-view .card{ max-height:92vh; overflow:auto; }
.kit-big{ display:block; width:100%; height:auto; border-radius:10px; }
/* the app's own error / note sheet (Khoa 01/10/2026): plain reason above, the raw technical text small underneath */
.notice h3{ color:#b3493a; }
.notice-tech{ margin:8px 0 0; font-size:11.5px; line-height:1.4; color:var(--muted); word-break:break-word; }
/* "Dọn phòng" for the housekeeper (Khoa 01/10/2026): one line per room, quiet buttons behind a tap, a booking's rooms
   hanging together, finished rooms dimmed at the bottom */
.hk-card .hk-name{ display:block; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.hk-card .hk-same{ font-weight:400; font-size:13px; color:var(--muted); }
.hk-card .rm{ min-width:46px; }
.hk-card .rm small{ display:block; margin-top:1px; font-size:11px; font-weight:500; white-space:nowrap; }
.hk-card .rm small .ga{ font-size:11px; padding:0; background:none; }
.hk-card .hk-first{ color:#a8742a; font-weight:700; }
.hk-card .who{ flex:1; }
.hk-card .hk-right{ flex:none; }
/* a card per booking, a row per room (Khoa 01/10: "chưa được mượt"): rows split by a hairline, everything centred on
   the row, a due sheet change as a small pill beside "Đã dọn", the quiet buttons on one line of equal pills */
.hk-card{ display:block; padding:0; }
.hk-row{ display:flex; flex-wrap:wrap; align-items:center; gap:10px; padding:8px 10px 8px 12px; min-height:52px; box-sizing:border-box; }
.hk-row + .hk-row{ border-top:1px solid var(--line); }
.hk-row.urgent{ background:#fdf6e8; }
.hk-row.done{ opacity:.55; }
.hk-row.dnd .rm, .hk-row.dnd .who{ opacity:.6; }
.hk-row .hk-due{ display:flex; flex:none; }
.hk-row .hk-due .hk-act{ min-height:36px; padding:0 12px; font-size:13px; }
.hk-row .hk-quiet{ display:none; }
.hk-row.open .hk-quiet{ display:flex; flex-basis:100%; gap:6px; margin:2px 0 0; }
.hk-row.open .hk-quiet .hk-act{ flex:1 1 0; min-width:0; padding:0 6px; font-size:13px; white-space:nowrap; }
.hk-ghd{ padding:10px 12px 2px; font-size:15px; font-weight:600; }
.hk-ghd .hk-name{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.hk-ghd + .hk-row{ border-top:0; }
/* "Sơ đồ": the two houses, a floor per line, a tile per room (Khoa 01/10/2026). Colours reuse the badges: amber = to
   clean after a check-out, teal = done, grey = DND, white = light clean; nothing dashed (dashed = free room on the board). */
.hk-views{ display:inline-flex; border:1px solid var(--line); border-radius:18px; overflow:hidden; margin-left:auto; }
.hk-vbtn{ border:0; background:var(--paper); font:inherit; font-size:12.5px; padding:0 12px; min-height:34px; color:var(--muted); }
.hk-vbtn.on{ background:var(--cognac); color:#fff; font-weight:600; }
.hk-map{ display:flex; flex-direction:column; gap:14px; margin-top:6px; }
.hk-hhd{ font-size:13px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--muted); margin:0 0 6px; text-align:center; }
.hk-floor{ display:flex; align-items:stretch; gap:6px; margin-bottom:10px; }   /* room for the 'ga N' tab on the border */
.hk-fl{ flex:none; width:24px; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:700; color:var(--muted); }
.hk-tiles{ flex:1; display:flex; flex-wrap:wrap; justify-content:center; gap:6px; }   /* a short floor sits centred (Khoa 02/10) */
.hk-tiles .hk-tile{ flex:none; width:calc((100% - 18px) / 4); }
.hk-tile{ position:relative; min-height:62px; border-radius:12px; border:1px solid var(--line); background:var(--paper); font:inherit;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px; padding:6px 2px; color:var(--ink);
  -webkit-user-select:none; user-select:none; -webkit-touch-callout:none; touch-action:manipulation; }
.hk-tile b{ font-size:17px; line-height:1; }
.hk-tile span{ font-size:11.5px; line-height:1.15; white-space:nowrap; }
.hk-tile i{ position:absolute; top:-8px; right:4px; font-style:normal; font-size:10.5px; font-weight:700; padding:1px 5px; border-radius:8px;
  background:#f6e2b8; color:#8a5a12; }
.hk-tile i.ga-warn{ background:#fbf0cf; color:#8a6a00; border:1px solid #efd98a; }
.hk-tile i.ga-late{ background:#f8dcd6; color:#9b2c1c; border:1px solid #eeb6aa; }
.hk-tile.k-out{ background:#fdf3e3; border-color:#e3c992; color:#7a531a; }
.hk-tile.k-out.urgent{ background:#f9dfb0; border-color:#d9a94e; }
.hk-tile.k-stay{ border-color:#bfe0da; }
.hk-tile.k-done{ background:#e2efed; border-color:#bfe0da; color:#2f7d74; }
.hk-tile.k-dnd{ background:#ecebe8; border-color:#d8d5cf; color:#6b6760; }
.hk-tile.k-arrive{ color:var(--muted); }
/* a guest due out who has not left (Khoa 03/10/2026): a check-out to come — amber like "cần dọn", dashed, so it is not
   the plain look of an empty room waiting for an arrival, nor the solid amber of a room to clean now */
.hk-tile.k-wait{ background:var(--paper); border:1.5px dashed #d9a94e; color:#8a5a12; }
.hk-tile.k-free, .hk-tile.k-off{ background:transparent; border-color:var(--line2); color:#b3a892; }
.hk-tile:disabled{ opacity:1; }
.hk-hint{ margin:10px 2px 0; font-size:12px; color:var(--muted); text-align:center; line-height:1.5; }
.ask-btns .hk-btn.hk-only{ background:var(--paper); color:var(--ink); border-color:var(--line); }
/* one filled button per room sheet — the job that is due (Khoa 03/10/2026, design A of three rendered on the real sheet):
   "Dọn + thay ga" in the colour of the sheets pill above it (yellow due, red late), "Chỉ dọn" only outlined */
.ask-btns .hk-btn.go.hk-both{ background:#9a7400; border-color:#9a7400; }
.ask-btns .hk-btn.go.hk-both.late{ background:#b3493a; border-color:#b3493a; }
.ask-btns:has(.hk-both) .hk-btn.hk-clean{ background:var(--paper); color:#2f7d74; border:1.5px solid var(--cognac); }
.hk-card .hk-act.ga-warn{ background:#fbf0cf; color:#8a6a00; border-color:#efd98a; }
.hk-card .hk-act.ga-late{ background:#f8dcd6; color:#9b2c1c; border-color:#eeb6aa; }
.hk-vbtn{ white-space:nowrap; } .hk-hd small{ white-space:nowrap; }
/* tile state = one-colour line icon + word (emoji drew in five styles — Khoa 01/10) */
.hk-tile span{ display:inline-flex; align-items:center; gap:3px; font-weight:600; }
/* a two-part word breaks where its meaning splits — "✓ Dọn" / "+ thay ga" — instead of running out of a ~88 px tile at
   402 px (Khoa 14/10/2026 screenshot); `.two` = the word carries its own line break (HK_BREAK in app.js) */
.hk-tile span.two{ display:block; text-align:center; }
.hk-tile span.two svg{ vertical-align:-2px; margin-right:3px; }
.hk-tile svg{ width:13px; height:13px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; flex:none; }
.hk-tile.k-stay span{ color:#2f7d74; }
/* the room sheet from the house map: one action per line, "cleaned" filled, the rest quiet (Khoa 02/10) */
.ask-btns:has(.hk-act){ flex-direction:column; }
.ask-btns:has(.hk-act) .hk-btn{ flex:none; width:100%; }
/* the room sheet's sheets line: a pill in the list's colours, big enough to catch the eye (Khoa 02/10) */
.ask-sub .mv{ display:flex; align-items:center; justify-content:center; gap:14px; margin:6px 0 12px; }
.ask-sub .mv-room{ min-width:72px; padding:10px 14px; border-radius:14px; border:1.5px solid var(--line); background:var(--paper);
  font-family:'EB Garamond', serif; font-size:28px; font-weight:600; color:var(--ink); text-align:center; }
.ask-sub .mv-room:last-child{ border-color:var(--cognac); background:color-mix(in srgb, var(--cognac) 12%, var(--paper)); }
.ask-sub .mv-room.mv-none{ font-family:inherit; font-size:15px; font-weight:500; color:var(--muted); border-style:dashed; }
.ask-sub .mv-arr{ font-size:22px; color:var(--cognac); }
.ask-sub .mv-who{ display:block; text-align:center; font-size:14px; line-height:1.5; color:var(--muted); }
.ask-sub .mv-warn{ display:block; margin-top:12px; padding:9px 12px; border-radius:10px; background:#fbf1dc; color:#7a5a12;
  font-size:13.5px; text-align:center; }
.ask-sub .ask-l{ display:block; margin-bottom:8px; }
.ask-sub .ga-pill{ display:inline-block; font-size:14px; font-weight:700; padding:5px 12px; border-radius:10px; }
.ask-sub .ga-pill.ga-ok{ padding-left:0; font-weight:500; }
.hk-tile.ga-due{ border-color:#e6cd7a; }   /* the whole tile says it, not only the corner (Khoa 02/10) */
.hk-tile.ga-late{ border-color:#e2a597; }
.hk-wait{ text-align:center; color:var(--muted); margin:40px 0; font-size:14px; }

/* seat_check's "open this many" beside Trip's open count (Khoa 07/10/2026) */
.cn-rec{ text-decoration:none; margin-left:3px; color:var(--cognac); font-weight:700; }
/* a toast that can take its save back (UX review 09/10/2026) */
.toast.has-undo{ display:flex; align-items:center; gap:14px; padding:6px 6px 6px 18px; white-space:nowrap; }
.toast-undo{ min-height:36px; padding:0 14px; border:0; border-radius:18px; background:rgba(255,255,255,.14);
  color:#f3d9a8; font:inherit; font-weight:700; }
/* "Bỏ nội dung" — the one destructive answer on a discard question, in the red the app already uses for errors */
.hk-btn.go.ask-drop{ background:#b3493a; border-color:#b3493a; }
/* An open layer holds the page still (UX review 09/10/2026: a swipe over a booking popup on Bảng scrolled the list
   behind it 1500 → 4576, ~25 cards; the "+ Thêm" form did the same once its own scroll ran out). `contain` keeps a
   scroll that reaches the end of a card from handing over to the page. */
html:has(.modal:not([hidden])), html:has(.modal:not([hidden])) body{ overflow:hidden; }
.modal, .modal *{ overscroll-behavior:contain; }
/* Layers come in and go out with a short ease (UX review 09/10/2026: every popup and sheet snapped on and off). The
   backdrop fades, the card rises a few pixels — no scale, so nothing measured on an open card changes size. Going out
   is `.out`, set by fadeLayer() in app.js for LAYER_OUT_MS. Reduced motion: the global rule at the top cuts both. */
.modal:not([hidden]){ animation:layer-in .18s ease-out; }
.modal:not([hidden]) > .card{ animation:card-in .22s cubic-bezier(.2,.8,.2,1); }
.modal.out{ animation:layer-out .14s ease-in forwards; }
.modal.out > .card{ animation:card-out .14s ease-in forwards; }
@keyframes layer-in{ from{ opacity:0; } }
@keyframes layer-out{ to{ opacity:0; } }
@keyframes card-in{ from{ opacity:0; transform:translateY(14px); } }
@keyframes card-out{ to{ opacity:0; transform:translateY(10px); } }
/* A pressed card / tile / button sinks a little, so a finger knows it landed (UX review 09/10/2026: nothing answered a
   tap on a card, a room tile or a booking bar). A card does not sink with a button inside it — one press, one motion.
   Calendar bars only darken: a thin bar shrinking reads as a jitter. iOS needs a touch listener for :active (app.js). */
.bcard, .droom, .hk-tile, .hk-btn, .fnb-day, .ch-row{ transition:transform .08s ease-out; }
.bcard:active, .droom:active:not(:has(button:active)), .hk-tile:not(:disabled):active, .hk-btn:not(:disabled):active,
.fnb-day:active, .ch-row:active{ transform:scale(.97); }
.bar:active{ filter:brightness(.9); }
/* Tap targets a thumb can hit (UX review 09/10/2026, measured at 402 px): channel filters 25–28 px tall, "Sơ đồ /
   Danh sách" 34, the breakfast picker in a booking 97×24, a filter chip's ✕ 23×23, price-sheet ticks 13×13. The
   header buttons (36×36) are Khoa's chosen header — not changed here. */
.cc-kinds button{ min-height:36px; }
.hk-vbtn{ min-height:40px; }
/* Safari ignores min-height on a native <select>: draw it ourselves, with the board's own arrow (.tbl .cell-sel) */
.modal .bfsel{ min-height:40px; appearance:none; -webkit-appearance:none; color:var(--ink);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='6'%3E%3Cpath d='M0 0l4 5 4-5z' fill='%23b0a890'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 10px center; }
.rfilter-x{ min-width:36px; min-height:36px; }
.cc-promo{ min-height:32px; }
.cc-promo input[type=checkbox]{ width:20px; height:20px; }
/* Header buttons: they LOOK as before (36 px — Khoa 09/10, after 40 px went live: "hơi to bé bé như cũ đi em đỡ thô"),
   but an invisible 2 px ring makes the finger target 40 px. */
.rf-btn, .add-btn{ position:relative; }
.rf-btn::before, .add-btn::before{ content:''; position:absolute; inset:-2px; }
.rf-btn::before{ inset:-3px; }   /* measured from inside its 1 px border */
#av-jump{ margin-left:6px; }

/* Zalo on the Chat screen (Khoa 12/10/2026): WhatsApp | Zalo switch above the list, quoted line as a small block */
.ch-src{ display:flex; width:fit-content; margin:0 auto 10px; border:1px solid var(--line); border-radius:12px; overflow:hidden; background:var(--paper); }
.ch-src button{ border:0; background:none; padding:7px 16px; font:inherit; font-size:14px; color:var(--muted); min-height:36px; }
.ch-src button.on{ background:var(--cognac); color:#fff; font-weight:600; }
.ch-msg .z-q{ display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; white-space:normal;
  margin:0 0 6px; padding:1px 0 1px 8px; border-left:3px solid var(--line2); font-size:13px; color:var(--muted); cursor:pointer; }
.ch-msg.out .z-q{ color:rgba(255,255,255,.8); border-left-color:rgba(255,255,255,.5); }
.ch-msg .z-q b{ display:block; font-weight:600; }
.ch-msg .z-q.open{ display:block; }
.ch-msg .z-who{ font-size:12px; font-weight:600; color:var(--muted); margin-bottom:2px; }
.ch-msg.pend{ opacity:.7; }


/* ── Kho đồ (Khoa 15/10/2026). Compact: one ~52px line a supply, tap to open its actions — the first version's cards
   needed "vuốt mỗi tay mới coi hết được" (10 supplies ≈ 4 screens). Three sections: daily supplies · door-lock batteries
   (a tile a room) · room assets. Phone first: taps ≥ 44px, nothing under 12px; a desktop puts the sections side by side.
   Colour never alone: the chip SAYS the state. ── */
.pane.kho{ max-width:760px; }
.kho-todo{ margin:4px 2px 12px; padding:9px 12px; border-radius:10px; font-size:14px; font-weight:600; color:#2f6b52; background:#e3efe8; }
.kho-todo.warn{ color:#9a3b2e; background:#f6e3df; }
.kho-todo.gap{ color:#7a5414; background:#f7ecd6; }   /* not counted yet: neither "all fine" (green) nor an alarm (red) */
.kho-last{ display:block; margin-top:2px; font-size:12.5px; font-weight:500; color:var(--muted); }
.kho-pinhead{ display:flex; align-items:center; justify-content:space-between; gap:8px; margin:-2px 2px 8px; }
.kho-pinhead .kho-locksum{ margin:0; }
.kho-pinbulk, .kho-pinall{ min-height:44px; padding:0 14px; border-radius:22px; border:1px solid var(--line); background:var(--card);
  font:inherit; font-size:13.5px; font-weight:600; color:var(--ink); cursor:pointer; white-space:nowrap; }
.kho-pinbulk.on{ background:var(--ink); color:#fff; border-color:var(--ink); }
.kho-pinall{ width:100%; margin-top:6px; }
.kho-pinbar{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-top:10px; }
.kho-pinbar .kho-q{ display:flex; align-items:center; gap:6px; margin:0; }
.kho-pinday{ min-height:44px; padding:0 8px; border:1px solid var(--line); border-radius:10px; font:inherit; font-size:15px; background:#fff; }
.kho-lock.pick{ background:#e3efe8; border-color:#2f6b52; }
.kho-sec{ margin-bottom:14px; min-width:0; }
.kho-box{ background:var(--paper); border:1px solid var(--line); border-radius:12px; overflow:hidden; }
.kho-line{ display:flex; align-items:center; gap:10px; width:100%; min-height:52px; padding:6px 12px; border:0; background:none;
  font:inherit; text-align:left; color:var(--ink); cursor:pointer; box-shadow:inset 3px 0 0 transparent; }
.kho-line + .kho-line, .kho-panel + .kho-line{ border-top:1px solid var(--line2); }
.kho-line.s-red{ box-shadow:inset 3px 0 0 #b3493a; } .kho-line.s-amber{ box-shadow:inset 3px 0 0 #c08a2e; }
.kho-line.open{ background:var(--cream); }
.kho-txt{ flex:1; min-width:0; display:flex; flex-direction:column; }
.kho-nm{ font-size:14.5px; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.kho-sub{ font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.kho-chip{ flex:none; font-size:12px; font-weight:700; padding:2px 8px; border-radius:10px; background:var(--line2); color:var(--muted); }
.s-red .kho-chip{ background:#f6e3df; color:#9a3b2e; } .s-amber .kho-chip{ background:#f7ecd6; color:#7a5414; }
.s-green .kho-chip{ background:#e3efe8; color:#2f6b52; }
.kho-panel{ padding:4px 12px 12px; background:var(--cream); }
.kho-rule{ font-size:12px; color:var(--muted); line-height:1.4; }
.kho-acts{ display:flex; gap:6px; margin-top:8px; flex-wrap:wrap; }
.kho-act{ min-height:44px; padding:0 14px; border:1px solid var(--line); border-radius:22px; background:#fff; font:inherit; font-size:13px; color:var(--ink); }
.kho-act.on{ border-color:var(--cognac); }
.kho-form{ display:flex; flex-wrap:wrap; gap:8px; margin-top:10px; align-items:center; }
.kho-q{ flex-basis:100%; font-size:13px; color:var(--ink); }
.kho-form input{ min-height:44px; box-sizing:border-box; border:1px solid var(--line); border-radius:10px; padding:0 12px; font:inherit; font-size:16px; }
.kho-form input[type=number]{ width:110px; }
.kho-form input[type=text]{ flex:1; min-width:140px; }
.kho-save{ min-height:44px; padding:0 20px; border:0; border-radius:22px; background:var(--cognac); color:#fff; font:inherit; font-size:14px; font-weight:600; }
.kho-panel > .kho-save{ margin-top:8px; }
.kho-save:disabled{ opacity:.6; }
/* door locks: a tile a room, 7 to a row on a phone; the words sit in .kho-locksum above (stage 3) */
.kho-locks{ display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:5px; }
.kho-lock{ min-height:44px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:1px; padding:4px 2px;
  border:1px solid var(--line); border-radius:10px; background:var(--paper); font:inherit; color:var(--ink); cursor:pointer; }
.kho-lock b{ font-size:14px; } .kho-lock small{ font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; }
.kho-lock.s-over{ border-color:#b3493a; background:#f6e3df; } .kho-lock.s-over small{ color:#9a3b2e; font-weight:700; }
.kho-lock.s-soon{ border-color:#c08a2e; background:#f7ecd6; } .kho-lock.s-soon small{ color:#7a5414; font-weight:700; }
.kho-lock.sel{ outline:2px solid var(--cognac); outline-offset:1px; }
.kho-sec > .kho-panel{ margin-top:8px; border:1px solid var(--line); border-radius:12px; padding-top:10px; }
.kho-foot{ margin:6px 4px 0; font-size:12px; color:var(--muted); }
/* stage 3 (Khoa 15/10/2026: "Em xem tối ưu hơn nhé"): what needs nothing folds away; one count sheet; the shopping list */
.kho-tools{ display:flex; gap:8px; margin:0 0 12px; }
.kho-count, .kho-buy{ flex:1; min-height:44px; border:1px solid var(--line); border-radius:22px; background:var(--paper); font:inherit; font-size:14px;
  font-weight:600; color:var(--ink); cursor:pointer; }
.kho-count{ background:var(--cognac); border-color:var(--cognac); color:#fff; }
.kho-export{ display:block; width:100%; min-height:44px; margin:14px 0 4px; border:1px solid var(--line); border-radius:22px;
  background:var(--paper); color:var(--ink); font:inherit; font-size:14px; }
.kho-export:disabled{ opacity:.7; }
.kho-count.on, .kho-buy.on{ outline:2px solid var(--cognac); outline-offset:1px; }
.kho-count.on{ background:var(--paper); color:var(--ink); }
.kho-hint{ margin:0 2px 8px; font-size:13px; color:var(--muted); }
.kho-fold + .kho-fold, .kho-line + .kho-fold, .kho-panel + .kho-fold{ border-top:1px solid var(--line2); }
.kho-fold > summary{ min-height:44px; display:flex; align-items:center; padding:0 12px; font-size:13.5px; color:var(--muted); cursor:pointer; list-style:none; }
.kho-fold > summary::-webkit-details-marker{ display:none; }
.kho-fold > summary::after{ content:'›'; margin-left:auto; font-size:18px; transition:transform .15s; }
.kho-fold[open] > summary::after{ transform:rotate(90deg); }
.kho-fold > .kho-line{ border-top:1px solid var(--line2); }
.kho-locksum{ margin:-2px 2px 8px; font-size:13px; color:var(--ink); }
.kho-hist{ margin-top:10px; padding-top:8px; border-top:1px dashed var(--line); font-size:12px; color:var(--muted); line-height:1.6;
  font-variant-numeric:tabular-nums; }
.kho-buylist{ margin:0 0 12px; padding:10px 12px; background:var(--paper); border:1px solid var(--line); border-radius:12px; }
.kho-buytxt{ margin:0 0 8px; font:inherit; font-size:14px; line-height:1.55; white-space:pre-wrap; }
.kho-copy{ min-height:44px; padding:0 18px; border:1px solid var(--cognac); border-radius:22px; background:#fff; font:inherit; font-size:14px;
  color:var(--ink); cursor:pointer; }
.kho-sheet{ background:var(--paper); border:1px solid var(--line); border-radius:12px; padding:8px 12px 0; }
.kho-srow{ display:flex; align-items:center; gap:10px; min-height:52px; border-top:1px solid var(--line2); }
.kho-srow input{ width:96px; min-height:44px; box-sizing:border-box; border:1px solid var(--line); border-radius:10px; padding:0 10px; font:inherit;
  font-size:16px; text-align:right; }
.kho-sbar{ position:sticky; bottom:0; margin:0 -12px; padding:10px 12px calc(10px + env(safe-area-inset-bottom)); background:var(--paper);
  border-top:1px solid var(--line); }
.kho-sbar .kho-save{ width:100%; }
/* desktop (≥1024): its own layout — tables, counts typed in, locks by floor (Khoa: "Giao diện desktop khác mobile nữa") */
@media(min-width:1024px){
  .pane.kho{ max-width:none; }
  .kho-desk{ display:grid; grid-template-columns:minmax(0,2fr) minmax(0,1fr); gap:22px; align-items:start; }
  .kho-desk .kho-a{ grid-row:span 2; }
  .kho-table{ width:100%; border-collapse:separate; border-spacing:0; background:var(--paper); border:1px solid var(--line); border-radius:12px;
    overflow:hidden; font-size:13.5px; font-variant-numeric:tabular-nums; }
  .kho-table th{ text-align:left; font-size:12px; font-weight:600; color:var(--muted); padding:8px 8px; border-bottom:1px solid var(--line); white-space:nowrap; }
  @media(max-width:1279px){ .kho-table th{ white-space:normal; } }
  .kho-table th.sort{ cursor:pointer; user-select:none; }
  .kho-table th.sort:hover{ color:var(--ink); }
  .kho-table th.up::after{ content:' ▲'; font-size:9px; } .kho-table th.down::after{ content:' ▼'; font-size:9px; }
  .kho-table td{ padding:4px 8px; border-top:1px solid var(--line2); height:34px; }
  .kho-table tbody tr:first-child td{ border-top:0; }
  .kho-table td.num, .kho-table .kho-chip{ white-space:nowrap; }
  .kho-table tr.s-red td:first-child{ box-shadow:inset 3px 0 0 #b3493a; } .kho-table tr.s-amber td:first-child{ box-shadow:inset 3px 0 0 #c08a2e; }
  .kho-name{ border:0; background:none; padding:0; font:inherit; font-weight:600; color:var(--ink); cursor:pointer; text-align:left; }
  .kho-name:hover, .kho-name.open{ color:var(--cognac); text-decoration:underline; }
  .kho-table .kho-cnt{ width:72px; text-align:right; }
  .kho-table .kho-cnt input{ width:64px; height:30px; box-sizing:border-box; border:1px solid var(--line); border-radius:8px; padding:0 8px;
    font:inherit; text-align:right; }
  .kho-table .kho-prow td{ padding:0; }
  .kho-table .kho-prow .kho-panel{ padding:8px 12px 12px; }
  .kho-dbar{ display:flex; align-items:center; gap:12px; margin-top:10px; }
  .kho-dbar .kho-save{ min-height:38px; }
  .kho-desk .kho-buylist{ margin:0; }
  .kho-floor{ display:grid; grid-template-columns:48px minmax(0,1fr); gap:8px; align-items:center; margin-bottom:6px; }
  .kho-floor > b{ font-size:12.5px; color:var(--muted); font-weight:600; }
  .kho-floor .kho-locks{ grid-template-columns:repeat(8,minmax(0,1fr)); gap:4px; }
  .kho-floor .kho-lock{ min-height:38px; padding:2px 0; }
  .kho-floor .kho-lock b{ font-size:13px; letter-spacing:-.2px; }
  .kho-floor .kho-lock.word{ grid-column:span 2; }
  .kho-recent{ list-style:none; margin:0; padding:8px 12px; background:var(--paper); border:1px solid var(--line); border-radius:12px; font-size:12.5px;
    line-height:1.75; color:var(--ink); font-variant-numeric:tabular-nums; }
}
@media(min-width:1280px){
  .kho-desk{ grid-template-columns:minmax(0,2.1fr) minmax(0,1.15fr) minmax(0,1fr); }
  .kho-desk .kho-a{ grid-row:auto; }
}
