/* style.css
   OMC Queue (Restaurant Queue Professional) - Custom overrides บน Bootstrap 5
   คงไว้เฉพาะส่วนที่ Bootstrap ไม่มีให้ (สี Primary ของธีม, จอ Display, ใบพิมพ์ 80mm)
   ตัวแปรสีตั้งชื่อตาม Bootstrap CSS variable เพื่อให้ระบบ SaaS อื่นในอนาคต override ธีมได้จากไฟล์เดียว */

:root {
    /* Spacing System (CLAUDE.md ข้อ 7 · DESIGN_GUIDELINE ข้อ 3) - ระยะห่างทุกค่าต้องมาจากสเกลนี้
       ‼️ ประกาศไว้ก่อนค่าอื่นเสมอ เพราะเคยมีจุดที่เรียก var(--space-3, 16px) โดยไม่มีการประกาศ
          แล้วรอดมาได้เพราะ fallback - ค่า fallback ปิดบังตัวแปรที่หายไปจนไม่มีใครเห็นปัญหา */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 16px;
    --space-4: 24px;
    --space-5: 32px;
    --space-6: 48px;

    --bs-primary: #0f766e;
    --bs-primary-rgb: 15, 118, 110;
    --bs-primary-soft: #e2efee;
    --bs-primary-strong: #0d645e;
    --bs-primary-strong-rgb: 13, 100, 94;

    /* ---------- สีลิงก์ของทั้งระบบ ----------
       ‼️ Bootstrap ไม่ได้ถูกตั้งสีลิงก์ไว้เลย ลิงก์เปล่า ๆ และปุ่ม .btn-link ทุกอันจึงเป็นสีน้ำเงิน
          #0d6efd ของ Bootstrap ตรง ๆ ซึ่ง CLAUDE.md ข้อ 0 ห้ามไว้ และตีกับสีหลักของระบบ
          (ผู้ใช้แจ้ง 2026-09-16 - เจอทั้งในโซนหลังบ้าน หน้าสาธารณะ และวิดเจ็ตแจ้งปัญหาที่อยู่ทุกหน้า)
       ‼️ ผูกกับ --bs-primary ไม่ใช่ค่าคงที่ ลิงก์จึงเปลี่ยนตามสีของแต่ละร้านเหมือนปุ่มและเมนู
       ‼️ แก้ที่ตัวแปรของ Bootstrap จุดเดียว ไม่ไล่เติม class ตามลิงก์ทีละอัน - ลิงก์ที่เพิ่มใหม่ทีหลัง
          จะได้สีถูกต้องเองโดยไม่มีใครต้องจำ (.btn ตัวอื่น/.nav-link/.dropdown-item ตั้งสีของตัวเอง
          อยู่แล้วและไม่ได้อ่านตัวแปรชุดนี้ จึงไม่ถูกกระทบ)
       ‼️ หน้าสาธารณะมีจานสีของตัวเอง (--omc-*) จึงทับค่าชุดนี้อีกทีที่ body.page-home/page-customer */
    --bs-link-color: var(--bs-primary);
    --bs-link-color-rgb: var(--bs-primary-rgb);
    --bs-link-hover-color: var(--bs-primary-strong);
    --bs-link-hover-color-rgb: var(--bs-primary-strong-rgb);
    --header-bg: #ffffff;
    --header-text: #1e293b;
    --header-overlay-rgb: 15, 23, 42;

    /* ---------- สีแบรนด์ของ LINE ----------
       ‼️ เป็น "สีของแบรนด์ภายนอก" ไม่ใช่สีของธีมร้าน จึงต้องคงที่เสมอ ห้ามผูกกับ --bs-primary
          และห้ามเปลี่ยนตามโหมดมืด - ปุ่มนี้ต้องดูเป็นปุ่ม LINE ที่ลูกค้าจำได้ทันทีในทุกร้านและทุกโหมด
          ถ้าปล่อยให้ตามสีร้าน มันจะกลายเป็นปุ่มสีร้านที่บังเอิญเขียนว่า LINE ซึ่งคนไม่กด
       ‼️ #06c755 คือค่าทางการของ LINE ห้ามปรับเฉดเอง */
    --line-green: #06c755;
    --line-green-strong: #05a948;
    --line-green-darker: #04913d;
    --line-on-green: #ffffff;
}

[data-bs-theme="dark"] {
    --bs-primary-soft: #1b3744;
}

.btn-primary {
    --bs-btn-bg: var(--bs-primary);
    --bs-btn-border-color: var(--bs-primary);
    --bs-btn-hover-bg: var(--bs-primary-strong);
    --bs-btn-hover-border-color: var(--bs-primary-strong);
    --bs-btn-active-bg: var(--bs-primary-strong);
    --bs-btn-active-border-color: var(--bs-primary-strong);
    --bs-btn-disabled-bg: var(--bs-primary);
    --bs-btn-disabled-border-color: var(--bs-primary);
}

/* ---------- ปุ่มของ LINE ----------
   ใช้กับ "การกระทำที่เป็นของ LINE จริง ๆ" เท่านั้น (ผูกคิวกับ LINE / เข้าสู่ระบบด้วย LINE / เพิ่มเพื่อน)
   ‼️ ห้ามเอาไปใช้กับปุ่มทั่วไปของระบบ สีเขียวนี้ต้องแปลว่า "กดแล้วไป LINE" เท่านั้น

   ‼️ ตัวอักษรขาวบนเขียว LINE มีคอนทราสต์ 2.23:1 ซึ่งต่ำกว่าเกณฑ์ WCAG AA (4.5:1) - เป็นการยอมรับ
      โดยตั้งใจตามแนวทางแบรนด์ของ LINE เอง (ปุ่มทางการของ LINE เป็นขาวบนเขียวชุดนี้) การเปลี่ยนเป็น
      เขียวเข้มพอจะผ่าน AA จะทำให้ไม่ใช่สีของ LINE อีกต่อไป จึงชดเชยด้วยสามอย่างแทน:
        1. ตัวอักษรหนาและใหญ่กว่าปุ่มปกติ (อ่านง่ายกว่าตัวบางขนาดปกติมาก)
        2. มีเส้นขอบเข้มกว่าพื้นหนึ่งขั้น = ขอบเขตของปุ่มยังชัดตาม WCAG 1.4.11 (กติกาเดียวกับกราฟแท่ง)
        3. วงแหวนโฟกัสเป็นสีเขียวเข้ม มองเห็นได้ชัดบนพื้นขาวของการ์ด
      ‼️ ห้ามลบเส้นขอบออก และห้ามลดน้ำหนักตัวอักษร - สองอย่างนี้คือสิ่งที่ชดเชยคอนทราสต์อยู่ */
.btn-line {
    --bs-btn-color: var(--line-on-green);
    --bs-btn-bg: var(--line-green);
    --bs-btn-border-color: var(--line-green-strong);
    --bs-btn-hover-color: var(--line-on-green);
    --bs-btn-hover-bg: var(--line-green-strong);
    --bs-btn-hover-border-color: var(--line-green-darker);
    --bs-btn-active-color: var(--line-on-green);
    --bs-btn-active-bg: var(--line-green-darker);
    --bs-btn-active-border-color: var(--line-green-darker);
    --bs-btn-disabled-color: var(--line-on-green);
    --bs-btn-disabled-bg: var(--line-green);
    --bs-btn-disabled-border-color: var(--line-green-strong);
    --bs-btn-focus-shadow-rgb: 5, 169, 72;
    font-weight: 600;
}

/* ไอคอน LINE ใหญ่กว่าตัวอักษรหนึ่งขั้น - เป็นตัวที่ทำให้คนรู้ว่านี่คือปุ่ม LINE ก่อนจะอ่านข้อความด้วยซ้ำ */
.btn-line .bi {
    font-size: 1.25em;
    vertical-align: -0.12em;
}

/* Bootstrap ไม่ผูก .btn-outline-primary กับ --bs-primary ให้โดย default (hardcode #0d6efd)
   ผูกเพิ่มเพื่อให้ปุ่ม outline (เช่น ปุ่มเลือกจำนวนลูกค้า) ตามสีธีมที่ตั้งค่าไว้เหมือน .btn-primary */
.btn-outline-primary {
    --bs-btn-color: var(--bs-primary);
    --bs-btn-border-color: var(--bs-primary);
    --bs-btn-hover-bg: var(--bs-primary);
    --bs-btn-hover-border-color: var(--bs-primary);
    --bs-btn-active-bg: var(--bs-primary);
    --bs-btn-active-border-color: var(--bs-primary);
    --bs-btn-disabled-color: var(--bs-primary);
    --bs-btn-disabled-border-color: var(--bs-primary);
}

.text-primary {
    color: var(--bs-primary) !important;
}

.js-view-detail {
    cursor: pointer;
}

/* ปุ่มสลับ Light/Dark บนหน้าเช็คสถานะคิวของลูกค้า (queue.php) - เดิมใช้ btn-outline-light เปล่า ๆ
   ในแถบสีหลัก ดูไม่เข้าพวกกับปุ่มไอคอนพื้นขาวที่หน้าอื่น (.icon-btn) ทำให้พื้นหลังขาวชัดเจน
   ตั้งแต่แรกโหลด แล้วสลับเป็นพื้นเข้ม/ไอคอนสว่างเองอัตโนมัติเมื่อสลับเป็น Dark Mode */
body.page-customer #btn-theme-toggle {
    width: 36px;
    height: 36px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    background: #f1f3f5;
    border: 1px solid #e2e5ea;
    color: var(--bs-primary);
}

body.page-customer #btn-theme-toggle:hover {
    background: #e9ecef;
    color: var(--bs-primary);
}

[data-bs-theme="dark"] body.page-customer #btn-theme-toggle {
    background: #334155;
    border-color: #475569;
    color: #e2e8f0;
}

[data-bs-theme="dark"] body.page-customer #btn-theme-toggle:hover {
    background: #3f4d63;
    color: #fff;
}

/* ---------- Header ขาว (เหมือน .app-topbar ของ page-admin) + โลโก้ร้าน ---------- */
body.page-customer .customer-topbar {
    background: var(--header-bg);
    color: var(--header-text);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.75rem 1.25rem;
}

body.page-customer .customer-topbar .app-brand {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--header-text);
}

body.page-customer .app-brand-icon {
    font-size: 1.3rem;
    color: var(--bs-primary);
    display: inline-flex;
    align-items: center;
}

body.page-customer .app-brand-icon img {
    width: 44px;
    height: 44px;
    object-fit: contain;
    border-radius: 12px;
}

[data-bs-theme="dark"] body.page-customer .customer-topbar {
    background: #1e293b;
    color: #e2e8f0;
}

/* ปุ่มเลือกประเภทคิวที่หน้ารับคิว - ใหญ่ กดง่าย เหมาะกับจอทัชสกรีนหน้าร้าน
   โชว์ code ตัวใหญ่เสมอ + label (ถ้ามี) เป็น text เล็กใต้ code - ดู .type-select-btn-code/.type-select-btn-label
   ใช้ height ตายตัว (ไม่ใช่ min-height) เพื่อล็อคขนาดการ์ด "รับคิวใหม่" ไว้เสมอ ไม่ให้ยุบเวลาประเภทคิวไม่มี label
   (ปุ่มที่ไม่มี label จะโชว์แค่ code บรรทัดเดียว แต่กล่องปุ่ม/ความสูงทั้งแถวยังคงเท่าเดิมเพราะ height ไม่ใช่ min-height)
   ไม่บีบ/ตัดคำ (flex-shrink: 0 + white-space: nowrap) เพราะอยู่ในแถวเลื่อนแนวนอน ดู .type-select-scroll */
.type-select-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.2rem;
    height: 76px;
    padding: 0.5rem 1.25rem;
    border-radius: 10px;
    white-space: nowrap;
    overflow: hidden;
    flex-shrink: 0;
}

.type-select-btn-code {
    font-size: 1.7rem;
    font-weight: 700;
    line-height: 1;
}

.type-select-btn-label {
    font-size: 0.62rem;
    font-weight: 500;
    opacity: 0.85;
    line-height: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

/* ปุ่มลูกศรเลื่อนหน้าประเภทคิว (โชว์เฉพาะตอนมีประเภทคิวเกิน 6 ประเภท) - ทำเป็นวงกลมเล็ก ๆ มองง่าย
   ไม่ต้องสูงเท่าปุ่มเลือกประเภท เพราะอยู่ในแถว align-items-center (ดู console.php) จัดกึ่งกลางแนวตั้งให้เองอยู่แล้ว */
.type-page-nav {
    width: 30px;
    height: 30px;
    padding: 0;
    font-size: 0.8rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    flex-shrink: 0;
}

.type-select-btn-placeholder {
    visibility: hidden;
    pointer-events: none;
}

/* ======================================================
   Nocturne (light) theme - เฉพาะ console.php (Queue Dashboard)
   scope ด้วย body.page-admin ทั้งหมด เพื่อไม่กระทบหน้าอื่น (login, report, settings, display, queue)
   ที่ยังใช้ card shadow-sm/border-0 + navbar bg-primary แบบเดิม
   ====================================================== */

body.page-admin {
    --nc-bg: #eceef4;
    --nc-column-bg: #f6f7fb;
    --nc-surface: #ffffff;
    --nc-border: #e2e5ee;
    --nc-text: #23263a;
    --nc-text-muted: #71768a;
    --nc-text-faint: #9aa0b2;
    --nc-success: #2f9e6b;
    --nc-success-soft: #e5f5ee;
    --nc-warning: #c08a2e;
    --nc-warning-soft: #faf1e2;
    --nc-danger: #cc5a55;
    --nc-danger-soft: #faeceb;
    --nc-info: #3d6fd6;
    --nc-info-soft: #e8eefb;
    --nc-teal: #2f8fa0;
    --nc-teal-soft: #e6f2f3;

    background: var(--nc-bg);
    color: var(--nc-text);
    font-family: "Segoe UI", "Leelawadee UI", "Noto Sans Thai", Tahoma, sans-serif;
}

/* ---------- Nocturne (dark) - เวอร์ชันมืดของธีมเดียวกันด้านบน ---------- */
[data-bs-theme="dark"] body.page-admin {
    --nc-bg: #0f172a;
    --nc-column-bg: #16213a;
    --nc-surface: #1e293b;
    --nc-border: #334155;
    --nc-text: #e2e8f0;
    --nc-text-muted: #94a3b8;
    --nc-text-faint: #64748b;
    --nc-success: #4ade80;
    --nc-success-soft: #14532d;
    --nc-warning: #fbbf24;
    --nc-warning-soft: #451a03;
    --nc-danger: #f87171;
    --nc-danger-soft: #450a0a;
    --nc-info: #60a5fa;
    --nc-info-soft: #1e3a5f;
    --nc-teal: #5eead4;
    --nc-teal-soft: #134e4a;

    --header-bg: #1e293b;
    --header-text: #e2e8f0;
    --header-overlay-rgb: 255, 255, 255;
}

/* ---------- /admin sidebar shell ---------- */
body.page-admin .admin-shell {
    display: flex;
    height: 100vh;
}

body.page-admin .admin-sidebar {
    flex: 0 0 300px;
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--nc-border);
    background: var(--nc-surface);
    min-height: 0;
}

body.page-admin .admin-sidebar-brand {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 1rem 1.25rem;
    font-weight: 600;
    color: var(--nc-text);
    border-bottom: 1px solid var(--nc-border);
}

body.page-admin .admin-sidebar-brand-icon {
    font-size: 1.3rem;
    color: var(--bs-primary);
    display: inline-flex;
    align-items: center;
}

body.page-admin .admin-sidebar-brand-icon img {
    width: 56px;
    height: 56px;
    object-fit: contain;
    border-radius: 12px;
}

body.page-admin .admin-sidebar-nav {
    flex: 1 1 auto;
    /* Bootstrap .nav ตั้ง flex-wrap: wrap ไว้เป็นค่าเริ่มต้น - ถ้าไม่ปิด พอเนื้อหาสูงเกินพื้นที่ (เปิดหลาย
       กลุ่มพร้อมกัน) แกน column จะ wrap เป็นคอลัมน์ใหม่ทางขวาแทนที่จะ scroll ลงตามที่ overflow-y: auto ตั้งใจไว้ */
    flex-wrap: nowrap;
    padding: 0.75rem;
    overflow-y: auto;
}

body.page-admin .admin-sidebar-nav .nav-link {
    display: block;
    width: 100%;
    text-align: left;
    color: var(--nc-text);
    /* ‼️ เมนู sidebar ไม่มีกรอบและไม่มีพื้นการ์ดตามที่ผู้ใช้กำหนด - เห็นเป็นกรอบเฉพาะตัวที่ hover/active
       เท่านั้น (border ยังอยู่แต่โปร่งใส เพื่อไม่ให้ความสูง/ตำแหน่งของแถวขยับตอนเปลี่ยนสถานะ) */
    background: transparent;
    border: 1px solid transparent;
    border-radius: 8px;
    padding: 0.6rem 0.9rem;
    margin-bottom: 0.4rem;
    transition: background .15s ease, border-color .15s ease, box-shadow .15s ease;
}

body.page-admin .admin-sidebar-nav .nav-link:hover {
    background: var(--bs-primary-soft);
}

/* ---------- จุดแดงบอกเรื่องที่ยังไม่มีใครรับ (ผู้สนใจติดต่อ / เรื่องที่แจ้งเข้ามา) ----------
   ‼️ ต้องผูกกับ .nav-link ด้วยเพื่อให้ specificity สูงกว่า "display: block" ของกฎด้านบน
   ไม่งั้นเลขจะตกไปอยู่คนละบรรทัดกับป้ายเมนูแทนที่จะชิดขวาในแถวเดียวกัน */
body.page-admin .admin-sidebar-nav .nav-link:has(.admin-sidebar-badge) {
    display: flex;
    align-items: center;
}

body.page-admin .admin-sidebar-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    margin-left: auto;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--nc-danger);
    color: #fff;
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

/* เมนูที่กำลังเปิดอยู่มีพื้นเป็นสีหลักอยู่แล้ว จุดแดงบนพื้นนั้นจะจมหาย - สลับเป็นพื้นขาวตัวหนังสือแดงแทน
   ‼️ ใช้แดงเข้มค่าคงที่ ไม่ใช่ --nc-danger เพราะใน Dark Mode ตัวแปรนั้นเป็นแดงอ่อน (#f87171)
   ซึ่งวางบนพื้นขาวแล้วคอนทราสต์ต่ำจนอ่านเลขไม่ออก */
body.page-admin .admin-sidebar-nav .nav-link.active .admin-sidebar-badge {
    background: #fff;
    color: #b42318;
}

body.page-admin .admin-sidebar-nav .nav-link.active {
    background-color: var(--bs-primary);
    border-color: var(--bs-primary);
    color: #fff;
    box-shadow: 0 2px 6px rgba(var(--bs-primary-rgb), 0.35);
}

/* หัวข้อกลุ่มใหญ่ที่ขยาย/ย่อได้ (ตั้งค่าร้าน, รายงานและกิจกรรม) - หน้าตาเหมือน nav-link ปกติ
   (ได้ style จาก .nav-link ด้านบนฟรีอยู่แล้ว) ต่างแค่มี chevron บอกสถานะขยาย/ย่อ
   selector ต้องผูกกับ .nav-link ด้วย (ไม่ใช่แค่ .admin-sidebar-group-toggle เดี่ยว ๆ) เพื่อให้ specificity
   สูงกว่า "body.page-admin .admin-sidebar-nav .nav-link { display: block }" ไม่งั้น display:flex จะโดนแทนที่
   จนลูกศร ms-auto ไม่ถูกผลักไปชิดขวา */
body.page-admin .admin-sidebar-nav .nav-link.admin-sidebar-group-toggle {
    display: flex;
    align-items: center;
    width: 100%;
}

/* กลุ่มที่มีเมนูย่อยกำลัง active อยู่ข้างใน - ให้หัวข้อกลุ่ม (ตัวหลัก) เข้มเต็มเหมือนเมนู active ปกติ
   แม้ตัวมันเองไม่ได้ถูกเลือกตรง ๆ เพื่อให้รู้ว่า "อยู่ในหมวดนี้" ชัดเจนที่สุด (ใช้ :has() เช็คจากปุ่มลูก
   ใน .admin-sidebar-group ถัดจากตัวเอง) - เมนูย่อยด้านในใช้สีรองลงมาแทน (ดู .admin-sidebar-group .nav-link.active) */
body.page-admin .admin-sidebar-group-toggle:has(+ .admin-sidebar-group .nav-link.active) {
    background-color: var(--bs-primary);
    border-color: var(--bs-primary);
    color: #fff;
    box-shadow: 0 2px 6px rgba(var(--bs-primary-rgb), 0.35);
}

body.page-admin .admin-sidebar-group-chevron {
    transition: transform .15s ease;
}

body.page-admin .admin-sidebar-group-toggle[aria-expanded="true"] .admin-sidebar-group-chevron {
    transform: rotate(180deg);
}

/* เมนูย่อยในกลุ่ม - เยื้องซ้ายเข้ามาให้แคบลง (เยื้องซ้ายอย่างเดียว ไม่เพิ่ม padding-right) เพื่อให้ขอบขวา
   ของปุ่มลูกยังชิดขวาเสมอกับปุ่มหัวข้อกลุ่มด้านบน เห็นลำดับชั้นชัดเจนว่าเป็นเมนูรอง */
/* เส้นตั้งยาวข้างเมนูย่อย - บอกด้วยสายตาว่าทั้งกลุ่มนี้อยู่ใต้หัวข้อด้านบน (ไม่ใช่เมนูเดี่ยวที่เยื้องเฉย ๆ)
   ‼️ margin-left ต้องเท่ากับ "กึ่งกลางไอคอนของหัวข้อกลุ่ม" = padding ซ้ายของ .nav-link (0.9rem)
   + ครึ่งหนึ่งของความกว้างไอคอน (1.1rem / 2) เส้นจึงลากลงมาจากใต้ไอคอนพอดี ถ้าแก้ค่าใดค่าหนึ่ง
   ต้องแก้ที่นี่ตามด้วย ไม่งั้นเส้นจะเยื้องออกจากไอคอน */
body.page-admin .admin-sidebar-group {
    margin-left: 1.45rem;
    padding-left: 0.7rem;
    border-left: 1px solid var(--nc-border);
}

body.page-admin .admin-sidebar-group .nav-link {
    font-size: 0.9rem;
    padding: 0.4rem 0.75rem;
    margin-bottom: 0.3rem;
}

/* เมนูย่อยที่ active - ใช้สีรองลงมา (soft) ไม่ใช่สีทึบเข้มแบบเมนูหลัก เพราะหัวข้อกลุ่มด้านบน
   (:has() ด้านบน) เด่นแทนไปแล้ว ตัวรองไม่ควรแย่งความสนใจจากตัวหลัก */
body.page-admin .admin-sidebar-group .nav-link.active {
    background-color: var(--bs-primary-soft);
    color: var(--bs-primary-strong);
    font-weight: 600;
    box-shadow: none;
}

/* จัดให้ icon นำหน้าทุกปุ่มใน sidebar (หลักและรอง) กินความกว้างเท่ากันเสมอ ไม่ว่า glyph จะกว้าง/แคบแค่ไหน
   (เช่น bi-list-ul แคบกว่า bi-speedometer2) เพื่อให้ตัวอักษรที่ตามมาเริ่มที่ตำแหน่ง X เดียวกันทุกแถว */
body.page-admin .admin-sidebar-nav .nav-link > i:first-child {
    display: inline-block;
    width: 1.1rem;
    text-align: center;
}

body.page-admin .admin-content {
    flex: 1 1 auto;
    /* Phase 19.2 - ไม่มี padding ด้านบน เพื่อให้ .app-navbar (ลูกตัวแรกของทุกหน้าที่ใช้ shell) แนบขอบ
       บนสุดของพื้นที่เนื้อหาพอดี ระยะห่างใต้ navbar มาจาก margin-bottom ของตัว navbar เอง */
    padding: 0 1.5rem 1.5rem;
    overflow-y: auto;
    min-height: 0;
    width: 100%;
}

/* ---------- จอเล็ก: เมนูข้างเป็น "ลิ้นชัก" เลื่อนออกมาทับเนื้อหา (off-canvas) ----------
   ‼️ ของเดิมวางเมนูไว้ "บน" เนื้อหาในสายเลย์เอาต์ (.admin-shell เป็น column) แล้วจำกัดความสูงเมนู 45vh
      ผลจริงคือแถบแบรนด์ + เมนูกินพื้นที่จอไปกว่าครึ่ง เนื้อหาจริงของหน้าเริ่มที่ ~55% ของความสูงจอ
      ต้องเลื่อนลงทุกครั้งกว่าจะเห็นข้อมูล (ผู้ใช้แจ้ง 2026-09-03) จึงเปลี่ยนเป็นลิ้นชักที่ซ่อนไว้โดย
      ปริยาย เปิดด้วยปุ่ม ☰ บนแถบบน แล้วเลื่อนเข้ามาทับเนื้อหาชั่วคราว
   ‼️ ลิ้นชักเป็น position: fixed จึงไม่ใช่ flex item ของ .admin-shell อีก - ไม่ต้องสั่ง flex-direction:
      column แล้ว และเนื้อหาได้ความกว้างเต็มจอเองโดยไม่ต้องแก้ .admin-content
   ‼️ ลำดับชั้น z-index ต้องอยู่ระหว่างปุ่มแจ้งปัญหา (1050 - inline style ที่ render_support_widget())
      กับ modal ของ Bootstrap (1055) เพื่อให้ลิ้นชักทับปุ่มแจ้งปัญหาได้ แต่ไม่ทับ modal ที่เปิดค้างอยู่ */
@media (max-width: 767.98px) {
    body.page-admin .admin-sidebar {
        position: fixed;
        top: 0;
        bottom: 0;
        left: 0;
        /* ‼️ ไม่เต็มความกว้างจอโดยเจตนา - เหลือขอบขวาให้เห็นเนื้อหาที่ถูกทับ ผู้ใช้จึงรู้ว่ายังอยู่หน้าเดิม
           และมีที่ให้แตะปิดลิ้นชักโดยไม่ต้องหาปุ่มปิด */
        width: min(300px, 86vw);
        z-index: 1052;
        overflow-y: auto;
        transform: translateX(-100%);
        transition: transform .2s ease;
    }

    html.sidebar-open body.page-admin .admin-sidebar {
        transform: none;
        box-shadow: 0 0 24px rgb(15 23 42 / 18%);
    }

    /* ‼️ พื้นทึบหลังลิ้นชักสร้างจาก JS (initSidebarToggle) ไม่ใช่ markup เพราะ .admin-shell ถูกเขียนซ้ำ
       ใน 34 หน้า การเพิ่ม element จริงต้องไปแก้ทั้ง 34 ไฟล์ ซึ่งเป็น copy-paste ข้ามหน้าที่ข้อ 7 ห้ามไว้ */
    body.page-admin .admin-sidebar-backdrop {
        position: fixed;
        inset: 0;
        z-index: 1051;
        background: rgb(15 23 42 / 45%);
        opacity: 0;
        pointer-events: none;
        transition: opacity .2s ease;
    }

    html.sidebar-open body.page-admin .admin-sidebar-backdrop {
        opacity: 1;
        pointer-events: auto;
    }
}

/* ‼️ ลิ้นชักมีเฉพาะจอเล็ก - จอกว้างเมนูอยู่ในสายเลย์เอาต์ตามปกติ พื้นทึบต้องไม่ค้างอยู่กินคลิกทั้งจอ */
@media (min-width: 768px) {
    body.page-admin .admin-sidebar-backdrop {
        display: none;
    }
}

/* ปุ่มลบ (icon-btn-danger) เป็นจุดเดียวที่ hardcode สี hover ไว้ตรง ๆ ไม่ได้ผูกกับ --nc-* เหมือนจุดอื่น */
[data-bs-theme="dark"] body.page-admin .icon-btn-danger:hover {
    background: #451a1a;
    color: var(--nc-danger);
    border-color: #5c2626;
}

body.page-admin .fw-semibold,
body.page-admin h1, body.page-admin h2, body.page-admin h3,
body.page-admin h4, body.page-admin h5, body.page-admin h6 {
    font-weight: 600;
}

body.page-admin .text-muted { color: var(--nc-text-muted) !important; }
body.page-admin .text-secondary { color: var(--nc-text-muted) !important; }
body.page-admin .text-success { color: var(--nc-success) !important; }
body.page-admin .text-warning { color: var(--nc-warning) !important; }
body.page-admin .bg-success { background-color: var(--nc-success) !important; }
body.page-admin .bg-warning { background-color: var(--nc-warning) !important; color: #fff !important; }

/* ---------- การ์ด/แผงพื้นฐาน ---------- */
body.page-admin .card {
    background: var(--nc-surface);
    border: 1px solid var(--nc-border);
    border-radius: 14px;
    box-shadow: 0 1px 3px rgba(20, 22, 40, .08);
    /* Bootstrap ปัดมุมให้อัตโนมัติเฉพาะ .card-header/.card-footer (first/last-child) เท่านั้น
       .card-body ธรรมดา (เช่น stat-panel-body ที่มีพื้นหลังสีเทาเต็มขอบ) ไม่ถูกปัดมุมตาม การ์ดเลยเหลี่ยม
       ที่มุมทั้งที่ตัวการ์ดโค้งอยู่แล้ว - ใส่ overflow hidden ให้คลิปเนื้อหาข้างในตามมุมการ์ดเสมอ */
    overflow: hidden;
}

body.page-admin .card-header {
    background: transparent;
    border-bottom: 1px solid var(--nc-border);
    font-weight: 600;
    color: var(--nc-text);
    padding: 0.9rem 1.25rem;
}

body.page-admin .btn {
    border-radius: 10px;
}

body.page-admin .modal-content {
    border-radius: 14px;
    border-color: var(--nc-border);
}

body.page-admin .alert-primary {
    background: var(--bs-primary-soft);
    border-color: var(--bs-primary-soft);
    color: var(--bs-primary-strong);
}

body.page-admin .table thead th {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--nc-text-faint);
    font-weight: 600;
    border-bottom-width: 1px;
}

body.page-admin .table > :not(caption) > * > * {
    border-bottom-color: var(--nc-border);
}

/* ---------- แถบบน (สีหัวข้อ - แยกอิสระจากสีปุ่มแล้ว ตั้งค่าได้จากหน้า Settings) ---------- */
body.page-admin .app-topbar {
    background: var(--header-bg);
    border-bottom: none;
}

/* สีตัวหนังสือ/ไอคอน/พื้นรองบน header ทั้งหมดอ้างอิงจาก --header-text/--header-overlay-rgb
   ที่คำนวณอัตโนมัติจากความสว่างของสีหัวข้อ (ดู render_theme_style ใน config.php) - เลือก header สีอ่อน/ขาว
   แล้วตัวหนังสือจะสลับเป็นสีเข้มให้เองอัตโนมัติ ไม่ต้องมองไม่เห็นตัวหนังสือแบบเดิม */
body.page-admin .app-topbar .app-brand,
body.page-admin .app-topbar .topbar-field,
body.page-admin .app-topbar .topbar-field label,
body.page-admin .app-topbar .topbar-user {
    color: var(--header-text);
}

body.page-admin .app-topbar .app-brand-sub,
body.page-admin .app-topbar .brand-kicker {
    color: rgba(var(--header-overlay-rgb), 0.75);
}

body.page-admin .app-topbar .app-brand-icon {
    color: var(--header-text);
}

body.page-admin .app-topbar .topbar-field select {
    background-color: rgba(var(--header-overlay-rgb), 0.14);
    border-color: rgba(var(--header-overlay-rgb), 0.3);
    color: var(--header-text);
}

body.page-admin .app-topbar .icon-btn,
body.page-admin .app-topbar .icon-btn-sm {
    background: rgba(var(--header-overlay-rgb), 0.14);
    border-color: rgba(var(--header-overlay-rgb), 0.28);
    color: var(--header-text);
}

body.page-admin .app-topbar .icon-btn:hover,
body.page-admin .app-topbar .icon-btn-sm:hover,
body.page-admin .app-topbar .icon-btn-danger:hover {
    background: rgba(var(--header-overlay-rgb), 0.26);
    border-color: rgba(var(--header-overlay-rgb), 0.4);
    color: var(--header-text);
}

/* grid 3 คอลัมน์ (ไม่ใช้ flex justify-content:space-between เดิม) เพื่อให้นาฬิกากลาง (.app-topbar-clock)
   อยู่กึ่งกลางแถบจริงๆ เสมอ ไม่ขึ้นกับความกว้างของ brand/actions สองฝั่ง (ถ้าใช้ flex 3 ชิ้น กับ space-between
   ตัวกลางจะอยู่กึ่งกลาง "พื้นที่ว่างที่เหลือ" เท่านั้น ไม่ใช่กึ่งกลางแถบทั้งแถบถ้า brand/actions กว้างไม่เท่ากัน) */
body.page-admin .app-topbar-inner {
    max-width: 1600px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 1rem;
    padding: 0.85rem 1.5rem;
}

body.page-admin .app-brand { grid-column: 1; justify-self: start; }
body.page-admin .app-topbar-clock { grid-column: 2; justify-self: center; }
body.page-admin .app-topbar-actions { grid-column: 3; justify-self: end; }

/* จอแคบ: กลับไปเรียงต่อกันแบบ flex-wrap เหมือนเดิมก่อนมีนาฬิกา (grid 3 คอลัมน์ ไม่มี wrap ให้ใช้
   พอพื้นที่ไม่พอจะดันล้นแทน) ให้นาฬิกาอยู่ลำดับกลางในการไหลแทนการบังคับกึ่งกลางแบบพอดี */
@media (max-width: 767.98px) {
    body.page-admin .app-topbar-inner {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-between;
    }

    body.page-admin .app-brand,
    body.page-admin .app-topbar-clock,
    body.page-admin .app-topbar-actions {
        justify-self: auto;
    }
}

body.page-admin .app-brand {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--nc-text);
}

body.page-admin .app-brand-icon {
    font-size: 1.3rem;
    color: var(--bs-primary);
    display: inline-flex;
    align-items: center;
}

body.page-admin .app-brand-icon img {
    width: 44px;
    height: 44px;
    object-fit: contain;
    border-radius: 12px;
}

body.page-admin .app-brand-sub {
    color: var(--nc-text-faint);
    font-weight: 400;
    margin-left: 0.15rem;
}

body.page-admin .app-topbar-clock {
    display: flex;
    flex-direction: column;
    align-items: center;
    line-height: 1.2;
    white-space: nowrap;
}

body.page-admin .app-topbar-clock-time {
    font-weight: 700;
    font-size: 1.1rem;
    font-variant-numeric: tabular-nums;
    /* ต้องใช้ --header-text (สลับเข้ม/อ่อนอัตโนมัติตามความสว่างของสี header ที่แอดมินเลือก - ดูคอมเมนต์
       ด้านบนหัวไฟล์นี้ที่ .app-brand ใช้ตัวเดียวกัน) ไม่ใช่ --nc-text (สีตัวหนังสือของพื้นหลังหน้าเว็บทั่วไป)
       เพราะนาฬิกาอยู่บนพื้น header ที่สีต่างไปเลย ไม่ใช่พื้นหน้าเว็บปกติ (บั๊กที่เจอจริง - อ่านยาก/กลืนกับพื้นบางสี) */
    color: var(--header-text);
}

body.page-admin .app-topbar-clock-date {
    font-size: 0.78rem;
    color: rgba(var(--header-overlay-rgb), 0.75);
}

body.page-admin .app-topbar-actions {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}

body.page-admin .topbar-field {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--nc-text-muted);
    font-size: 0.85rem;
}

/* ‼️ เดิมมี override ของชิปผู้ใช้บนแถบ Console ให้ใช้ rgba(--header-overlay-rgb) + --header-text
   ด้วยเหตุผลว่า "แถบบนของ Console เป็นพื้นสีเข้มตามธีมของสาขา" ซึ่ง**ไม่จริงแล้ว**:
   --header-bg เป็นค่าคงที่ (#ffffff โหมดสว่าง / #1e293b โหมดมืด) และ render_theme_style() ไม่ได้ตั้ง
   ค่านี้เลย - มันตั้งแค่ --bs-primary/--bs-primary-soft (ยืนยันที่คอมเมนต์ config.php บรรทัด ~628)
   ผลของ override คือชิปผู้ใช้หน้า Console กลายเป็น "สีเทา" อยู่หน้าเดียวในระบบ ต่างจากทุกโซน
   ‼️ เอา override ออกแล้ว ให้ตกไปใช้กฎกลาง .app-navbar-avatar ที่อิง --bs-primary-soft/--bs-primary
      = สีจากตั้งค่าสาขา เหมือนโซนอื่นทั้งหมด ห้ามเพิ่มกลับมาโดยไม่เช็คว่า --header-bg
      ผูกกับธีมสาขาจริงแล้วหรือยัง */

body.page-admin .topbar-user {
    color: var(--nc-text-muted);
    font-size: 1rem;
    font-weight: 700;
    white-space: nowrap;
}

/* ---------- ปุ่มไอคอน (แถบบน + การ์ดัคิว) ---------- */
/* ปุ่มไอคอนกลางของทั้งระบบ - รูปทรง (เหลี่ยมมน 10px) นิยามที่นี่ที่เดียว
   โซนไหนสีต่างก็ override เฉพาะสีทับ เช่น body.page-home ด้านล่างที่ใช้ token --omc-* ของหน้าแรก */
.icon-btn,
.icon-btn-sm {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    border: 1px solid var(--nc-border);
    background: var(--nc-surface);
    color: var(--nc-text-muted);
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}

.icon-btn {
    width: 36px;
    height: 36px;
    font-size: 1rem;
}

.icon-btn-sm {
    width: 30px;
    height: 30px;
    border-radius: 8px;
    font-size: 0.85rem;
}

.icon-btn:hover,
.icon-btn-sm:hover {
    background: var(--bs-primary-soft);
    color: var(--bs-primary);
    border-color: var(--bs-primary-soft);
}

body.page-admin .icon-btn-danger:hover {
    background: #faeceb;
    color: var(--nc-danger);
    border-color: #f3d9d7;
}

/* ปุ่ม action ของการ์ด "กำลังเรียก" - สี hover สื่อความหมายของแต่ละปุ่มแทนสี primary กลางๆ แบบปุ่มอื่น */
body.page-admin .btn-recall:hover {
    background: var(--nc-warning-soft);
    color: var(--nc-warning);
    border-color: var(--nc-warning-soft);
}

body.page-admin .btn-skip-row:hover {
    background: #faeceb;
    color: var(--nc-danger);
    border-color: #f3d9d7;
}

body.page-admin .btn-finish-row:hover {
    background: var(--nc-success-soft);
    color: var(--nc-success);
    border-color: var(--nc-success-soft);
}

/* ---------- เลย์เอาต์ 3 คอลัมน์ ---------- */
body.page-admin .dash-shell {
    max-width: 1600px;
    margin: 0 auto;
    padding: 24px;
}

body.page-admin .dash-grid {
    display: grid;
    grid-template-columns: 280px minmax(0, 1fr) 360px;
    align-items: stretch;
    gap: 20px;
}

@media (max-width: 1199.98px) {
    body.page-admin .dash-grid {
        grid-template-columns: 1fr;
    }

    body.page-admin .dash-col-left {
        align-self: auto;
    }

    body.page-admin .list-scroll {
        max-height: 60vh;
    }
}

body.page-admin .dash-col-left {
    /* คอลัมน์ซ้ายคือตัวกำหนดความสูงของแถว (เนื้อหาคงที่ ไม่มีลิสต์ที่ยาวขึ้นได้)
       align-self: start กันไม่ให้ตัวมันเองถูกยืดสูงเกินเนื้อหาจริงถ้าแถวสูงกว่าโดยบังเอิญ */
    align-self: start;
}

body.page-admin .dash-col-left,
body.page-admin .dash-col-right {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

/* คอลัมน์กลาง/ขวา: ความสูงจริงถูกล็อคด้วย JS (syncDashboardColumnHeights ใน app.js) ให้เท่าคอลัมน์ซ้ายเสมอ
   เพราะ pure CSS Grid ไม่มีทาง cap ความสูงคอลัมน์หนึ่งให้เท่าอีกคอลัมน์ที่เนื้อหายาวไม่เท่ากันได้
   min-height: 0 ที่นี่จำเป็นเพื่อให้คอลัมน์ "ยอมหด" ลงมาเท่าความสูงที่ JS กำหนด แทนที่จะขยายตามเนื้อหาลิสต์ข้างในเสมอ */
body.page-admin .dash-col-middle,
body.page-admin .dash-col-right {
    min-height: 0;
    overflow: hidden;
}

body.page-admin .min-h-0 {
    min-height: 0;
}

body.page-admin .list-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

/* ---------- รับคิวใหม่: แถวเลือกประเภทคิว ขึ้นบรรทัดใหม่เองเมื่อล้นแถว (ไม่ใช้เลื่อนซ้าย-ขวาแล้ว
   เพราะกดตอนเลื่อนยากบนจอทัชสกรีน) รองรับเพิ่มประเภทคิวในอนาคตได้เรื่อย ๆ โดยแค่ขึ้นบรรทัดเพิ่มเอง ---------- */
body.page-admin .type-select-scroll {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

/* บังคับ 2 ปุ่มต่อแถวเสมอ (เต็มความกว้างครึ่งหนึ่งลบครึ่งหนึ่งของ gap) กดง่าย นิ้วโป้งกดได้ถนัดบนมือถือ/แท็บเล็ต */
body.page-admin .type-select-scroll .type-select-btn {
    flex: 1 1 calc(50% - 0.3rem);
    white-space: normal;
}

/* ---------- แผงสถิติ (box ใน box: พื้นเทาอ่อนคั่น + กล่องขาวยกตัวแต่ละสถิติ เหมือนโทนการ์ด "รับคิวใหม่") ---------- */
body.page-admin .stat-panel-body {
    background: var(--nc-column-bg);
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
}

body.page-admin .stat-panel-label {
    font-size: 0.78rem;
    color: var(--nc-text-muted);
    line-height: 1.25;
}

body.page-admin .stat-row-lg,
body.page-admin .stat-grid-2x2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.65rem;
}

/* box ใน box แต่ละกล่อง: บรรทัดแรก icon + label ชิดซ้าย, บรรทัดสองตัวเลขชิดขวา */
body.page-admin .stat-box {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    background: var(--nc-surface);
    border: 1px solid var(--nc-border);
    border-radius: 14px;
    box-shadow: 0 1px 3px rgba(20, 22, 40, .06);
    padding: 0.75rem 1rem;
    min-width: 0;
    transition: box-shadow .15s ease, transform .15s ease;
}

body.page-admin .stat-box:hover {
    box-shadow: 0 4px 10px rgba(20, 22, 40, .08);
    transform: translateY(-1px);
}

body.page-admin .stat-box-left {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-width: 0;
}

body.page-admin .stat-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    font-size: 0.88rem;
    flex-shrink: 0;
    background: var(--stat-icon-bg, var(--nc-column-bg));
    color: var(--stat-icon-color, var(--nc-text-muted));
}

body.page-admin .stat-box-accent { --stat-icon-bg: var(--nc-danger-soft); --stat-icon-color: var(--nc-danger); }
body.page-admin .stat-box-info { --stat-icon-bg: var(--nc-info-soft); --stat-icon-color: var(--nc-info); }
body.page-admin .stat-box-success { --stat-icon-bg: var(--nc-success-soft); --stat-icon-color: var(--nc-success); }
body.page-admin .stat-box-warning { --stat-icon-bg: var(--nc-warning-soft); --stat-icon-color: var(--nc-warning); }
body.page-admin .stat-box-teal { --stat-icon-bg: var(--nc-teal-soft); --stat-icon-color: var(--nc-teal); }
body.page-admin .stat-box-muted { --stat-icon-bg: var(--nc-column-bg); --stat-icon-color: var(--nc-text-muted); }

/* ‼️ ห้ามแทรกกฎใหม่ "ระหว่าง" สองบรรทัดนี้ - มันเป็น selector list ก้อนเดียวที่คั่นด้วยคอมมา
   ถ้าแทรกเข้าไปกลาง .stat-lg-value จะไปเกาะกับ selector ของกฎที่แทรก แล้วเสียบล็อกของตัวเองไปทั้งก้อน
   (บั๊กที่เจอจริง: เลข "กำลังเรียก"/"รอเรียก" ที่หน้า /console กลายเป็นตัวจิ๋วชิดซ้าย เพราะไปกินสไตล์
   ของจุดสีประเภทคิวขนาด 12px แทน ส่วนอีกสี่กล่องที่เป็น .stat-sm-value ยังปกติ) */
body.page-admin .stat-lg-value,
body.page-admin .stat-sm-value {
    font-size: 1.75rem;
    font-weight: 600;
    line-height: 1.1;
    color: var(--nc-text);
    text-align: center;
}

/* จุดสีของประเภทคิว (แดชบอร์ดสาขา) - ‼️ "สี" มาจากค่าที่ผู้ใช้ตั้งไว้ต่อสาขาในหน้าประเภทคิว จึงต้องเป็น
   inline style ตามข้อยกเว้นเดิมของระบบ (CLAUDE.md ข้อ 0 - multi-tenant color) แต่ "ขนาด/รูปทรง" คุมที่นี่
   ไม่ปล่อยให้ค่าดิบกระจายอยู่ในมาร์กอัป */
body.page-admin .queue-type-dot {
    display: inline-block;
    width: 12px;
    height: 12px;
    margin-right: 0.35rem;
    border-radius: 50%;
    vertical-align: middle;
}

/* บรรทัดเสริมใต้ตัวเลขใน .stat-box สำหรับข้อมูลรอง เช่น เดือนบิล/จำนวน token
   (.stat-box เดิมไม่มี slot นี้ - Membership Dashboard, Phase 9 Task 2) */
body.page-admin .stat-box-subtitle {
    font-size: 0.72rem;
    color: var(--nc-text-muted);
    text-align: center;
}

/* ---------- คิวที่รออยู่ (ลิสต์การ์ด แทนตาราง) ---------- */
body.page-admin .waiting-list-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-bottom: 0.85rem;
    font-size: 0.85rem;
    color: var(--nc-text-muted);
}

/* เลือกทั้งหมด - ขยายกล่อง checkbox ให้ใหญ่ขึ้นสำหรับกดบนจอทัชสกรีน */
body.page-admin .waiting-check-all-wrap {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    cursor: pointer;
    padding: 0.3rem 0;
}

body.page-admin .waiting-check-all-wrap input[type="checkbox"] {
    width: 1.4rem;
    height: 1.4rem;
    cursor: pointer;
}

body.page-admin .queue-card-list,
body.page-admin .calling-card-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

body.page-admin .queue-row {
    display: grid;
    grid-template-columns: 28px auto 100px 72px 1fr 30px 30px;
    align-items: center;
    gap: 0.75rem;
    padding: 0.65rem 0.9rem;
    border: 1px solid var(--nc-border);
    border-radius: 10px;
    background: var(--nc-surface);
    transition: background .15s ease, border-color .15s ease;
}

body.page-admin .queue-row-selected {
    background: var(--bs-primary-soft);
    border-color: var(--bs-primary-soft);
}

/* Bootstrap ไม่ได้ตั้ง cursor: pointer ให้ checkbox โดย default ทำให้ดูเหมือนกดไม่ได้ทั้งที่กดได้จริง */
.waiting-checkbox {
    cursor: pointer;
}

body.page-admin .queue-row-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.3rem 0.85rem;
    border-radius: 999px;
    font-weight: 600;
    font-size: 0.92rem;
    cursor: pointer;
    white-space: nowrap;
}

body.page-admin .queue-row-type {
    color: var(--nc-text-muted);
    font-size: 0.85rem;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

body.page-admin .queue-row-people {
    color: var(--nc-text-muted);
    font-size: 0.85rem;
}

body.page-admin .queue-row-time {
    color: var(--nc-text-faint);
    font-size: 0.82rem;
    text-align: right;
}

/* ---------- กำลังเรียก (การ์ดหน้าตาเหมือนกันหมด) ---------- */
body.page-admin .calling-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--nc-border);
    border-radius: 10px;
    background: var(--nc-surface);
}

body.page-admin .calling-meta {
    display: flex;
    flex-direction: column;
    color: var(--nc-text-muted);
    font-size: 0.84rem;
    flex: 1;
    min-width: 0;
    margin-left: 0.6rem;
}

body.page-admin .calling-meta-line {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

body.page-admin .calling-meta-caller {
    font-size: 0.76rem;
    opacity: 0.85;
}

body.page-admin .calling-actions {
    display: flex;
    gap: 0.3rem;
    flex-shrink: 0;
}

/* ---------- empty state (แทน alert-secondary เดิม) ---------- */
body.page-admin .empty-state {
    background: var(--nc-column-bg);
    border: 1px dashed var(--nc-border);
    color: var(--nc-text-muted);
    border-radius: 10px;
    padding: 1.1rem;
    text-align: center;
    font-size: 0.88rem;
}

/* ---------- ประวัติวันนี้: ช่องค้นหา + ตารางแบบ scroll (โชว์ล่าสุด 20 แถวโดย default) ---------- */
body.page-admin .history-search-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr) auto;
    gap: 0.6rem;
    align-items: end;
}

@media (max-width: 991.98px) {
    body.page-admin .history-search-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

body.page-admin .history-search-actions {
    display: flex;
    gap: 0.4rem;
}

body.page-admin .history-scroll {
    max-height: 480px;
    overflow-y: auto;
}

/* ---------- Display Page (TV เรียกคิว) - จอเต็มหน้าจอ 2 โหมด: landscape (16:9, default) / portrait (9:16)
   โหมดถูก resolve ไว้ที่ <html data-mode="..."> ตั้งแต่ใน <head> ของ display.php ก่อน CSS วาดผลครั้งแรก (กัน FOUC)
   ทุกขนาดใช้หน่วย vh ให้ scale ตามความสูงจอ - ห้ามมี scrollbar เด็ดขาด (overflow: hidden ทุกชั้น)
   สีตามหมวดคิวยังคงดึงจาก typeColorMap (get_queue_types) เหมือนเดิมทุกจุด ให้ตรงกับสีในหน้า console เป๊ะ
   ไม่ใช้ primary_color/header_color ที่แอดมินตั้ง (render_theme_style) เพราะจอนี้ตั้งใจให้มีธีมเข้มของตัวเองแยกต่างหาก ---------- */
.page-display {
    /* ‼️ จุดไล่สีของพื้นจอต้องคำนวณจาก --disp-bg (สีที่เจ้าของร้านเลือก) ไม่ใช่ค่าตายตัว
       เดิม hardcode ไว้เป็นโทนน้ำเงิน ทำให้ช่อง "สีพื้นหลัง" กดแล้วจอไม่เปลี่ยนอะไรเลยบนธีมมืด
       bg-1 = จุดสว่างมุมบนซ้าย (ผสมขาวเล็กน้อย) / bg-2 = สีที่เลือกตรง ๆ / bg-3 = ขอบจอที่เข้มลง
       อัตราส่วนเลือกให้ค่าเริ่มต้น #0b1220 ออกมาใกล้เคียงโทนเดิมของระบบมากที่สุด */
    --disp-bg-1: color-mix(in srgb, var(--disp-bg) 92%, #ffffff);
    --disp-bg-2: var(--disp-bg);
    --disp-bg-3: color-mix(in srgb, var(--disp-bg) 72%, #000000);
    --disp-bg: #0b1220;
    --disp-text: #eaf0ff;
    /* พื้นการ์ด = สีพื้นจอที่สว่างขึ้นเล็กน้อยแล้วโปร่งแสง 75% (ค่าเดิม rgba(19,29,55,.75) ตรงกับ
       ผลลัพธ์ของสูตรนี้เมื่อ --disp-bg เป็นค่าเริ่มต้น #0b1220) */
    --disp-card-bg: color-mix(in srgb, color-mix(in srgb, var(--disp-bg) 94%, #ffffff) 75%, transparent);
    /* ‼️ เส้นขอบทุกเส้นบนจออ้างตัวนี้ตัวเดียว - ตั้งค่าได้จากหน้าตั้งค่า (--disp-border)
       ถ้าไม่มีค่ามา (เช่นหน้าที่ไม่ได้ผ่าน DisplayController) ตกกลับไปคำนวณจากสีตัวอักษรเหมือนเดิม */
    --disp-card-border: var(--disp-border, color-mix(in srgb, var(--disp-text) 16%, transparent));
    --disp-red: #e8394a;
    /* header เข้ม/ทึบกว่าการ์ดทั่วไปตั้งใจ (ต่างจาก --disp-card-bg ที่ใช้กับการ์ดอื่น) เพราะอยู่ตรงจุดที่ radial
       gradient ของพื้นหลังยังสว่างอยู่ (จุดศูนย์กลาง gradient อยู่ที่มุมบนซ้าย) ถ้าใช้สีการ์ดเดิมจะกลืนกับพื้นหลัง
       ไม่ตัดกันชัดแบบที่ต้องการ - ใช้โทนน้ำเงินเข้มเดียวกับ --disp-bg-2 (ไม่ใช้ดำ/เทาเข้มแบบไม่มีสี) แค่ทึบกว่าเดิม */
    --disp-header-bg: color-mix(in srgb, color-mix(in srgb, var(--disp-bg) 88%, #000000) 92%, transparent);
    /* ไม่มี IBM Plex Sans Thai/Archivo แบบ vendor local ในโปรเจกต์ (ทุก asset อื่น vendor ไว้ในเครื่องหมด ไม่พึ่ง CDN)
       จอ TV เปิดค้างทั้งวันอาจไม่มีเน็ตเสถียร จึงใช้ฟอนต์ระบบที่น้ำหนักใกล้เคียงแทนทั้งคู่ */
    --disp-font-th: "Segoe UI", "Leelawadee UI", "Noto Sans Thai", Tahoma, sans-serif;
    --disp-font-num: "Segoe UI", "Arial Black", Arial, sans-serif;

    background: radial-gradient(circle at 30% 20%, var(--disp-bg-1) 0%, var(--disp-bg-2) 55%, var(--disp-bg-3) 100%);
    color: var(--disp-text);
    font-family: var(--disp-font-th);
    margin: 0;
    height: 100vh;
    width: 100vw;
    overflow: hidden;
    /* header/footer เป็นแถบเต็มความกว้างแบบเว็บไซต์ (ดูคอมเมนต์ #display-header/#display-footer) เรียงกับ
       #display-shell แนวตั้งด้วย flex ธรรมดา - ไม่ใช้ grid ระดับ body เพราะมีแค่ 3 บล็อคเรียงกัน
       (header คงที่ + shell กินที่เหลือ + footer คงที่) */
    display: flex;
    flex-direction: column;
}

/* ---------- Header: แถบเต็มความกว้างจอแบบเว็บไซต์ทั่วไป (ตาม .app-topbar ของ console.php ที่ผู้ใช้อยากได้)
   อยู่นอก #display-shell โดยตั้งใจ - เต็มขอบซ้าย-ขวาจริง ไม่มีมุมโค้ง/ไม่ลอยเป็นการ์ด มีแค่พื้นหลังทึบกว่ารอบข้าง
   เล็กน้อย + เส้นคั่นด้านล่าง ให้ความรู้สึกเป็น section คนละก้อนกับเนื้อหาหลักด้านล่างชัดเจน ---------- */
#display-header {
    flex: 0 0 auto;
    height: 9vh;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-width: 0;
    padding: 0 1.6vw;
    background: var(--disp-header-bg);
    border-bottom: 1px solid var(--disp-card-border);
}

html[data-mode="portrait"] #display-header {
    /* เตี้ยกว่า landscape - จอแนวตั้งพื้นที่แคบกว่า ให้พื้นที่คืนไปกับการ์ดเรียกคิว/สื่อโปรโมชันแทน */
    height: 6.5vh;
}

/* Grid หลักของเนื้อหา (ใต้ header) - 2 ชุด grid-template-areas สลับกันตาม data-mode บน <html> (ดู display.php)
   #display-left/#display-right เป็น flex column ปกติใน landscape (คุม flex-ratio ภายในคอลัมน์ตัวเอง อิสระจากอีกฝั่ง)
   ส่วน portrait ใช้ display:contents ปลด #display-left/#display-right ออกจาก layout ให้ลูกๆ (calling-card,
   previous-queues, stats, slideshow-box) กลายเป็น grid item ตรงของ #display-shell แทน - ใช้ DOM ชุดเดียวกันทั้ง 2 โหมด
   ไม่ duplicate element ไหนเลย (สลับโหมดจึงไม่กระทบ interval/ข้อมูลที่ app.js ผูกไว้กับ id พวกนี้อยู่แล้ว) */
#display-shell {
    display: grid;
    flex: 1 1 auto;
    min-height: 0;
    width: 100vw;
    box-sizing: border-box;
    padding: 1.6vh 1.6vw;
    gap: 1.4vh 1.6vw;
    grid-template-columns: 1.62fr 1fr;
    grid-template-rows: 1fr;
    grid-template-areas: "left right";
}

/* ‼️ สลับช่องโฆษณาไปอยู่ "ซ้าย" ของจอแนวนอน (ตั้งค่าได้ต่อจอ - ดู media_side ใน DisplayConfigService)
   สลับด้วย grid-template-areas/columns เท่านั้น ไม่แตะลำดับใน HTML - ลำดับการอ่านของ screen reader
   และลำดับ tab จึงยังเป็น "คิวก่อน โฆษณาทีหลัง" เหมือนเดิม ซึ่งเป็นลำดับที่ถูกต้องเชิงความหมาย
   ‼️ สัดส่วนคอลัมน์ต้องสลับตามด้วย ไม่งั้นช่องโฆษณาจะได้พื้นที่ 1.62fr ของฝั่งคิวไปแทน */
html[data-mode="landscape"] body.display-media-left #display-shell {
    grid-template-columns: 1fr 1.62fr;
    grid-template-areas: "right left";
}

html[data-mode="portrait"] #display-shell {
    grid-template-columns: 1fr;
    /* media (พื้นที่โปรโมชัน รวมสถิติเข้าไปในการ์ดเดียวกันแล้ว - ดู #display-media-panel) ได้สัดส่วนมากกว่า
       การ์ดเรียกคิวเดิม (1fr -> 1.8fr) ให้กินพื้นที่ราวครึ่งล่างจอจริงตามที่ขอ - calling ลดจาก 2fr เหลือ 1fr
       เพราะเลขคิวใหญ่แค่ไหนก็ยังใหญ่เท่าเดิม (กำหนดด้วย vh ตายตัว ไม่ใช่สัดส่วนของกล่อง) ไม่จำเป็นต้องมีกล่องสูงมาก
       ไม่มีแถว "stats" แยกอีกต่อไป เพราะ #display-stats ย้ายเข้าไปอยู่ใน #display-media-panel แล้ว
    ‼️ media เหลือ auto (ไม่ใช่ 1.8fr) เพราะภาพโฆษณาของจอแนวตั้งย้ายไปอยู่แถบแบนเนอร์ด้านล่างแล้ว
       แถวนี้จึงเหลือแค่แถบสถิติ - ถ้ายังกันไว้ 1.8fr จะได้ที่ว่างเปล่าเกือบครึ่งจอแล้วเบียดแถวคิวจนล้น
    ‼️ previous เป็น minmax(0, 1fr) เพื่อให้ "ย่อได้จริง" - auto ย่อไม่ได้ ช่องคิวเลยล้นไปทับแบนเนอร์ */
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas:
        "calling"
        "previous"
        "media";
}

/* footer เต็มความกว้างจอแบบเดียวกับ #display-header (ผู้ใช้ขอให้แยกเป็น section ชัดเจนแบบ header) - อยู่นอก
   #display-shell เช่นกัน ไม่ใช่ grid item อีกต่อไป ใช้สี/เส้นคั่นชุดเดียวกับ header แค่คั่นด้านบนแทนด้านล่าง */
#display-footer {
    flex: 0 0 auto;
    height: 5.6vh;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    gap: 1.2vw;
    min-width: 0;
    padding: 0 1.6vw;
    background: var(--disp-header-bg);
    border-top: 1px solid var(--disp-card-border);
}

#display-left { grid-area: left; display: flex; flex-direction: column; gap: 1.4vh; min-height: 0; min-width: 0; }
#display-right { grid-area: right; display: flex; flex-direction: column; gap: 1.4vh; min-height: 0; min-width: 0; }

html[data-mode="portrait"] #display-left,
html[data-mode="portrait"] #display-right {
    display: contents;
}

/* ‼️ ลดจาก 34vh เหลือ 26vh พร้อมกับลดเลขคิวลงเป็น 11vh - ต้องลดคู่กันเสมอ ลดแต่กรอบอย่างเดียวเนื้อหา
   จะตกออกนอก overflow: hidden แล้วโดนตัดหายไปเงียบ ๆ (บั๊กเดิมที่เคยเจอตอนตั้ง 26vh คู่กับเลข 13.5vh)
   เหตุผลที่ต้องลด: แถบแบนเนอร์ 16:9 เต็มความกว้างจอกินไปแล้วราว 32vh ถ้าการ์ดเรียกคิวยังกัน 34vh
   แถวช่อง "เรียกล่าสุด" จะเหลือไม่ถึง 6vh จนอ่านไม่ออก (วัดจริงได้ 71px บนจอ 1280) */
html[data-mode="portrait"] #calling-card { grid-area: calling; min-height: 26vh; }

/* ‼️ min-height: 0 + overflow: hidden จำเป็นทั้งคู่ - grid item ที่ไม่ได้ตั้ง min-height: 0 จะไม่ยอมย่อ
   ต่ำกว่าขนาดเนื้อหา แล้วดันตัวเองล้นออกไปทับแถบแบนเนอร์ด้านล่าง (บั๊กที่ผู้ใช้เจอ)
   grid-auto-rows แบ่งความสูงที่มีให้ทุกแถวเท่า ๆ กัน แทนที่จะให้แถวแรกกินตามเนื้อหาแล้วแถวหลังตกขอบ */
html[data-mode="portrait"] #previous-queues {
    grid-area: previous;
    min-height: 0;
    overflow: hidden;
    grid-auto-rows: minmax(0, 1fr);
    align-content: stretch;
}

html[data-mode="portrait"] .display-previous-slot {
    min-height: 0;
    overflow: hidden;
}
/* #display-media-panel (ไม่ใช่ #display-slideshow-box/#display-stats แยกกันอีกต่อไป) เป็น grid item ตรง
   ของ #display-shell ในโหมดนี้ (รวมภาพสไลด์ + สถิติเป็นการ์ดเดียวกันแล้ว - ดูคอมเมนต์ .display-media-panel) */
html[data-mode="portrait"] #display-media-panel {
    grid-area: media;
    min-height: 0;
    background: transparent;
}

/* ‼️ จอแนวตั้งมีพื้นที่โฆษณาที่เดียวคือแถบแบนเนอร์ด้านล่าง (คนละชุดภาพกับจอแนวนอน)
   กล่องสไลด์เดิมจึงว่างเปล่าเสมอในโหมดนี้ - ต้องซ่อนทิ้ง ไม่ใช่ปล่อยให้กินที่/ดันแถบสถิติออกนอกกรอบ */
html[data-mode="portrait"] #display-slideshow-box { display: none; }

#calling-card { flex: 1.5; }
#previous-queues { flex: 1; }
#display-media-panel { flex: 1; }

/* ---------- Header: จุดแดง + ชื่อร้าน (shop_name) | ปุ่มเปิดเสียง (icon, หายไปเองหลังแตะ) + ปุ่มสลับโหมด (icon) | นาฬิกา ---------- */
.display-header-brand { display: flex; align-items: center; gap: .7vw; min-width: 0; }

/* กล่องโลโก้แบบเดียวกับ .app-brand-icon ของ console.php (สี่เหลี่ยมมุมโค้ง) - โชว์รูป shop_logo จริงถ้ามี
   ไม่งั้น fallback เป็นไอคอน bi-ticket-perforated-fill (สลับเนื้อหาโดย loadDisplaySettings ใน app.js) */
.display-brand-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    color: var(--disp-red);
    font-size: 1.3rem;
}

.display-brand-icon img {
    width: 44px;
    height: 44px;
    object-fit: contain;
    border-radius: 12px;
}

.display-shop-name {
    font-family: var(--disp-font-num);
    font-weight: 800;
    font-size: 2.6vh;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.display-header-right { display: flex; align-items: center; gap: .9vw; flex: 0 0 auto; }

/* ปุ่ม icon กลมใช้ร่วมกันทั้งปุ่มเปิดเสียงและปุ่มสลับโหมด - ขนาดคงที่เป็นวงกลมสัดส่วน 1:1 ตามความสูง header เสมอ */
.display-icon-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 4.4vh;
    height: 4.4vh;
    background: var(--disp-card-bg);
    border: 1px solid var(--disp-card-border);
    color: var(--disp-text);
    border-radius: 50%;
    font-size: 2vh;
    cursor: pointer;
    padding: 0;
}

.display-icon-btn:hover { border-color: rgba(126, 158, 255, .4); }

/* ปุ่มเปิดเสียง (ยังไม่ถูกแตะ) เน้นสีเตือนให้สังเกตง่าย - หายไปเองหลังแตะครั้งเดียว (ดู initSoundUnlockButton ใน app.js) */
.display-icon-btn-warn {
    background: #ffc107;
    border-color: #ffc107;
    color: #1a1200;
}

/* ‼️ ปุ่มลำโพงข้างนาฬิกา - "จุดกดที่ชัวร์" สำหรับปลดล็อกเสียงของเบราว์เซอร์ (ผู้ใช้กำหนด 2026-09-09)
   สีจางโดยเจตนา ไม่แย่งสายตาจากเลขคิวซึ่งเป็นพระเอกของจอ (DESIGN_GUIDELINE ข้อ 2 - CTA เด่นได้จุดเดียว)
   ใช้ token ของจอล้วน ไม่ hardcode สี เพื่อให้กลืนกับทุกชุดสีที่เจ้าของร้านเลือก */
.display-sound-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 4.4vh;
    height: 4.4vh;
    flex: 0 0 auto;
    padding: 0;
    border: 1px solid color-mix(in srgb, var(--disp-text) 18%, transparent);
    border-radius: 50%;
    background: transparent;
    /* จางกว่าตัวอักษรอื่นในหัวจออย่างชัดเจน - เห็นว่ากดได้ แต่ไม่สะดุดตาลูกค้า */
    color: color-mix(in srgb, var(--disp-text) 45%, transparent);
    cursor: pointer;
    transition: color .2s ease, border-color .2s ease;
}

.display-sound-toggle i { font-size: 2.4vh; line-height: 1; }

.display-sound-toggle:hover,
.display-sound-toggle:focus-visible {
    color: var(--disp-text);
    border-color: color-mix(in srgb, var(--disp-text) 45%, transparent);
    outline: none;
}

/* ‼️ ยังปลดล็อกไม่สำเร็จ = จอนี้ยังเงียบอยู่ ต้องเห็นชัดกว่าปกติเล็กน้อยเพื่อให้พนักงานสังเกตได้
   แต่ยังไม่ใช้สีเตือน (แดง/เหลือง) เพราะไม่ใช่ error ของระบบ เป็นแค่สถานะที่รอคนกด */
.display-sound-toggle.is-locked {
    color: color-mix(in srgb, var(--disp-text) 78%, transparent);
    border-color: color-mix(in srgb, var(--disp-text) 38%, transparent);
}

.display-clock { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.3; }

.display-clock-time {
    font-family: var(--disp-font-num);
    font-weight: 900;
    font-size: 2.6vh;
    font-variant-numeric: tabular-nums;
}

.display-clock-date { font-size: 1.4vh; color: color-mix(in srgb, var(--disp-text) 70%, transparent); white-space: nowrap; }

/* ---------- การ์ด "กำลังเรียกคิว" - แถบสีหมวดคิวคุมด้วย --cat-color (ตั้งจาก JS ตาม typeColorMap ของคิวนั้น) ---------- */
#calling-card {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-height: 0;
    /* ‼️ ผสมสีประเภทคิวจาง ๆ ลงบนพื้นการ์ด ไม่ใช่ปล่อยเป็นพื้นการ์ดเปล่าที่มีแค่แถบสีซ้าย
       10% เป็นค่าที่จางกว่าจอแนวตั้ง (14%) ตามที่ผู้ใช้กำหนดว่าแนวนอนควรอ่อนกว่า */
    background: color-mix(in srgb, var(--cat-color, var(--disp-primary)) 10%, var(--disp-card-bg));
    border: 1px solid var(--disp-card-border);
    border-left: 10px solid var(--cat-color, var(--disp-primary, #5b74ff));
    border-radius: 20px;
    padding: 2vh 2.4vw;
    overflow: hidden;
}

html[data-mode="portrait"] #calling-card {
    border-left: 0;
    border-top: 8px solid var(--cat-color, var(--disp-primary, #5b74ff));
    flex-direction: column;
    justify-content: center;
    text-align: center;
    /* padding/gap ลดลงเล็กน้อยจาก base (2vh/2.4vw, ไม่มี gap ระบุ) คืนพื้นที่ให้ media บ้างหลังต้องเพิ่ม
       min-height ของการ์ดนี้เป็น 34vh (ดูคอมเมนต์ที่ grid-area: calling) กันเนื้อหาล้นกรอบจริง */
    padding: 1.6vh 2.4vw;
    gap: 1vh;
}

/* z-index: -1 สำคัญมาก - #calling-card มี position:relative (สร้าง stacking context) แต่ .display-calling-main/
   .display-calling-counter เป็น element ธรรมดา (position: static) ตามกฎ CSS stacking แล้ว absolute element ที่
   z-index:auto (ค่า default) จะ "ทาสีทับ" static element พี่น้องเสมอไม่ว่าจะเรียงอยู่ก่อนใน DOM หรือไม่ก็ตาม
   ถ้าไม่ตั้ง z-index ติดลบไว้ ก้อนแสงนี้จะไปทับกล่อง "เชิญที่ช่อง" จนมองข้อความในกล่องไม่ชัด (บั๊กที่เจอจริง) */
.display-calling-glow {
    position: absolute;
    z-index: -1;
    top: 50%;
    right: -10%;
    transform: translateY(-50%);
    width: 38vh;
    height: 38vh;
    border-radius: 50%;
    background: var(--cat-color, var(--disp-primary, #5b74ff));
    filter: blur(6vh);
    opacity: .5;
    animation: displayGlow 4s ease-in-out infinite;
    pointer-events: none;
}

html[data-mode="portrait"] .display-calling-glow { display: none; }

.display-calling-main { position: relative; display: flex; flex-direction: column; gap: .6vh; min-width: 0; }
html[data-mode="portrait"] .display-calling-main { align-items: center; }

.display-calling-label {
    display: flex;
    align-items: center;
    gap: .8vw;
    font-size: 1.8vh;
    letter-spacing: 3px;
    text-transform: uppercase;
    color: color-mix(in srgb, var(--disp-text) 75%, transparent);
}

.display-pulse-dot {
    position: relative;
    width: 1.2vh;
    height: 1.2vh;
    border-radius: 50%;
    background: var(--cat-color, var(--disp-primary, #5b74ff));
}

.display-pulse-dot::after {
    content: '';
    position: absolute;
    inset: -.3vh;
    border-radius: 50%;
    border: 2px solid var(--cat-color, var(--disp-primary, #5b74ff));
    animation: displayPulse 1.8s ease-out infinite;
}

.display-calling-queueno {
    font-family: var(--disp-font-num);
    font-weight: 900;
    font-size: 17vh;
    /* line-height 1.3 แทนค่าตาม spec เป๊ะ ๆ (.9/.86) - ทดสอบจริงแล้วฟอนต์ fallback ที่ใช้ (ไม่มี Archivo ใน
       เครื่อง) มี glyph metrics ตามธรรมชาติสูงกว่า line-height ที่ตั้งไว้ (scrollHeight > clientHeight ของ span
       นี้เอง จนกว่าจะขึ้นไปถึงราว 1.3) ทำให้ตัวเลขเสี่ยงเบียด/ทับข้อความ "ประเภทบริการ" ด้านล่าง จึงเผื่อ buffer
       แทนการรัดจนพอดีเป๊ะ - ยังดูใหญ่เด่นเหมือนเดิม เพราะ font-size ไม่ได้เปลี่ยน */
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
}

html[data-mode="portrait"] .display-calling-queueno { font-size: 11vh; line-height: 1.2; }

.display-calling-counter {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .4vh;
    flex: 0 0 auto;
    /* พื้นหลังทึบพอให้กล่องนี้อ่านชัดเสมอ ไม่ว่าแสง glow ด้านหลังจะสว่างแค่ไหน (ดู .display-calling-glow)
       ‼️ ต้องคำนวณจาก --disp-bg ไม่ใช่ค่าตายตัว - เดิม hardcode เป็น rgba(5,9,18,.55) (เกือบดำ)
       พอเจ้าของร้านเลือกพื้นจอโทนสว่าง ตัวอักษรจะกลายเป็นสีเข้มตามธีม แต่กล่องยังดำอยู่
       = ตัวเข้มบนพื้นเข้ม มองไม่เห็นเลย (บั๊กที่ผู้ใช้เจอจริงตอนลองพื้นสีขาว) */
    background: color-mix(in srgb, color-mix(in srgb, var(--disp-bg) 80%, #000000) 62%, transparent);
    border: 1px solid var(--disp-card-border);
    border-radius: 16px;
    padding: 1.4vh 2vw;
    opacity: 1;
    transition: opacity .25s ease;
}

html[data-mode="portrait"] .display-calling-counter { flex-direction: row; gap: 1vw; padding: .7vh 1.6vw; }

/* setting-show-counter ปิด: ซ่อนด้วย opacity แทน display:none/d-none ตั้งใจ (ใช้ร่วมกันทั้งกล่องหลักและ
   "ช่อง N" ใต้คิวก่อนหน้า 4 ช่อง - ดู renderPreviousQueues ใน app.js) - กันที่ว่างของ element นี้ไว้เสมอ ไม่ให้
   เนื้อหาข้างๆ/กล่องทั้งใบขยับ-หุบตอนสลับเปิด/ปิด setting (บั๊กที่เจอจริงตอนใช้ d-none เดิม - element หายไปจาก
   flow ทำให้ parent จัดเลย์เอาต์ใหม่ทั้งก้อน)
   ต้องระบุคู่กับ class เดิมเสมอ (.display-calling-counter.display-counter-hidden ไม่ใช่แค่ .display-counter-hidden
   เฉยๆ) เพื่อให้ specificity สูงกว่า .display-previous-counter{opacity:1}/.display-calling-counter{opacity:1}
   เสมอไม่ว่าใครจะประกาศทีหลังในไฟล์นี้ - ถ้าใช้แค่ class เดียว (specificity เท่ากัน) กฎที่มาทีหลังใน source จะชนะ
   แทน ทำให้ opacity:0 ไม่มีผลจริง (บั๊กที่เจอจริง - "ช่อง N" ไม่หายตอนปิด setting) */
.display-calling-counter.display-counter-hidden,
.display-previous-counter.display-counter-hidden {
    opacity: 0;
    pointer-events: none;
}

.display-counter-label { font-size: 1.5vh; letter-spacing: 1px; color: color-mix(in srgb, var(--disp-text) 70%, transparent); }

.display-counter-no {
    font-family: var(--disp-font-num);
    font-weight: 900;
    font-size: 9vh;
    line-height: 1.3;
    color: var(--cat-color, var(--disp-primary, #5b74ff));
}

html[data-mode="portrait"] .display-counter-no { font-size: 3vh; }

/* ตอน counter <= 0 ("ไม่ระบุเคาน์เตอร์") ลดขนาด "-" ลงมาก จาก 9vh เท่าตัวเลขปกติ - ที่ font-size เท่าตัวเลข
   "-" ตัวเดียวจะเห็นเป็นแค่แท่งสี่เหลี่ยมเล็กๆ ลอยๆ ดูเหมือน render พัง ไม่เหมือนสัญลักษณ์ที่ตั้งใจ (ดู renderCallingCard ใน app.js) */
.display-counter-no.display-counter-no-empty { font-size: 2.4vh; font-weight: 700; }
html[data-mode="portrait"] .display-counter-no.display-counter-no-empty { font-size: 2vh; }

/* ---------- สถานะ "ยังไม่มีคิวที่กำลังเรียก" ----------
   ‼️ #calling-board-empty เป็น "ชั้นทับ" อยู่ข้างใน #calling-card ใบเดิม ไม่ใช่การ์ดคนละใบที่สลับด้วย d-none
   อีกต่อไป - การ์ดจึงอยู่ในเลย์เอาต์ตลอดเวลาและกินพื้นที่เท่าเดิมเป๊ะไม่ว่าจะมีคิวหรือไม่ (ดูคอมเมนต์ใน display.php)
   เดิมทั้งสองก้อนตั้ง min-height: 26vh เท่ากันก็จริง แต่ min-height เป็นแค่ "พื้น" ไม่ใช่ความสูงจริง - เนื้อหาการ์ด
   (เลขคิว 11vh + ป้าย + กล่องเคาน์เตอร์) ดันเกิน 26vh ส่วนข้อความบรรทัดเดียวไม่เกิน ความสูงจึงไม่เท่ากันอยู่ดี */
.display-board-empty {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 0 2vw;
    text-align: center;
    font-size: 2.4vh;
    color: color-mix(in srgb, var(--disp-text) 50%, transparent);
    margin: 0;
}

/* ตอนว่าง: ลบสีประเภทคิวออกจากแถบขอบ (ไม่มีคิวก็ไม่มีประเภท) และซ่อนเนื้อหาด้วย visibility ไม่ใช่ display
   เพื่อให้กล่องยังคำนวณความสูงจากเนื้อหาชุดเดิมอยู่ - ถ้าใช้ display:none ความสูงจะยุบกลับไปเป็นบั๊กเดิมทันที */
#calling-card.display-calling-idle {
    --cat-color: var(--disp-card-border);
    background: var(--disp-card-bg);
}

.display-calling-idle .display-calling-glow,
.display-calling-idle .display-calling-main,
.display-calling-idle .display-calling-counter { visibility: hidden; }

.display-calling-idle .display-board-empty { display: flex; }

/* ---------- แถวคิวก่อนหน้า 4 ช่อง (calls[1..4] จาก get_recent_calls limit 5) ---------- */
/* ‼️ จำนวนคอลัมน์มาจากค่าตั้งค่าของจอนั้น ๆ - ค่าเริ่มต้น 4 = เท่ากับ repeat(4, 1fr) เดิมทุกประการ
   (เดิมเขียนตายตัวไว้ 4 ทำให้ปรับคอลัมน์ในหน้าตั้งค่าแล้วจอแนวนอนไม่เปลี่ยนตาม - บั๊กที่ผู้ใช้แจ้ง) */
#previous-queues {
    display: grid;
    grid-template-columns: repeat(var(--disp-recent-columns, 4), minmax(0, 1fr));
    gap: 1vh 1vw;
    min-height: 0;
}

.display-previous-slot {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .4vh;
    background: var(--disp-card-bg);
    border: 1px solid var(--disp-card-border);
    border-left: 6px solid var(--cat-color, #475569);
    border-radius: 14px;
    padding: 1vh;
    min-width: 0;
}

html[data-mode="portrait"] .display-previous-slot {
    border-left: 0;
    border-top: 5px solid var(--cat-color, #475569);
}

.display-previous-slot.is-empty { opacity: .4; }

.display-previous-queueno {
    font-family: var(--disp-font-num);
    font-weight: 800;
    font-size: 5.6vh;
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
}

/* ‼️ ขนาดเลขคิวต้องอิง "ความสูงของช่อง" ด้วย ไม่ใช่ความสูงจอเพียงอย่างเดียว
   จำนวนคอลัมน์ที่ตั้งได้ (1-4) เปลี่ยนจำนวนแถวตามไปด้วย ช่องจึงเตี้ย/สูงไม่เท่ากันในแต่ละค่า
   ถ้าล็อกเป็น vh อย่างเดียว พอปรับเป็น 2 คอลัมน์ กล่องตัวเลขจะถูก flex บีบจนเตี้ยกว่า line-height
   (วัดจริง: กล่องสูง 31px แต่ line-height 40px) แล้วโดน overflow:hidden ของช่องตัดหัว-ท้ายตัวอักษร
   หายไป - ผู้ใช้แจ้ง 2026-09-09 ว่า "ปรับเป็น 2 คอลัมน์แล้วอักษรโดนบีบหาย"
   cqh = เปอร์เซ็นต์ของความสูงช่อง จึงย่อตามช่องเองทุกค่าคอลัมน์โดยไม่ต้องเขียนกฎแยกทีละค่า */
html[data-mode="portrait"] .display-previous-slot {
    container-type: size;
}

html[data-mode="portrait"] .display-previous-queueno {
    font-size: min(4.4vh, 40cqh);
    line-height: 1.15;
    /* ‼️ ห้ามถูกบีบ - ถ้าที่ไม่พอให้บรรทัด "ช่อง" ด้านล่างย่อแทน ตัวเลขคิวคือสิ่งที่ต้องอ่านออกเสมอ */
    flex: 0 0 auto;
}

/* บรรทัด "ช่อง -" ยอมให้ย่อ/หายก่อนตัวเลขเมื่อช่องเตี้ยมาก (เช่น 1 คอลัมน์ + จำนวนช่องเยอะ) */
html[data-mode="portrait"] .display-previous-counter {
    min-height: 0;
    flex: 0 1 auto;
    overflow: hidden;
}

.display-previous-counter {
    font-size: 1.4vh;
    color: color-mix(in srgb, var(--disp-text) 65%, transparent);
    white-space: nowrap;
    opacity: 1;
    transition: opacity .25s ease;
}

/* ---------- การ์ดสื่อโปรโมชันรวมกับสถิติเป็นก้อนเดียว (ผู้ใช้ขอ) - #display-media-panel คุมกรอบ/พื้นหลัง/
   มุมโค้งก้อนเดียว #display-slideshow-box (รูป) กับ #display-stats (ตัวเลข) ข้างในแค่เป็นส่วนบน/ล่างของการ์ด
   เดียวกัน ไม่มีกรอบของตัวเองแล้ว คั่นด้วยเส้นบางๆ แทน ---------- */
/* ‼️ ไม่มีเส้นขอบแล้ว - ผู้ใช้ต้องการให้ภาพโฆษณาชิดขอบการ์ดไปเลย ไม่มีเส้นมาคั่นให้ดูเหมือนภาพถูกย่อไว้ในกรอบ
   (พื้นหลังยังอยู่ เพื่อให้แถบสถิติด้านล่างในการ์ดเดียวกันยังอ่านออกตอนยังไม่มีภาพ) */
.display-media-panel {
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--disp-card-bg);
    border: 0;
    border-radius: 18px;
    overflow: hidden;
}

/* ---------- แถวสถิติ 2 ช่อง (รอเรียก จาก get_list.waiting_count / รอเฉลี่ย จาก get_list.avg_wait_minutes -
   เวลารอเฉลี่ยจริงของวันนี้ คำนวณเหมือนหน้า Dashboard/Report ทุกประการ)
   ตัดช่อง "ให้บริการแล้ว" ออก เพราะไม่มี endpoint public ใดส่งค่านี้ (get_history ต้อง login เท่านั้น)
   อยู่ในการ์ดเดียวกับรูปสไลด์แล้ว (ดู .display-media-panel) - เส้นคั่นบนสุด/ระหว่างช่องแทนกรอบของตัวเอง ---------- */
#display-stats {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    flex: 0 0 auto;
    border-top: 1px solid var(--disp-card-border);
}

.display-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .3vh;
    padding: 1.2vh 1vw;
}

.display-stat:first-child { border-right: 1px solid var(--disp-card-border); }

/* ‼️ จอแนวตั้งให้แถบสถิติบางที่สุดเท่าที่ยังอ่านออก (ผู้ใช้ขอ 2026-09-09) - เป็นข้อมูลรอง
   ความสูงที่ประหยัดได้ตกไปเป็นของช่อง "เรียกล่าสุด" ซึ่งเป็นสิ่งที่ลูกค้าต้องอ่านจริง
   เรียงเป็นแถวนอน (ตัวเลขติดกับป้าย) จึงไม่ต้องใช้ความสูงสองบรรทัดเหมือนจอแนวนอน */
html[data-mode="portrait"] .display-stat { flex-direction: row; gap: .7vw; padding: .4vh 1vw; }

.display-stat-value {
    font-family: var(--disp-font-num);
    font-weight: 800;
    font-size: 3.2vh;
    font-variant-numeric: tabular-nums;
}

html[data-mode="portrait"] .display-stat-value { font-size: 2.1vh; }
html[data-mode="portrait"] .display-stat-label { font-size: 1.2vh; }

.display-stat-value small { font-size: 1.6vh; font-weight: 600; }
.display-stat-label { font-size: 1.4vh; letter-spacing: 1px; color: color-mix(in srgb, var(--disp-text) 65%, transparent); }

/* ---------- พื้นที่สื่อโปรโมชัน/สไลด์โฆษณา - ตั้งใจไม่ใช้คำว่า "ad"/"banner" ใน id/class ใด ๆ เพราะ Ad Blocker
   (AdGuard/uBlock ฯลฯ) จะซ่อน element ที่ id/class ขึ้นต้นด้วย "ad-" ทิ้งอัตโนมัติผ่าน cosmetic filter แม้เนื้อหา
   จะไม่ใช่โฆษณาจริงก็ตาม - ‼️ ใช้ object-fit: cover ตามที่ผู้ใช้กำหนดว่าภาพต้องเต็มพื้นที่ชิดขอบ ไม่ใช่ย่อลอย
   อยู่กลางกรอบ (แลกกับการที่ขอบภาพอาจถูกตัดถ้าสัดส่วนไม่ตรงช่อง - หน้าตั้งค่าจึงบอกสัดส่วนที่แนะนำและเตือนตอนอัปโหลด) ---------- */
.display-slideshow-box {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: color-mix(in srgb, var(--disp-text) 30%, transparent);
    font-size: 2vh;
    letter-spacing: 2px;
    overflow: hidden;
}

/* บันทึกการใช้งาน: ค่าก่อน/หลังที่ยาว - กดดูค่าเต็มได้ ไม่ตัดทิ้ง */
.audit-json summary {
    cursor: pointer;
    word-break: break-all;
}

.audit-json-full {
    margin: var(--space-1) 0 0;
    white-space: pre-wrap;
    word-break: break-all;
    font-size: inherit;
    max-width: 32rem;
}

/* คอนโซล: ยังไม่มีประเภทคิวที่เปิดใช้ - ข้อความเต็มแถวแทนปุ่มประเภทคิว */
.console-empty-state {
    grid-column: 1 / -1;
    padding: var(--space-3);
    border: 1px dashed var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    text-align: center;
}

/* ยังไม่มีภาพสไลด์ - แบรนด์ของร้านแบบจาง (แทนข้อความ placeholder เดิม) สีสืบจาก .display-slideshow-box */
.display-slideshow-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    text-align: center;
    letter-spacing: 0;
}

.display-slideshow-empty .bi {
    font-size: 8vh;
    line-height: 1;
}

.display-slideshow-empty-name {
    font-size: 2.4vh;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.display-slideshow {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}

/* ‼️ cover ไม่ใช่ contain - ผู้ใช้ต้องการให้ภาพเต็มพื้นที่จากขอบซ้ายถึงขอบขวา ไม่ใช่ย่อลอยอยู่กลางกรอบ
   จนเหลือแถบว่างรอบ ๆ (แลกกับการที่ขอบภาพอาจถูกตัดบ้างถ้าสัดส่วนไม่ตรงกับช่อง จึงมีคำแนะนำสัดส่วน
   และคำเตือนตอนอัปโหลดไว้ให้แล้วในหน้าตั้งค่า) */
.display-slideshow img {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity 1s ease;
}

.display-slideshow img.active {
    opacity: 1;
}

/* ---------- Footer: badge "ประกาศ" (แดงแบรนด์) + ข้อความวิ่ง (marquee ด้วย CSS keyframes ล้วน ไม่ใช้ <marquee>)
   .display-ticker-track มีข้อความซ้ำ 2 ชุดวางเรียงกันแนวนอน (width: max-content) แล้วเลื่อน translateX(-50%)
   พอถึงครึ่งทาง (=จบชุดแรกพอดี) วนกลับมาเริ่ม 0% เป๊ะ ทำให้ดูต่อเนื่องไม่มีรอยสะดุด ---------- */
.display-footer-badge {
    flex: 0 0 auto;
    background: var(--disp-red);
    color: #fff;
    font-weight: 800;
    font-size: 1.6vh;
    padding: .5vh 1.2vw;
    border-radius: 999px;
    letter-spacing: 1px;
}

.display-ticker { flex: 1 1 auto; min-width: 0; overflow: hidden; position: relative; }

.display-ticker-track {
    display: flex;
    width: max-content;
    animation: displayTicker 32s linear infinite;
}

.display-ticker-item {
    white-space: nowrap;
    padding-right: 6vw;
    font-size: 1.8vh;
    color: color-mix(in srgb, var(--disp-text) 90%, transparent);
}

@keyframes displayPulse {
    0% { transform: scale(1); opacity: .9; }
    100% { transform: scale(2.6); opacity: 0; }
}

@keyframes displayGlow {
    0%, 100% { opacity: .35; }
    50% { opacity: .85; }
}

@keyframes displayTicker {
    0% { transform: translateX(0); }
    100% { transform: translateX(-50%); }
}

/* เรียก flashCallingCard() ใน app.js ตอนมีคิวใหม่/เรียกซ้ำ (เพิ่ม/ลบ class นี้เอง ไม่ต้องผูก animationend) */
.display-flash { animation: displayFlash .6s ease; }

@keyframes displayFlash {
    0% { filter: brightness(1); }
    30% { filter: brightness(1.7); }
    100% { filter: brightness(1); }
}

/* แกลเลอรีภาพสไลด์ในหน้า Settings (แท็บ "การตั้งค่าจอแสดงผล") */
.slideshow-media-gallery {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}

.slideshow-media-item {
    position: relative;
    width: 140px;
    height: 90px;
    border-radius: 0.5rem;
    overflow: hidden;
    border: 1px solid var(--nc-border, #e2e5ee);
}

.slideshow-media-item img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.slideshow-media-item .btn-delete-slideshow-media {
    position: absolute;
    top: 0.25rem;
    right: 0.25rem;
    width: 24px;
    height: 24px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    font-size: 0.7rem;
    line-height: 1;
    opacity: 0.85;
}

@keyframes callingRowIn {
    from {
        opacity: 0;
        transform: translateY(12px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* ---------- ใบคิว 80mm (แสดงเฉพาะตอนสั่งพิมพ์) ----------
   ทุก selector ในบล็อกนี้ตั้งใจไม่พึ่ง class ของ Bootstrap เลย (มันมี margin utility แบบ !important
   ที่จะ override ระยะห่างที่เรากำหนดเองไม่ได้) ควบคุมด้วย .ticket-* ทั้งหมดเพื่อความแน่นอนตอนพิมพ์จริง */
#print-ticket {
    display: none;
}

@media print {
    @page {
        margin: 0;
        size: 80mm auto;
    }

    /* ใช้ display:none (ไม่ใช่ visibility:hidden) เพื่อเอาเนื้อหาอื่นออกจาก layout จริง ๆ
       ถ้าใช้ visibility:hidden เนื้อหาที่ซ่อนไว้ยังกินพื้นที่แนวตั้งอยู่ ทำให้เอกสารล้นไปหน้า 2
       เมื่อกระดาษปลายทางถูกบังคับให้สูงจำกัด (เช่นตอนทดสอบด้วย custom paper size ใน print preview) */
    body > *:not(#print-ticket) {
        display: none !important;
    }

    #print-ticket {
        display: block !important;
    }

    .ticket-80mm {
        width: 80mm;
        box-sizing: border-box;
        margin: 0;
        padding: 2mm 3mm;
        color: #000;
        font-family: Arial, Tahoma, sans-serif;
        text-align: center;
    }

    .ticket-shop-name {
        margin: 0 0 1mm;
        font-size: 14px;
        font-weight: 700;
    }

    .ticket-line {
        margin: 0 0 1.5mm;
        font-size: 12px;
    }

    .ticket-queue-no {
        margin: 0 0 1.5mm;
        font-size: 56px;
        font-weight: 800;
        line-height: 1.1;
        letter-spacing: 1px;
    }

    .ticket-datetime {
        margin: 0 0 1.5mm;
        font-size: 12px;
    }

    .ticket-qrcode-box {
        display: flex;
        justify-content: center;
        margin: 0 0 1.5mm;
    }

    .ticket-qrcode-box img,
    .ticket-qrcode-box canvas {
        width: 100px !important;
        height: 100px !important;
    }

    .ticket-note {
        margin: 0;
        font-size: 14px;
    }
}

/* Login layout - ภาพแบนเนอร์เต็มจอเป็นฉากหลัง + กล่องฟอร์มลอยค่อนไปทางขวา (ไม่ชิดขอบ/มุม)
   ใช้ร่วมกันทุกโซน (สาขา/console/องค์กร/OMC)
   ‼️ ชื่อคลาส .login-split-* คงไว้ตามเดิม - public.js อ่าน .login-split-banner-photo เพื่อโหลดรูปแบบ fade-in */
.login-split {
    --login-card-width: 400px;
    --login-card-inset: clamp(var(--space-3), 9vw, 160px);
    position: relative;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    min-height: 100vh;
    padding: var(--space-5) var(--login-card-inset);
}

.login-split-banner {
    position: absolute;
    inset: 0;
    background-color: var(--bs-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    /* ‼️ โลโก้/ไอคอนตอนไม่มีรูปแบนเนอร์ ต้องอยู่กลาง "พื้นที่ว่างฝั่งซ้าย" ไม่ใช่กลางจอ - ไม่งั้นโดนกล่องฟอร์มบังครึ่งหนึ่ง */
    padding-right: calc(var(--login-card-width) + var(--login-card-inset));
    overflow: hidden;
}

.login-split-banner.has-photo {
    background-color: var(--bs-white);
}

.login-split-banner-photo {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    opacity: 0;
    transition: opacity 0.6s ease;
}

.login-split-banner-photo.is-loaded {
    opacity: 1;
}

.login-split-banner i {
    font-size: 5rem;
    color: rgba(255, 255, 255, 0.85);
}

.login-split-banner img {
    max-width: 55%;
    max-height: 45%;
    object-fit: contain;
}

.login-split-logo {
    max-width: 160px;
    max-height: 160px;
    object-fit: contain;
    margin-bottom: 1rem;
}

/* โซนที่ไม่มีโลโก้ของตัวเอง (OMC/องค์กร) ใช้ไอคอนประจำโซนแทนรูป - ขนาดพอ ๆ กับโลโก้ที่ย่อแล้ว */
.login-split-logo-fallback {
    display: inline-block;
    margin-bottom: 1rem;
    font-size: 2.5rem;
    line-height: 1;
}

.login-success-content {
    border-radius: 16px;
    border: none;
}

.login-success-icon {
    width: 72px;
    height: 72px;
    border-radius: 50%;
    background: var(--bs-success-bg-subtle, rgba(25, 135, 84, 0.12));
    display: flex;
    align-items: center;
    justify-content: center;
}

.login-success-icon i {
    font-size: 2.25rem;
    color: var(--bs-success);
}

.login-split-form {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: var(--login-card-width);
    background-color: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color-translucent);
    border-radius: 20px;
    /* เงาเดียวแบบกว้าง-จาง ให้กล่องลอยพ้นรูปพื้นหลัง โดยไม่เป็นเงาหนาตาม DESIGN_GUIDELINE (corporate clean) */
    box-shadow: 0 24px 64px -24px rgba(var(--header-overlay-rgb), 0.45);
}

.login-split-form-inner {
    padding: var(--space-5);
}

/* ปุ่มสลับภาษามุมขวาบนของกล่องฟอร์ม - ‼️ หน้า login ไม่มีโทเค็น --nc-* ของโซนหลังบ้าน
   จึงผูกสีกับตัวแปรของ Bootstrap แทน (ตามโหมดมืดให้เอง) */
.login-split-lang {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    z-index: 2;
}

.login-split-lang .app-lang-menu .app-lang-toggle {
    color: var(--bs-secondary-color);
}
.login-split-lang .app-lang-menu .app-lang-toggle:hover,
.login-split-lang .app-lang-menu .app-lang-toggle[aria-expanded="true"] {
    background: var(--bs-tertiary-bg);
    color: var(--bs-body-color);
}

@media (max-width: 767.98px) {
    .login-split {
        justify-content: center;
        padding: var(--space-4) var(--space-3);
    }

    .login-split-banner {
        padding-right: 0;
    }

    .login-split-banner img,
    .login-split-banner i {
        display: none;
    }

    .login-split-form-inner {
        padding: var(--space-4);
    }
}

/* ======================================================
   Organization Admin layer (/organization/*) — Phase 17
   ใช้ shell/sidebar/การ์ด/stat-box ของ body.page-admin ซ้ำทั้งชุด (body_class มี page-admin ติดมาด้วย)
   ที่เพิ่มที่นี่คือส่วนที่ไม่มีของเดิมให้ใช้จริงเท่านั้น
   ====================================================== */
/* หัวแบรนด์แบบ "kicker + ชื่อ" ใช้ร่วมกันทุกโซน: หัว sidebar ของ OMC/Organization/Branch
   และแถบบนของหน้า Console จึงไม่ผูกกับ body class หรือกับ sidebar อีกต่อไป */
.brand-stack {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
    min-width: 0;
}

.brand-kicker {
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-weight: 600;
    color: var(--nc-text-muted);
}

/* ไอคอนใหญ่กลางกล่อง "ยังไม่เปิดใช้งาน" ของหน้า Subscription/Wallet/Billing (Phase 20-22) */
body.page-organization .org-placeholder-icon {
    display: block;
    margin-bottom: 0.5rem;
    font-size: 2.5rem;
    color: var(--nc-text-muted);
}

/* ======================================================
   แถบบนแบบกะทัดรัด (app navbar) — Phase 19.2
   อยู่เหนือ "พื้นที่เนื้อหา" เท่านั้น ไม่คร่อม sidebar (sidebar ยังเต็มความสูงเหมือนเดิม)
   ใช้ sticky + margin ติดลบ เพื่อให้แนบขอบบน/ซ้าย/ขวาของ .admin-content ที่มี padding 1.5rem อยู่แล้ว
   โดยไม่ต้องแก้โครง HTML ของทุกหน้า (แทรก partial เป็นลูกตัวแรกของ <main> ก็พอ)
   ====================================================== */
/* ‼️ ความสูง navbar ประกาศเป็นตัวแปรเพราะมีของอื่นต้องเกาะใต้มันพอดี (แผงตัวอย่างในหน้าตั้งค่าจอ)
   ถ้า hardcode เลขซ้ำสองที่ พอแก้ความสูง navbar ทีหลังแผงนั้นจะมุดหลัง navbar โดยไม่มีใครรู้ */
body.page-admin {
    --nc-navbar-h: 51px;
    --nc-navbar-gap: 1.25rem;
}

body.page-admin .app-navbar {
    position: sticky;
    top: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    height: var(--nc-navbar-h);
    /* ซ้าย/ขวาติดลบเพื่อกินเต็มความกว้างของพื้นที่เนื้อหา ส่วนบน = 0 เพราะ .admin-content ไม่มี padding บนแล้ว */
    margin: 0 -1.5rem var(--nc-navbar-gap);
    padding: 0 0.75rem;
    background: var(--nc-surface);
    border-bottom: 1px solid var(--nc-border);
}

body.page-admin .app-navbar-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

@media (max-width: 767.98px) {
    /* จอเล็ก .admin-shell เป็น column และ sidebar ไปอยู่ด้านบน - .admin-content ยัง padding 1.5rem เท่าเดิม
       margin ติดลบจึงต้องเท่ากับ 1.5rem เหมือน desktop ไม่งั้นจะเหลือช่องว่างข้างละ 0.5rem */
    body.page-admin .app-navbar {
        --nc-navbar-h: 49px;
        --nc-navbar-gap: 1rem;
        height: var(--nc-navbar-h);
        margin: 0 -1.5rem var(--nc-navbar-gap);
    }
}

/* ======================================================
   เว็บสาธารณะ OMC Queue (/ , /pricing , /contact)

   ทิศทาง (DESIGN_GUIDELINE ข้อ 0): corporate clean ของแอปเดิม + signature = "จอเรียกคิวจริง"
   รอบนี้จัดใหม่ตามสกิล landing-page-design ทั้งชุด ค่าที่ต้องอ้างได้ทุกตัวประกาศเป็น token ด้านล่าง:
     - ขนาดตัวอักษร  ลงบนขั้นของ Tailwind type scale เท่านั้น (--omc-fs-*) ห้ามมีค่าดิบนอกขั้น
     - ระยะห่าง      ใช้ได้เฉพาะ 13 ค่าใน --omc-sp-* เท่านั้น
     - ความมน        ใช้ค่าของ Tailwind (--omc-r-*) การ์ดซ้อนใช้สูตร inner = outer − gap
     - เส้นโค้งจังหวะ  --omc-ease ตัวเดียวทั้งโซน ห้ามใช้ ease/linear ของเบราว์เซอร์

   ‼️ ห้าม gradient บนพื้นหลังทุกกรณี - gradient ใช้ได้จุดเดียวคือ "ตัวอักษร" ของหัวเรื่อง hero
   ‼️ ห้ามใส่เส้นขอบด้านเดียวให้การ์ด (มีครบสี่ด้านหรือไม่มีเลย)
   ‼️ โซนนี้ใช้ Phosphor SVG (partials/public-icon.php) ไม่ใช่ bi-* เหมือนโซนหลังบ้าน
   ====================================================== */
body.page-home {
    /* ‼️ สีแบรนด์ของหน้าสาธารณะเป็นส้ม #f7a332 ตามที่ผู้ใช้กำหนด (2026-09-02)
       ‼️ #f7a332 คอนทราสต์กับ "ขาว" แค่ 2.05 ใช้เป็นสีตัวอักษรบนพื้นสว่างหรือใส่ตัวอักษรขาวทับไม่ได้
          จึงต้องแยกหน้าที่กันสองตัว ห้ามยุบเป็นตัวเดียว (วัดจริงทุกคู่แล้ว ตัวเลขในวงเล็บ):
            --omc-brand       = พื้น (ปุ่มหลัก/ป้าย/แถบ) คู่กับตัวอักษร --omc-ink เท่านั้น  (8.32)
            --omc-brand-deep  = ตัวอักษร/ลิงก์/ไอคอน บนพื้นสว่าง และพื้นปุ่มที่คู่กับตัวอักษรขาว (5.56)
       ‼️ สีกลางเป็นโทนอุ่นทั้งชุด ถ้าคงโทนเทาอมเขียวเดิมไว้จะตีกับส้มเป็นเขียว-ส้ม */
    /* ‼️ ลิงก์/ปุ่มลิงก์ของหน้าสาธารณะใช้สีแบรนด์ของหน้านี้ ไม่ใช่ --bs-primary ของโซนหลังบ้าน
       ใช้ตัวที่ผ่านคอนทราสต์บนพื้นสว่าง (--omc-brand-deep) ไม่ใช่ --omc-brand ที่เป็นสีพื้น */
    --bs-link-color: var(--omc-brand-deep);
    --bs-link-color-rgb: 169, 78, 8;
    --bs-link-hover-color: var(--omc-brand-deep-hover);
    --bs-link-hover-color-rgb: 138, 74, 8;

    /* ‼️ 2026-09-24 ผู้ใช้สั่งแก้สีทั้งหน้า (น้ำตาลเยอะเกินไม่สวย) - สีเข้มหลักเปลี่ยนจากน้ำตาลเป็นกรมท่า
       ตามโลโก้ OMC (วงกลมกรมท่า + ตัวอักษรส้ม) ได้คู่สีกรมท่า-ส้มชุดเดียวทั้งหน้า
       วัดคอนทราสต์แล้ว: ขาวบน ink 15.67 / --omc-brand บน ink 7.64 / --omc-on-ink-muted บน ink 8.82 */
    --omc-ink: #13233f;
    --omc-ink-soft: #1d3157;
    --omc-brand-deep: #a94e08;
    --omc-brand: #f7a332;
    /* สีที่ใช้ตอน hover ของสองตัวข้างบน - เข้มขึ้น/สว่างขึ้นหนึ่งขั้นแต่ยังผ่านคอนทราสต์ */
    --omc-brand-deep-hover: #8a4a08;
    --omc-brand-hover: #ffb955;

    /* ‼️ สีตัวอักษร/ลิงก์แบรนด์ที่ "สลับตามโหมด" - ต้องแยกออกมาจาก --omc-brand-deep เพราะ token นั้นทำสองหน้าที่
       (สีตัวอักษรบนพื้นสว่าง + สีพื้นปุ่มที่คู่กับตัวอักษรขาว) พอเข้าโหมดมืด สองหน้าที่นี้ต้องไปคนละทาง:
       ตัวอักษรต้อง "สว่างขึ้น" ส่วนพื้นปุ่มต้อง "เข้มไว้" ไม่งั้นตัวอักษรขาวบนปุ่มจะจม
       วัดจริงบนพื้นโหมดมืดทั้งสามระดับ: #a94e08 ได้แค่ 3.18 (ตก AA) ส่วน #f7a332 ได้ 7.78-9.20 (ผ่าน)
       ‼️ ใช้เฉพาะบทบาท "ตัวอักษร/ไอคอน/ลิงก์" ห้ามเอาไปใช้เป็นสีพื้น */
    --omc-brand-text: #a94e08;
    /* 2026-09-24 ผู้ใช้สั่ง: พื้นหลังขาวล้วน ไม่เอาขาวอมส้ม - พื้น/กรอบ/ตัวอักษรรองเป็นโทนกลางแล้ว สีส้มเหลือเฉพาะจุดเน้น */
    --omc-page: #ffffff;
    --omc-surface: #ffffff;
    --omc-surface-2: #f3f5f9;
    --omc-border: #e2e6ee;
    --omc-text: #16202e;
    --omc-text-muted: #5a6472;
    --omc-on-ink: #ffffff;
    --omc-on-ink-muted: #b8c3d6;

    /* ขั้นตัวอักษร = Tailwind type scale ตรงตัว (ขนาด/ความสูงบรรทัดมาคู่กันเสมอ) */
    --omc-fs-xs: 12px;
    --omc-lh-xs: 16px;
    --omc-fs-sm: 14px;
    --omc-lh-sm: 20px;
    --omc-fs-base: 16px;
    --omc-lh-base: 24px;
    --omc-fs-lg: 18px;
    --omc-lh-lg: 28px;
    --omc-fs-xl: 20px;
    --omc-lh-xl: 28px;
    --omc-fs-2xl: 24px;
    --omc-lh-2xl: 32px;
    --omc-fs-3xl: 30px;
    --omc-lh-3xl: 36px;
    --omc-fs-4xl: 36px;
    --omc-lh-4xl: 40px;
    --omc-fs-5xl: 48px;
    --omc-fs-6xl: 60px;

    /* ระยะห่าง 13 ค่า - ไม่มีค่าระหว่างขั้นและไม่มีค่านอกตาราง */
    --omc-sp-25: 2px;
    --omc-sp-50: 4px;
    --omc-sp-75: 8px;
    --omc-sp-100: 12px;
    --omc-sp-200: 16px;
    --omc-sp-300: 24px;
    --omc-sp-400: 32px;
    --omc-sp-500: 40px;
    --omc-sp-600: 48px;
    --omc-sp-700: 64px;
    --omc-sp-800: 80px;
    --omc-sp-900: 96px;

    --omc-r-lg: 8px;
    --omc-r-xl: 12px;
    --omc-r-2xl: 16px;
    --omc-r-3xl: 24px;

    --omc-ease: cubic-bezier(0.32, 0.72, 0, 1);
    --omc-container: 1290px;
    --omc-gutter: max(var(--omc-sp-300), calc((100% - var(--omc-container)) / 2));
    --omc-measure: 680px;

    --omc-shadow: 0 1px 2px rgba(11, 31, 29, 0.04), 0 8px 20px rgba(11, 31, 29, 0.06);
    --omc-shadow-lift: 0 4px 8px rgba(11, 31, 29, 0.06), 0 16px 32px rgba(11, 31, 29, 0.10);

    /* ‼️ ยังใช้ system stack เหมือนทั้งระบบ ห้ามโหลด webfont ภายนอก (CLAUDE.md ข้อ 0)
       สกิล landing-page-design กำหนด Geist/Manrope/Poppins ไว้ แต่ทั้งสามตัวไม่มี glyph ไทย
       และกฎของโปรเจคห้ามพึ่งฟอนต์จากเน็ตอยู่แล้ว - กฎโปรเจคชนะ */
    font-family: "Segoe UI", "Leelawadee UI", "Noto Sans Thai", Tahoma, sans-serif;
    background: var(--omc-page);
    color: var(--omc-text);
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
}


html[data-bs-theme="dark"] body.page-home {
    /* ตัวอักษรแบรนด์สลับไปใช้สีสว่าง (ดูคอมเมนต์ที่ --omc-brand-text) - พื้นปุ่มยังใช้ --omc-brand-deep เดิม */
    --omc-brand-text: #f7a332;
    --omc-page: #16100a;
    --omc-surface: #1f1710;
    --omc-surface-2: #2a2016;
    --omc-border: #3a2c1e;
    --omc-text: #f3ece2;
    --omc-text-muted: #b0a091;
    /* ‼️ โหมดมืดคงโทนน้ำตาลอุ่นเดิม (ผู้ใช้สั่ง 2026-09-28) - กรมท่าของ 74b2662 ใช้เฉพาะโหมดสว่าง */
    --omc-ink: #241a10;
    --omc-ink-soft: #33261a;
    --omc-on-ink-muted: #c4b39d;
    --omc-shadow: 0 1px 2px rgba(0, 0, 0, 0.30);
    --omc-shadow-lift: 0 6px 22px rgba(0, 0, 0, 0.42);
}

/* ‼️ ภาษาไทยไม่มีช่องว่างระหว่างคำ วลียาว ๆ จึงกลายเป็นก้อนเดียวที่เบราว์เซอร์ตัดบรรทัดไม่ได้
   และดันความกว้างของหน้าจนล้นออกนอกจอมือถือ - ต้องอนุญาตให้ตัดได้ทุกจุดเป็นทางออกสุดท้าย */
body.page-home,
body.page-home h1,
body.page-home h2,
body.page-home h3,
body.page-home p,
body.page-home li,
body.page-home dt,
body.page-home dd,
body.page-home span,
body.page-home summary {
    /* break-word ไม่ใช่ anywhere - anywhere จะไปเปลี่ยนความกว้าง min-content ของ grid/flex ด้วย */
    overflow-wrap: break-word;
}

body.page-home h1,
body.page-home h2,
body.page-home h3 {
    text-wrap: balance;
}

body.page-home p {
    text-wrap: pretty;
}

/* ---------- แถบบน (คนละตัวกับ .app-navbar ของโซนหลังบ้าน) ----------
   ‼️ ผู้ใช้เลือกให้คงเป็น nav bar เต็มความกว้างแบบเดิม ไม่เอาแบบแถบเกาะลอย
   พื้นโปร่ง + เบลอ ให้เนื้อหาที่เลื่อนผ่านใต้แถบยังรู้สึกต่อเนื่อง */
body.page-home .home-topbar {
    position: sticky;
    top: 0;
    z-index: 40;
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--omc-sp-200);
    min-height: 80px;
    padding: var(--omc-sp-200) var(--omc-gutter);
    background: color-mix(in srgb, var(--omc-page) 88%, transparent);
    backdrop-filter: blur(16px);
    border-bottom: 1px solid var(--omc-border);
}

body.page-home .home-mark {
    justify-self: start;
    display: inline-flex;
    align-items: center;
    gap: var(--omc-sp-100);
    color: var(--omc-text);
    font-size: var(--omc-fs-lg);
    line-height: var(--omc-lh-lg);
    font-weight: 700;
    letter-spacing: -0.01em;
    text-decoration: none;
    white-space: nowrap;
}

body.page-home .home-mark-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--omc-r-xl);
    background: var(--omc-brand-deep);
    color: #ffffff;
    overflow: hidden;
}

body.page-home .home-mark-badge svg {
    width: 20px;
    height: 20px;
}

body.page-home .home-mark-badge-image {
    background: transparent;
}

body.page-home .home-mark-badge img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/* เมนูกลางแถบบน - ลิงก์ไปหน้าอื่นในโซนสาธารณะเท่านั้น (ไม่มีทางเข้าระบบบนหน้าสาธารณะ) */
body.page-home .home-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--omc-sp-400);
}

body.page-home .home-nav-link {
    position: relative;
    display: block;
    padding: var(--omc-sp-50) 0;
    color: var(--omc-text-muted);
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    transition: color 300ms var(--omc-ease);
}

/* ขีดใต้วิ่งออกจากกลาง ใช้ scaleX เพื่อไม่ให้ layout ขยับตอน hover (ต่างจากการเปลี่ยนความหนาเส้นขอบ) */
body.page-home .home-nav-link::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 2px;
    border-radius: 999px;
    background: var(--omc-brand-deep);
    transform: scaleX(0);
    transition: transform 300ms var(--omc-ease);
}

body.page-home .home-nav-link:hover,
body.page-home .home-nav-link:focus-visible,
body.page-home .home-nav-link.active {
    color: var(--omc-text);
}

body.page-home .home-nav-link:hover::after,
body.page-home .home-nav-link:focus-visible::after,
body.page-home .home-nav-link.active::after {
    transform: scaleX(1);
}

body.page-home .home-topbar-actions {
    justify-self: end;
    display: flex;
    align-items: center;
    gap: var(--omc-sp-75);
}

/* ---------- ปุ่มติดต่อเราบนแถบบน ----------
   จุดที่ "สีเข้ม" ที่สุดของแถบบน = ตัวเดียวในแถบที่เป็นพื้นทึบ จึงเด่นโดยไม่ต้องเพิ่มสีใหม่เข้าระบบ
   ‼️ ใช้ teal ของแบรนด์ตามเดิม ไม่เอาสีส้มของเว็บองค์กร (ผู้ใช้เลือกคงเทียลไว้)
   เงาใช้สีเดียวกับปุ่มแบบจาง ๆ ให้รู้สึกลอยขึ้นมาจากแถบ ไม่ใช่เงาเทากลาง ๆ ที่ดูสกปรกบนพื้นสี */
body.page-home .home-topbar-cta {
    display: inline-flex;
    align-items: center;
    gap: var(--omc-sp-75);
    padding: var(--omc-sp-75) var(--omc-sp-200);
    border: 0;
    border-radius: 999px;
    background: var(--omc-brand-deep);
    color: #ffffff;
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    white-space: nowrap;
    text-decoration: none;
    box-shadow: 0 1px 2px rgba(169, 78, 8, 0.24), 0 6px 16px rgba(169, 78, 8, 0.24);
    transition:
        background-color 300ms var(--omc-ease),
        border-color 300ms var(--omc-ease),
        box-shadow 300ms var(--omc-ease),
        transform 300ms var(--omc-ease);
}

body.page-home .home-topbar-cta svg {
    width: 16px;
    height: 16px;
    transition: transform 300ms var(--omc-ease);
}

body.page-home .home-topbar-cta:hover,
body.page-home .home-topbar-cta:focus-visible {
    background: var(--omc-brand-deep-hover);
    color: #ffffff;
    box-shadow: 0 2px 4px rgba(169, 78, 8, 0.28), 0 10px 24px rgba(169, 78, 8, 0.32);
}

/* ลูกศรขยับออกนิดเดียวตอน hover - บอกทิศว่ากดแล้วไปต่อ ไม่ใช่เอฟเฟกต์ประดับ */
body.page-home .home-topbar-cta:hover svg {
    transform: translateX(2px);
}

body.page-home .home-topbar-cta:active {
    transform: scale(0.98);
}

/* ปุ่มเดียวกันในเมนูเต็มจอของจอแคบ - ตัวใหญ่ขึ้นให้เท่ากับลิงก์เมนูรอบข้าง */
body.page-home .home-menu-cta {
    display: inline-flex;
    align-items: center;
    gap: var(--omc-sp-100);
    margin-top: var(--omc-sp-200);
    padding: var(--omc-sp-100) var(--omc-sp-300);
    border-radius: 999px;
    background: var(--omc-brand-deep);
    color: #ffffff;
    font-size: var(--omc-fs-xl);
    line-height: var(--omc-lh-xl);
    font-weight: 600;
    text-decoration: none;
    box-shadow: 0 6px 16px rgba(169, 78, 8, 0.24);
}

body.page-home .home-menu-cta svg {
    width: 20px;
    height: 20px;
}

body.page-home .home-menu-cta:active {
    transform: scale(0.98);
}

/* ‼️ ไม่มีเส้นขอบตามที่ผู้ใช้กำหนด - เหลือแค่ไอคอนลอยบนแถบ ให้ปุ่มติดต่อเราเป็นวัตถุเดียวที่มีรูปทรง
   พื้นหลังโผล่เฉพาะตอน hover/focus (ยังต้องรู้ว่ากดได้ และคีย์บอร์ดต้องเห็นโฟกัสชัด) */
body.page-home .icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--omc-text);
    transition: background-color 300ms var(--omc-ease), transform 300ms var(--omc-ease);
}

body.page-home .icon-btn:hover,
body.page-home .icon-btn:focus-visible {
    background: var(--omc-surface-2);
    color: var(--omc-text);
}

body.page-home .icon-btn:active {
    transform: scale(0.98);
}

/* ปุ่มแฮมเบอร์เกอร์: สองขีดหมุนมาไขว้เป็นกากบาท ไม่ใช่หายไปเฉย ๆ */
body.page-home .home-burger {
    display: none;
    position: relative;
    width: 40px;
    height: 40px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--omc-text);
    cursor: pointer;
    transition: background-color 300ms var(--omc-ease), transform 300ms var(--omc-ease);
}

body.page-home .home-burger:hover {
    background: var(--omc-surface-2);
}

body.page-home .home-burger:active {
    transform: scale(0.98);
}

body.page-home .home-burger-lines {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 20px;
    height: 16px;
    transform: translate(-50%, -50%);
}

body.page-home .home-burger-line {
    position: absolute;
    left: 0;
    width: 20px;
    height: 2px;
    border-radius: 999px;
    background: currentColor;
    transition: transform 500ms var(--omc-ease);
}

body.page-home .home-burger-line-top {
    top: var(--omc-sp-50);
}

body.page-home .home-burger-line-bottom {
    bottom: var(--omc-sp-50);
}

body.page-home .home-burger[aria-expanded="true"] .home-burger-line-top {
    transform: translateY(5px) rotate(45deg);
}

body.page-home .home-burger[aria-expanded="true"] .home-burger-line-bottom {
    transform: translateY(-5px) rotate(-45deg);
}

/* เมนูจอแคบ: เปิดเต็มจอพื้นเบลอหนา ลิงก์ไล่ขึ้นมาทีละอัน */
body.page-home .home-menu {
    position: fixed;
    inset: 0;
    z-index: 30;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: var(--omc-sp-300);
    background: color-mix(in srgb, var(--omc-page) 80%, transparent);
    backdrop-filter: blur(40px);
    opacity: 0;
    visibility: hidden;
    transition: opacity 500ms var(--omc-ease), visibility 500ms var(--omc-ease);
}

body.page-home .home-menu.is-open {
    opacity: 1;
    visibility: visible;
}

body.page-home .home-menu-list {
    display: flex;
    flex-direction: column;
    gap: var(--omc-sp-75);
    margin: 0;
    padding: 0;
    list-style: none;
}

body.page-home .home-menu-item {
    opacity: 0;
    transform: translateY(48px);
    transition: opacity 700ms var(--omc-ease), transform 700ms var(--omc-ease);
}

body.page-home .home-menu.is-open .home-menu-item {
    opacity: 1;
    transform: translateY(0);
}

body.page-home .home-menu-link {
    display: block;
    padding: var(--omc-sp-100) var(--omc-sp-200);
    border-radius: var(--omc-r-2xl);
    color: var(--omc-text);
    font-size: var(--omc-fs-3xl);
    line-height: var(--omc-lh-3xl);
    font-weight: 600;
    letter-spacing: -0.02em;
    text-decoration: none;
}

body.page-home .home-menu-link.active {
    color: var(--omc-brand-deep);
}

/* จอแคบ: เมนูกลางย้ายไปอยู่ในเมนูเต็มจอแทนที่จะซ่อนหายไปเฉย ๆ เพราะตอนนี้หน้ามีหลายหัวข้อกว่าเดิม */
@media (max-width: 768px) {
    body.page-home .home-topbar {
        grid-template-columns: 1fr auto;
        min-height: 64px;
    }

    body.page-home .home-nav {
        display: none;
    }

    /* จอแคบไม่มีที่พอสำหรับปุ่มติดต่อบนแถบ - ย้ายไปอยู่ท้ายเมนูเต็มจอแทน (ดู .home-menu-cta) */
    body.page-home .home-topbar-cta {
        display: none;
    }

    body.page-home .home-burger {
        display: block;
    }
}

/* ---------- โครงหน้า ---------- */
body.page-home .home-page {
    padding: var(--omc-sp-600) var(--omc-gutter) var(--omc-sp-700);
}

body.page-home .home-section {
    margin-top: var(--omc-sp-900);
}

body.page-home .home-section-head {
    max-width: var(--omc-measure);
    margin-bottom: var(--omc-sp-600);
}

body.page-home .home-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--omc-sp-75);
    margin-bottom: var(--omc-sp-200);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    /* ‼️ ห้ามยืดระยะตัวอักษร/ตัวพิมพ์ใหญ่ - อักษรไทยที่ถูกยืดจะแตกเป็นตัว ๆ อ่านยาก ("ค ว า ม ส า ม า ร ถ") */
    letter-spacing: 0;
    color: var(--omc-brand-deep);
}

body.page-home .home-eyebrow svg {
    width: 16px;
    height: 16px;
}

body.page-home .home-section-title {
    margin: 0;
    font-size: clamp(var(--omc-fs-3xl), 4vw, var(--omc-fs-4xl));
    line-height: var(--omc-lh-4xl);
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--omc-text);
}

body.page-home .home-section-lead {
    margin: var(--omc-sp-300) 0 0;
    max-width: var(--omc-measure);
    font-size: var(--omc-fs-lg);
    line-height: var(--omc-lh-lg);
    color: var(--omc-text-muted);
}

/* ---------- ปุ่ม ----------
   ระยะในปุ่มหลักคงที่ 8px/12px ตามสกิล ไม่ขยายตามขนาดจอ */
body.page-home .home-btn-primary,
body.page-home .home-btn-ghost {
    display: inline-flex;
    align-items: center;
    gap: var(--omc-sp-75);
    padding: var(--omc-sp-75) var(--omc-sp-100);
    border-radius: var(--omc-r-lg);
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color 300ms var(--omc-ease), color 300ms var(--omc-ease), transform 300ms var(--omc-ease);
}

body.page-home .home-btn-primary svg,
body.page-home .home-btn-ghost svg {
    width: 16px;
    height: 16px;
}

body.page-home .home-btn-primary {
    border: 1px solid var(--omc-brand);
    background: var(--omc-brand);
    color: var(--omc-ink);
}

body.page-home .home-btn-primary:hover,
body.page-home .home-btn-primary:focus-visible {
    background: var(--omc-brand-hover);
    border-color: var(--omc-brand-hover);
    color: var(--omc-ink);
}

body.page-home .home-btn-primary:active,
body.page-home .home-btn-ghost:active {
    transform: scale(0.98);
}

body.page-home .home-btn-ghost {
    border: 1px solid rgba(255, 255, 255, 0.24);
    background: transparent;
    color: var(--omc-on-ink);
}

body.page-home .home-btn-ghost:hover,
body.page-home .home-btn-ghost:focus-visible {
    background: rgba(255, 255, 255, 0.08);
    color: var(--omc-on-ink);
}

/* ปุ่มบนพื้นสว่าง (แถบราคา/ท้ายหน้า) */
body.page-home .home-btn-solid {
    display: inline-flex;
    align-items: center;
    gap: var(--omc-sp-75);
    padding: var(--omc-sp-75) var(--omc-sp-100);
    border: 1px solid var(--omc-brand-deep);
    border-radius: var(--omc-r-lg);
    background: var(--omc-brand-deep);
    color: #ffffff;
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    font-weight: 600;
    text-decoration: none;
    transition: background-color 300ms var(--omc-ease), transform 300ms var(--omc-ease);
}

body.page-home .home-btn-solid svg {
    width: 16px;
    height: 16px;
}

body.page-home .home-btn-solid:hover,
body.page-home .home-btn-solid:focus-visible {
    background: var(--omc-brand-deep-hover);
    border-color: var(--omc-brand-deep-hover);
    color: #ffffff;
}

body.page-home .home-btn-solid:active {
    transform: scale(0.98);
}

/* ---------- Hero ----------
   พื้นแบนสีเดียว (ห้าม gradient พื้นหลัง) - ความน่าสนใจมาจากคอนทราสต์กับพื้นหน้าและจอคิว ไม่ใช่จากไล่สี */
body.page-home .home-hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
    gap: var(--omc-sp-700);
    padding: var(--omc-sp-800);
    border-radius: var(--omc-r-3xl);
    background: var(--omc-ink);
    color: var(--omc-on-ink);
}

body.page-home .home-hero .home-eyebrow {
    color: var(--omc-brand);
}

/* B5: จุดเดียวในทั้งเว็บที่ใช้ gradient และใช้กับ "ตัวอักษร" เท่านั้น */
body.page-home .home-title {
    max-width: var(--omc-measure);
    margin: 0;
    font-size: clamp(var(--omc-fs-4xl), 5.5vw, var(--omc-fs-6xl));
    line-height: 1.1;
    font-weight: 700;
    letter-spacing: -0.03em;
    background-image: linear-gradient(to right, #ffffff, #9b9b9b);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

body.page-home .home-lead {
    max-width: var(--omc-measure);
    margin: var(--omc-sp-300) 0 0;
    font-size: var(--omc-fs-lg);
    line-height: var(--omc-lh-lg);
    color: var(--omc-on-ink-muted);
}

body.page-home .home-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--omc-sp-100);
    margin-top: var(--omc-sp-400);
}

body.page-home .home-hero-note {
    margin: var(--omc-sp-200) 0 0;
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    color: var(--omc-on-ink-muted);
}

/* ---------- Signature: จอเรียกคิวย่อส่วน ----------
   การ์ดนอก 24px, ระยะใน 8px -> ชั้นในใช้ 16px ตามสูตร inner = outer − gap */
body.page-home .home-board {
    margin: 0;
    padding: var(--omc-sp-75);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: var(--omc-r-3xl);
    background: var(--omc-ink-soft);
    box-shadow: 0 24px 48px rgba(0, 0, 0, 0.28);
}

body.page-home .home-board-inner {
    padding: var(--omc-sp-300);
    border-radius: var(--omc-r-2xl);
    background: var(--omc-ink);
}

body.page-home .home-board-bar {
    display: flex;
    align-items: center;
    gap: var(--omc-sp-75);
    padding-bottom: var(--omc-sp-200);
    border-bottom: 1px solid rgba(255, 255, 255, 0.10);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    color: var(--omc-on-ink-muted);
}

body.page-home .home-board-dot {
    width: 8px;
    height: 8px;
    border-radius: 999px;
    background: var(--omc-brand);
}

body.page-home .home-board-calling {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--omc-sp-75);
    margin-top: var(--omc-sp-300);
}

body.page-home .home-board-label {
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--omc-on-ink-muted);
}

body.page-home .home-board-number {
    font-family: var(--disp-font-num, "Segoe UI", "Arial Black", Arial, sans-serif);
    font-size: clamp(var(--omc-fs-5xl), 8vw, 72px);
    line-height: 1;
    font-weight: 700;
    letter-spacing: -0.03em;
    font-variant-numeric: tabular-nums;
    color: #ffffff;
}

body.page-home .home-board-counter {
    display: inline-flex;
    align-items: center;
    gap: var(--omc-sp-75);
    padding: var(--omc-sp-75) var(--omc-sp-100);
    border-radius: 999px;
    background: var(--omc-brand-deep);
    color: #ffffff;
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
}

body.page-home .home-board-counter svg {
    width: 16px;
    height: 16px;
}

body.page-home .home-board-queue {
    display: flex;
    flex-direction: column;
    gap: var(--omc-sp-75);
    margin: var(--omc-sp-300) 0 0;
    padding: 0;
    list-style: none;
}

/* ชั้นนอก 16px, ระยะใน 8px -> รายการในใช้ 8px */
body.page-home .home-board-ticket {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--omc-sp-200);
    padding: var(--omc-sp-100);
    border-radius: var(--omc-r-lg);
    background: rgba(255, 255, 255, 0.05);
    border-left: 3px solid var(--ticket-color, var(--omc-brand));
}

body.page-home .home-board-ticket-no {
    font-family: var(--disp-font-num, "Segoe UI", "Arial Black", Arial, sans-serif);
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: #ffffff;
}

body.page-home .home-board-ticket-meta {
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    color: var(--omc-on-ink-muted);
}

body.page-home .home-board-note {
    margin: var(--omc-sp-200) 0 0;
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    color: var(--omc-on-ink-muted);
}

/* ---------- แถบข้อเท็จจริงของระบบ ----------
   ‼️ ตัวเลขทุกตัวที่นี่ต้องอ้างจากพฤติกรรมจริงของระบบได้ ห้ามใส่จำนวนลูกค้า/คะแนนรีวิวที่ยังไม่มีจริง */
body.page-home .home-proof {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--omc-sp-600);
    margin: var(--omc-sp-700) 0 0;
    padding-top: var(--omc-sp-600);
    border-top: 1px solid var(--omc-border);
}

body.page-home .home-proof-value {
    margin: 0;
    font-size: var(--omc-fs-4xl);
    line-height: var(--omc-lh-4xl);
    font-weight: 700;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    color: var(--omc-text);
}

body.page-home .home-proof-text {
    margin: var(--omc-sp-75) 0 0;
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    color: var(--omc-text-muted);
}

/* ---------- เดิม / ใหม่ ---------- */
body.page-home .home-compare {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--omc-sp-200);
}

body.page-home .home-compare-card {
    padding: var(--omc-sp-300);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-2xl);
    background: var(--omc-surface);
    box-shadow: var(--omc-shadow);
    transition: transform 500ms var(--omc-ease), box-shadow 500ms var(--omc-ease), border-color 500ms var(--omc-ease);
}

body.page-home .home-compare-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--omc-shadow-lift);
    border-color: color-mix(in srgb, var(--omc-brand-deep) 30%, var(--omc-border));
}

body.page-home .home-compare-tag {
    display: block;
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    color: var(--omc-text-muted);
}

body.page-home .home-compare-before {
    margin: var(--omc-sp-75) 0 0;
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    color: var(--omc-text-muted);
    text-decoration: line-through;
    text-decoration-color: var(--omc-border);
}

body.page-home .home-compare-tag-after {
    display: flex;
    align-items: center;
    gap: var(--omc-sp-75);
    margin-top: var(--omc-sp-300);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    color: var(--omc-brand-deep);
}

body.page-home .home-compare-tag-after svg {
    width: 16px;
    height: 16px;
}

body.page-home .home-compare-after {
    margin: var(--omc-sp-75) 0 0;
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    font-weight: 600;
    color: var(--omc-text);
}

/* ---------- ความสามารถ ---------- */
body.page-home .home-features {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--omc-sp-200);
}

body.page-home .home-feature {
    padding: var(--omc-sp-300);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-2xl);
    background: var(--omc-surface);
    box-shadow: var(--omc-shadow);
    transition: transform 500ms var(--omc-ease), box-shadow 500ms var(--omc-ease), border-color 500ms var(--omc-ease);
}

body.page-home .home-feature-wide {
    grid-column: span 2;
}

body.page-home .home-feature:hover {
    transform: translateY(-2px);
    box-shadow: var(--omc-shadow-lift);
    border-color: color-mix(in srgb, var(--omc-brand-deep) 30%, var(--omc-border));
}

body.page-home .home-feature-icon,
body.page-home .home-flow-icon,
body.page-home .home-layer-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-xl);
    background: var(--omc-surface-2);
    color: var(--omc-brand-deep);
}

body.page-home .home-feature-icon svg,
body.page-home .home-flow-icon svg,
body.page-home .home-layer-icon svg {
    width: 20px;
    height: 20px;
}

body.page-home .home-feature-title,
body.page-home .home-flow-title,
body.page-home .home-layer-name {
    margin: var(--omc-sp-300) 0 0;
    font-size: var(--omc-fs-xl);
    line-height: var(--omc-lh-xl);
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--omc-text);
}

body.page-home .home-feature-text,
body.page-home .home-flow-text,
body.page-home .home-layer-text {
    margin: var(--omc-sp-75) 0 0;
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    color: var(--omc-text-muted);
}

/* ---------- ประโยคหลักของหน้า (สว่างทีละคำตอนเลื่อนถึง) ---------- */
body.page-home .home-tagline-section {
    margin-top: var(--omc-sp-900);
    padding: var(--omc-sp-900) 0;
}

/* ‼️ ภาษาไทยไม่มีช่องว่างระหว่างคำ ทั้งวลีจึงเป็นก้อนเดียวที่ตัดบรรทัดไม่ได้
   ขั้นล่างของ clamp ต้องเล็กพอให้วลียาวสุดยังอยู่ในจอ 390px ได้ ไม่งั้นตัวหนังสือล้นออกนอกจอ
   (สกิลกำหนดช่วง text-4xl ถึง text-6xl ไว้ - บนจอแคบยอมลงมาที่ text-2xl เพราะล้นจอแย่กว่า) */
body.page-home .home-tagline {
    max-width: var(--omc-measure);
    margin: 0 auto;
    font-size: clamp(var(--omc-fs-2xl), 5.5vw, var(--omc-fs-5xl));
    line-height: 1.2;
    font-weight: 600;
    letter-spacing: -0.02em;
    text-align: center;
    overflow-wrap: anywhere;
}

html.has-js body.page-home .home-tagline-word {
    color: color-mix(in srgb, var(--omc-text) 30%, transparent);
    transition: color 700ms var(--omc-ease);
}

html.has-js body.page-home .home-tagline-word.is-lit {
    color: var(--omc-text);
}

/* ---------- ลำดับการทำงานของคิว ---------- */
body.page-home .home-flow {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--omc-sp-200);
    margin: 0;
    padding: 0;
    list-style: none;
}

body.page-home .home-flow-item {
    padding: var(--omc-sp-300);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-2xl);
    background: var(--omc-surface);
    box-shadow: var(--omc-shadow);
    transition: transform 500ms var(--omc-ease), box-shadow 500ms var(--omc-ease), border-color 500ms var(--omc-ease);
}

body.page-home .home-flow-item:hover {
    transform: translateY(-2px);
    box-shadow: var(--omc-shadow-lift);
    border-color: color-mix(in srgb, var(--omc-brand-deep) 30%, var(--omc-border));
}

body.page-home .home-flow-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--omc-sp-200);
}

body.page-home .home-flow-step {
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: color-mix(in srgb, var(--omc-brand-deep) 45%, transparent);
}

/* ---------- สามชั้นการใช้งาน ---------- */
body.page-home .home-layers {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--omc-sp-200);
}

body.page-home .home-layer {
    padding: var(--omc-sp-300);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-2xl);
    background: var(--omc-surface);
    box-shadow: var(--omc-shadow);
    transition: transform 500ms var(--omc-ease), box-shadow 500ms var(--omc-ease), border-color 500ms var(--omc-ease);
}

body.page-home .home-layer:hover {
    transform: translateY(-2px);
    box-shadow: var(--omc-shadow-lift);
    border-color: color-mix(in srgb, var(--omc-brand-deep) 30%, var(--omc-border));
}

body.page-home .home-layer-role {
    display: block;
    margin-top: var(--omc-sp-50);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    color: var(--omc-brand-deep);
}

/* ---------- แถบราคาย่อบนหน้าแรก ---------- */
body.page-home .home-priceband {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--omc-sp-400);
    padding: var(--omc-sp-600);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-3xl);
    background: var(--omc-surface);
    box-shadow: var(--omc-shadow);
}

body.page-home .home-priceband-copy {
    max-width: var(--omc-measure);
}

body.page-home .home-priceband-title {
    margin: var(--omc-sp-200) 0 0;
    font-size: var(--omc-fs-3xl);
    line-height: var(--omc-lh-3xl);
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--omc-text);
}

body.page-home .home-priceband-text {
    margin: var(--omc-sp-100) 0 0;
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    color: var(--omc-text-muted);
}

body.page-home .home-priceband-figure {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--omc-sp-200);
}

body.page-home .home-priceband-from {
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    color: var(--omc-text-muted);
}

body.page-home .home-priceband-amount {
    font-size: var(--omc-fs-5xl);
    line-height: 1;
    font-weight: 700;
    letter-spacing: -0.03em;
    font-variant-numeric: tabular-nums;
    color: var(--omc-text);
}

body.page-home .home-priceband-unit {
    margin-left: var(--omc-sp-75);
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    font-weight: 500;
    color: var(--omc-text-muted);
}

/* ---------- คำถามที่พบบ่อย ---------- */
body.page-home .home-faq-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
    gap: var(--omc-sp-600);
}

body.page-home .home-faq-list {
    display: flex;
    flex-direction: column;
    gap: var(--omc-sp-75);
}

/* การ์ดนอก 16px, ระยะใน 8px -> หัวข้อด้านในใช้ 8px */
body.page-home .home-faq-item {
    padding: var(--omc-sp-75);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-2xl);
    background: var(--omc-surface);
    box-shadow: var(--omc-shadow);
    transition: border-color 500ms var(--omc-ease);
}

body.page-home .home-faq-item[open] {
    border-color: color-mix(in srgb, var(--omc-brand-deep) 30%, var(--omc-border));
}

body.page-home .home-faq-q {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--omc-sp-200);
    padding: var(--omc-sp-100) var(--omc-sp-200);
    border-radius: var(--omc-r-lg);
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    font-weight: 600;
    color: var(--omc-text);
    cursor: pointer;
    list-style: none;
    transition: background-color 300ms var(--omc-ease);
}

body.page-home .home-faq-q::-webkit-details-marker {
    display: none;
}

body.page-home .home-faq-q:hover {
    background: var(--omc-surface-2);
}

body.page-home .home-faq-caret {
    flex: none;
    width: 16px;
    height: 16px;
    color: var(--omc-text-muted);
    transition: transform 400ms var(--omc-ease);
}

body.page-home .home-faq-item[open] .home-faq-caret {
    transform: rotate(180deg);
}

body.page-home .home-faq-a {
    margin: 0;
    padding: var(--omc-sp-75) var(--omc-sp-200) var(--omc-sp-100);
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    color: var(--omc-text-muted);
}

/* ---------- ปิดท้าย ----------
   การ์ดนอก 24px, ระยะใน 8px -> ชั้นในใช้ 16px */
body.page-home .home-final {
    padding: var(--omc-sp-75);
    border-radius: var(--omc-r-3xl);
    background: var(--omc-ink-soft);
}

body.page-home .home-final-inner {
    padding: var(--omc-sp-800) var(--omc-sp-300);
    border-radius: var(--omc-r-2xl);
    background: var(--omc-ink);
    text-align: center;
}

body.page-home .home-final-title {
    max-width: var(--omc-measure);
    margin: 0 auto;
    font-size: clamp(var(--omc-fs-3xl), 4vw, var(--omc-fs-4xl));
    line-height: 1.2;
    font-weight: 700;
    letter-spacing: -0.02em;
    background-image: linear-gradient(to right, #ffffff, #9b9b9b);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

body.page-home .home-final-lead {
    max-width: var(--omc-measure);
    margin: var(--omc-sp-300) auto 0;
    font-size: var(--omc-fs-lg);
    line-height: var(--omc-lh-lg);
    color: var(--omc-on-ink-muted);
}

body.page-home .home-final-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--omc-sp-100);
    margin-top: var(--omc-sp-400);
}

body.page-home .home-final-promises {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--omc-sp-300);
    margin: var(--omc-sp-500) 0 0;
    padding: 0;
    list-style: none;
}

body.page-home .home-final-promise {
    display: flex;
    align-items: center;
    gap: var(--omc-sp-75);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    color: var(--omc-on-ink-muted);
}

body.page-home .home-final-promise svg {
    flex: none;
    width: 16px;
    height: 16px;
    color: var(--omc-brand);
}

/* ---------- หัวหน้าย่อยของเว็บสาธารณะ (/pricing, /contact) ---------- */
body.page-home .home-pagehead {
    padding: var(--omc-sp-800) var(--omc-sp-300) var(--omc-sp-900);
    border-radius: var(--omc-r-3xl);
    background: var(--omc-ink);
    text-align: center;
}

body.page-home .home-pagehead .home-eyebrow {
    color: var(--omc-brand);
}

body.page-home .home-pagehead-title {
    max-width: var(--omc-measure);
    margin: 0 auto;
    font-size: clamp(var(--omc-fs-4xl), 5vw, var(--omc-fs-5xl));
    line-height: 1.1;
    font-weight: 700;
    letter-spacing: -0.03em;
    background-image: linear-gradient(to right, #ffffff, #9b9b9b);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

body.page-home .home-pagehead-lead {
    max-width: var(--omc-measure);
    margin: var(--omc-sp-300) auto 0;
    font-size: var(--omc-fs-lg);
    line-height: var(--omc-lh-lg);
    color: var(--omc-on-ink-muted);
}

/* การ์ดเนื้อหาเลื่อนขึ้นไปทับขอบล่างของแผงมืด ให้สองส่วนเกี่ยวกันแทนที่จะเป็นสองบล็อกวางต่อกัน */
body.page-home .home-section-first {
    position: relative;
    z-index: 1;
    margin-top: calc(var(--omc-sp-600) * -1);
}

/* ---------- ตารางราคา ----------
   การ์ดนอก 24px, ระยะใน 8px -> ตารางด้านในใช้ 16px */
body.page-home .home-price-card {
    padding: var(--omc-sp-75);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-3xl);
    background: var(--omc-surface);
    box-shadow: var(--omc-shadow-lift);
    overflow: hidden;
}

body.page-home .home-price-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    border-radius: var(--omc-r-2xl);
    overflow: hidden;
}

body.page-home .home-price-table th,
body.page-home .home-price-table td {
    padding: var(--omc-sp-200) var(--omc-sp-300);
    text-align: center;
}

body.page-home .home-price-table thead th {
    background: var(--omc-surface-2);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    color: var(--omc-text-muted);
}

body.page-home .home-price-table tbody th {
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    color: var(--omc-text);
}

body.page-home .home-price-table tbody tr + tr th,
body.page-home .home-price-table tbody tr + tr td {
    border-top: 1px solid var(--omc-border);
}

body.page-home .home-price-table tbody tr:nth-child(odd) th,
body.page-home .home-price-table tbody tr:nth-child(odd) td {
    background: color-mix(in srgb, var(--omc-surface-2) 55%, transparent);
}

body.page-home .home-price-amount {
    font-size: var(--omc-fs-xl);
    line-height: var(--omc-lh-xl);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--omc-text);
}

body.page-home .home-price-unit {
    margin-left: var(--omc-sp-50);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 500;
    color: var(--omc-text-muted);
}

/* ---------- ติดต่อเรา ---------- */
body.page-home .home-contact {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--omc-sp-200);
}

/* ---------- แผนที่ท้ายรายการช่องทางติดต่อ ---------- */


/* กรอบแผนที่ - iframe ของ Google ไม่มีอัตราส่วนของตัวเอง ต้องกำหนดให้ ไม่งั้นสูง 150px ตามค่า default ของเบราว์เซอร์
   ‼️ overflow: hidden จำเป็น - iframe เป็นสี่เหลี่ยมมุมฉาก จะทับมุมโค้งของกรอบออกมาถ้าไม่ตัด */
body.page-home .home-contact-map {
    overflow: hidden;
    aspect-ratio: 16 / 10;
    margin-top: var(--omc-sp-300);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-lg);
    background: var(--omc-surface-2);
}

body.page-home .home-contact-map iframe {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
}

body.page-home .home-contact-map-link {
    display: inline-flex;
    align-items: center;
    gap: var(--omc-sp-50);
    margin-top: var(--omc-sp-100);
    color: var(--omc-brand-text);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    text-decoration: none;
}

body.page-home .home-contact-map-link:hover,
body.page-home .home-contact-map-link:focus-visible { text-decoration: underline; }

body.page-home .home-contact-map-link svg {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
}

body.page-home .home-contact-panel {
    padding: var(--omc-sp-300);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-2xl);
    background: var(--omc-surface);
    box-shadow: var(--omc-shadow);
}

body.page-home .home-contact-heading {
    margin: 0 0 var(--omc-sp-300);
    font-size: var(--omc-fs-xl);
    line-height: var(--omc-lh-xl);
    font-weight: 600;
    color: var(--omc-text);
}

/* ‼️ ย่อรายการช่องทางติดต่อลงจากเดิม เพื่อให้มีที่เหลือให้แผนที่ท้ายการ์ดโดยไม่ต้อง scroll
   ค่าที่ย่อยังอยู่บนขั้นของ token เดิมทั้งหมด (ไอคอน 32px, ระยะ 8px, ตัวอักษร 14px) */
body.page-home .home-contact-list {
    display: flex;
    flex-direction: column;
    gap: var(--omc-sp-50);
    margin: 0;
    padding: 0;
    list-style: none;
}

body.page-home .home-contact-item {
    display: flex;
    align-items: center;
    gap: var(--omc-sp-100);
    padding: var(--omc-sp-75);
    border-radius: var(--omc-r-lg);
    text-decoration: none;
    color: inherit;
    transition: background-color 300ms var(--omc-ease);
}

body.page-home a.home-contact-item:hover,
body.page-home a.home-contact-item:focus-visible {
    background: var(--omc-surface-2);
}

body.page-home .home-contact-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 32px;
    height: 32px;
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-lg);
    background: var(--omc-surface-2);
    color: var(--omc-brand-deep);
}

body.page-home .home-contact-icon svg {
    width: 16px;
    height: 16px;
}

body.page-home .home-contact-body {
    min-width: 0;
}

body.page-home .home-contact-label {
    display: block;
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    color: var(--omc-text-muted);
}

body.page-home .home-contact-value {
    display: block;
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    color: var(--omc-text);
    overflow-wrap: anywhere;
}

/* ---------- ฟอร์มให้เราติดต่อกลับ (หน้า /contact) ---------- */
body.page-home .home-form-lead {
    margin: calc(var(--omc-sp-300) * -1) 0 var(--omc-sp-300);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    color: var(--omc-text-muted);
}

body.page-home .home-form {
    display: flex;
    flex-direction: column;
    gap: var(--omc-sp-200);
}

body.page-home .home-form-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--omc-sp-200);
}

body.page-home .home-form-field {
    display: flex;
    flex-direction: column;
    gap: var(--omc-sp-75);
}

body.page-home .home-form-label {
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    color: var(--omc-text);
}

body.page-home .home-form-required {
    color: var(--omc-brand-deep);
}

body.page-home .home-form-optional {
    font-weight: 400;
    color: var(--omc-text-muted);
}

body.page-home .home-form-input {
    width: 100%;
    padding: var(--omc-sp-100);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-lg);
    background: var(--omc-page);
    color: var(--omc-text);
    font: inherit;
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    transition: border-color 300ms var(--omc-ease), background-color 300ms var(--omc-ease);
}

body.page-home .home-form-input:hover {
    border-color: color-mix(in srgb, var(--omc-brand-deep) 40%, var(--omc-border));
}

body.page-home .home-form-input:focus {
    outline: 2px solid var(--omc-brand-deep);
    outline-offset: 2px;
    border-color: var(--omc-brand-deep);
    background: var(--omc-surface);
}

body.page-home .home-form-textarea {
    min-height: 120px;
    resize: vertical;
}

/* honeypot - ต้องไม่ใช่ display:none เพราะบอตบางตัวข้ามช่องที่ซ่อนแบบนั้น
   ดันออกนอกจอแทน คนจริงกับ screen reader (aria-hidden ที่ตัวห่อ) จึงไม่เจอ แต่บอตที่อ่าน DOM ยังกรอก */
body.page-home .home-form-trap {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* ‼️ เต็มความกว้างของฟอร์ม ไม่ชิดซ้ายและไม่ชิดขวา - ปุ่มยืนยันของฟอร์มที่ยืนอยู่ลำพังในหน้า
   (ไม่ใช่ใน dialog) ควรกินความกว้างเต็มเพื่อให้เป็นเป้าหมายเดียวที่ชัดเจนและกดง่ายบนมือถือ
   การชิดขวาเป็นธรรมเนียมของ modal/dialog ที่มีปุ่มยกเลิกคู่กัน ซึ่งฟอร์มนี้ไม่มี */
body.page-home .home-form-submit {
    justify-content: center;
    width: 100%;
    margin-top: var(--omc-sp-75);
}

body.page-home .home-form-submit:disabled {
    opacity: 0.6;
    cursor: progress;
}

body.page-home .home-form-feedback {
    margin: 0;
    padding: var(--omc-sp-100);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-lg);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    color: var(--omc-text-muted);
}

body.page-home .home-form-feedback.is-success {
    border-color: color-mix(in srgb, var(--omc-brand-deep) 40%, var(--omc-border));
    background: color-mix(in srgb, var(--omc-brand-deep) 8%, transparent);
    color: var(--omc-brand-deep);
    font-weight: 600;
}

body.page-home .home-form-feedback.is-error {
    border-color: color-mix(in srgb, #d92d20 40%, var(--omc-border));
    background: color-mix(in srgb, #d92d20 8%, transparent);
    color: #b42318;
    font-weight: 600;
}

html[data-bs-theme="dark"] body.page-home .home-form-feedback.is-error {
    color: #ff9b93;
}

body.page-home .home-form-note {
    margin: 0;
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    color: var(--omc-text-muted);
}

@media (max-width: 768px) {
    body.page-home .home-form-row {
        grid-template-columns: minmax(0, 1fr);
    }

}

/* ---------- ท้ายหน้า ---------- */
body.page-home .home-footer {
    margin-top: var(--omc-sp-900);
    padding: var(--omc-sp-700) var(--omc-gutter) var(--omc-sp-600);
    border-top: 1px solid var(--omc-border);
}

body.page-home .home-footer-top {
    display: grid;
    grid-template-columns: minmax(0, 2fr) repeat(2, minmax(0, 1fr));
    gap: var(--omc-sp-600);
}

body.page-home .home-footer-about {
    max-width: 320px;
    margin: var(--omc-sp-200) 0 0;
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    color: var(--omc-text-muted);
}

body.page-home .home-footer-heading {
    margin: 0 0 var(--omc-sp-200);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--omc-text-muted);
}

body.page-home .home-footer-list {
    display: flex;
    flex-direction: column;
    gap: var(--omc-sp-100);
    margin: 0;
    padding: 0;
    list-style: none;
}

body.page-home .home-footer-link {
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    color: var(--omc-text-muted);
    text-decoration: none;
    transition: color 300ms var(--omc-ease);
}

body.page-home .home-footer-link:hover,
body.page-home .home-footer-link:focus-visible {
    color: var(--omc-text);
}

body.page-home .home-footer-bottom {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--omc-sp-75);
    margin-top: var(--omc-sp-600);
    padding-top: var(--omc-sp-300);
    border-top: 1px solid var(--omc-border);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    color: var(--omc-text-muted);
}

/* ---------- จังหวะการเข้าเนื้อหา ----------
   ทุกบล็อกลอยขึ้นพร้อมคลายเบลอตอนเลื่อนถึง ผูกด้วย IntersectionObserver ใน app.js
   ‼️ ห้ามเปลี่ยนไปผูกกับ scroll event เพราะจะ reflow ทุกเฟรมและกระตุกบนมือถือ */
/* ‼️ ซ่อนเฉพาะตอนที่ JS ทำงานได้จริง (html.has-js ติดมาจากสคริปต์ใน <head>)
   ถ้า JS พัง/ถูกบล็อก เนื้อหาทั้งหน้าต้องยังอ่านได้ตามปกติ ห้ามให้หน้าว่างเปล่า */
html.has-js body.page-home .home-reveal {
    opacity: 0;
    transform: translateY(64px);
    filter: blur(12px);
    transition:
        opacity 800ms var(--omc-ease),
        transform 800ms var(--omc-ease),
        filter 800ms var(--omc-ease);
}

html.has-js body.page-home .home-reveal.is-in {
    opacity: 1;
    transform: translateY(0);
    filter: blur(0);
}

/* ---------- โฟกัสด้วยคีย์บอร์ด ---------- */
body.page-home a:focus-visible,
body.page-home button:focus-visible,
body.page-home summary:focus-visible {
    outline: 2px solid var(--omc-brand-deep);
    outline-offset: 3px;
    border-radius: var(--omc-r-lg);
}

body.page-home .home-hero a:focus-visible,
body.page-home .home-final a:focus-visible,
body.page-home .home-pagehead a:focus-visible {
    outline-color: var(--omc-brand);
}

body.page-home .home-skip {
    position: absolute;
    left: -9999px;
}

body.page-home .home-skip:focus {
    position: fixed;
    top: var(--omc-sp-300);
    left: var(--omc-sp-300);
    z-index: 50;
    padding: var(--omc-sp-75) var(--omc-sp-100);
    border-radius: var(--omc-r-lg);
    background: var(--omc-brand-deep);
    color: #ffffff;
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    text-decoration: none;
}

/* แถบบนลอยอยู่เหนือเนื้อหา ต้องกันระยะให้หัวข้อที่กระโดดไปไม่ถูกบัง */
body.page-home [id] {
    scroll-margin-top: var(--omc-sp-900);
}

html:has(body.page-home) {
    scroll-behavior: smooth;
}

/* ปุ่มแจ้งปัญหาลอย (render_support_widget - CLAUDE.md ข้อ 13) ใช้ .btn-primary ของ Bootstrap
   ซึ่งกินสีจาก --bs-primary ของธีมหลังบ้าน จึงยังเป็นสีเดิมทั้งที่หน้านี้เปลี่ยนเป็นส้มแล้ว

   ‼️ override เฉพาะใต้ body.page-home เท่านั้น ตามที่ผู้ใช้กำหนด - ปุ่มนี้อยู่ทุกหน้าในระบบ
      **ห้ามไปแก้ markup ที่ render_support_widget() หรือแก้ .btn-primary/.support-fab แบบ global**
      ผู้ใช้สั่งชัดเจนว่าให้ปรับแค่หน้า landing หน้าอื่นต้องเหมือนเดิมทุกอย่าง (เคยลองทำเป็น class
      กลาง .support-fab แล้วถูกให้ย้อนกลับ เพราะแตะไฟล์ที่ทุกหน้าใช้ร่วมกัน)
   ‼️ ไอคอนสีขาวตามที่ผู้ใช้กำหนด จึงต้องใช้พื้น --omc-brand-deep (ขาวบนพื้นนี้ = 5.56 ผ่าน)
      ห้ามเปลี่ยนพื้นไปเป็น --omc-brand (#f7a332) โดยที่ไอคอนยังขาว - คู่นั้นได้แค่ 2.05
      ไอคอนจะจมหายไปกับพื้น ถ้าอยากใช้ #f7a332 เป็นพื้น ไอคอนต้องเป็น --omc-ink */
body.page-home #btn-support-widget {
    border-color: var(--omc-brand-deep);
    background-color: var(--omc-brand-deep);
    color: var(--omc-on-ink);
}

body.page-home #btn-support-widget:hover,
body.page-home #btn-support-widget:focus-visible {
    border-color: var(--omc-brand-deep-hover);
    background-color: var(--omc-brand-deep-hover);
    color: var(--omc-on-ink);
}

body.page-home #btn-support-widget:focus-visible {
    outline: 2px solid var(--omc-brand-deep);
    outline-offset: 2px;
}

/* ปุ่มยืนยันในกล่องแจ้งปัญหาของหน้านี้ - ใช้โทนเข้มเพราะเป็นปุ่มมีตัวอักษรสีขาว (5.56) */
body.page-home #support-modal .btn-primary {
    border-color: var(--omc-brand-deep);
    background-color: var(--omc-brand-deep);
    color: var(--omc-on-ink);
}

body.page-home #support-modal .btn-primary:hover,
body.page-home #support-modal .btn-primary:focus-visible {
    border-color: var(--omc-brand-deep-hover);
    background-color: var(--omc-brand-deep-hover);
    color: var(--omc-on-ink);
}

body.page-home #support-modal .btn-outline-primary {
    border-color: var(--omc-brand-deep);
    color: var(--omc-brand-deep);
}

body.page-home #support-modal .btn-outline-primary:hover,
body.page-home #support-modal .btn-outline-primary:focus-visible {
    border-color: var(--omc-brand-deep);
    background-color: var(--omc-brand-deep);
    color: var(--omc-on-ink);
}

/* ---------- กล่องเลือกหน้าสาธิต (เปิดจากปุ่มหลักของ hero) ----------
   ‼️ เป็น <dialog> ของเบราว์เซอร์ ไม่ใช่ modal ของ Bootstrap - กับดักโฟกัส ปิดด้วย Esc และฉากหลัง
   มากับ showModal() อยู่แล้ว สิ่งที่เหลือคือทาสีด้วย token ของหน้านี้เท่านั้น
   การ์ดนอก 24px ระยะใน 8px -> รายการข้างในใช้ 16px ตามสูตร inner = outer − gap */
body.page-home .home-demo {
    width: min(680px, calc(100vw - var(--omc-sp-400)));
    max-height: calc(100dvh - var(--omc-sp-500));
    padding: 0;
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-3xl);
    background: var(--omc-surface);
    color: var(--omc-text);
    box-shadow: var(--omc-shadow-lift);
    /* ‼️ กล่องไม่เลื่อนเอง - ตัวที่เลื่อนคือ .home-demo-body อย่างเดียว หัวกับท้ายจึงอยู่นิ่ง
       (ผู้ใช้กำหนด 2026-09-16) */
    overflow: hidden;
}

/* ‼️ ต้องผูกกับ [open] เท่านั้น - <dialog> ที่ยังไม่เปิดถูก user agent ตั้ง display:none ไว้
   ถ้าประกาศ display:flex ลอย ๆ กล่องจะโผล่ค้างอยู่บนหน้าตั้งแต่โหลดเสร็จโดยไม่มีใครกด */
body.page-home .home-demo[open] {
    display: flex;
}

body.page-home .home-demo::backdrop {
    /* ‼️ ::backdrop ไม่รับ custom property ที่ประกาศบน body - ต้องเป็นค่าตรงที่นี่จุดเดียวของหน้านี้ */
    background: rgba(15, 10, 5, 0.62);
}

/* ‼️ min-height: 0 จำเป็น - ลูกของ flex มีค่าปริยาย min-height:auto ซึ่งไม่ยอมหดต่ำกว่าเนื้อหา
   ผลคือ .home-demo-body ไม่มีวันได้ความสูงจำกัด แล้วไม่เกิดแถบเลื่อนของตัวเองเลย */
body.page-home .home-demo-inner {
    display: flex;
    flex-direction: column;
    min-height: 0;
    width: 100%;
}

/* หัวกล่อง - อยู่นิ่ง มีเส้นคั่นเนื้อด้านล่าง และมีปุ่มกากบาทชิดขวาบน */
body.page-home .home-demo-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--omc-sp-100);
    flex: none;
    padding: var(--omc-sp-300);
    border-bottom: 1px solid var(--omc-border);
}

body.page-home .home-demo-dismiss {
    display: grid;
    place-items: center;
    flex: none;
    width: 36px;
    height: 36px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--omc-r-lg);
    background: transparent;
    color: var(--omc-text-muted);
    cursor: pointer;
    transition: background-color 300ms var(--omc-ease), color 300ms var(--omc-ease);
}

body.page-home .home-demo-dismiss svg {
    width: 20px;
    height: 20px;
}

body.page-home .home-demo-dismiss:hover,
body.page-home .home-demo-dismiss:focus-visible {
    background: var(--omc-surface-2);
    color: var(--omc-text);
}

/* เนื้อกล่อง - มีแถบเลื่อนของตัวเองตัวเดียวในกล่องนี้ */
body.page-home .home-demo-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: var(--omc-sp-300);
}

body.page-home .home-demo-title {
    margin: 0;
    font-size: var(--omc-fs-2xl);
    line-height: var(--omc-lh-2xl);
    font-weight: 700;
    letter-spacing: -0.01em;
}

body.page-home .home-demo-lead {
    margin: var(--omc-sp-75) 0 0;
    max-width: var(--omc-measure);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    color: var(--omc-text-muted);
}

body.page-home .home-demo-list {
    display: grid;
    gap: var(--omc-sp-75);
    /* ‼️ ไม่มี margin บน - ระยะห่างจากหัวกล่องมาจาก padding ของ .home-demo-body แล้ว
       ถ้าใส่ซ้ำจะได้ช่องว่างสองเท่าใต้เส้นคั่น */
    margin: 0;
    padding: 0;
    list-style: none;
}

body.page-home .home-demo-item {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--omc-sp-200);
    padding: var(--omc-sp-200);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-2xl);
    background: var(--omc-surface-2);
    color: inherit;
    text-decoration: none;
    transition: transform 300ms var(--omc-ease), border-color 300ms var(--omc-ease), background-color 300ms var(--omc-ease);
}

body.page-home .home-demo-item:hover,
body.page-home .home-demo-item:focus-visible {
    transform: translateY(-2px);
    border-color: color-mix(in srgb, var(--omc-brand-deep) 38%, var(--omc-border));
    background: var(--omc-surface);
}

body.page-home .home-demo-item:active {
    transform: scale(0.98);
}

body.page-home .home-demo-item-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-lg);
    background: var(--omc-surface);
    color: var(--omc-brand-text);
}

body.page-home .home-demo-item-icon svg,
body.page-home .home-demo-item-go svg {
    width: 20px;
    height: 20px;
}

body.page-home .home-demo-item-copy {
    display: block;
    min-width: 0;
}

body.page-home .home-demo-item-title {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--omc-sp-75);
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    font-weight: 600;
}

body.page-home .home-demo-item-text {
    display: block;
    margin-top: var(--omc-sp-50);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    color: var(--omc-text-muted);
}

body.page-home .home-demo-badge {
    padding: var(--omc-sp-25) var(--omc-sp-75);
    border: 1px solid color-mix(in srgb, var(--omc-brand-deep) 30%, var(--omc-border));
    border-radius: var(--omc-r-lg);
    background: var(--omc-surface);
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    font-weight: 600;
    color: var(--omc-brand-text);
    white-space: nowrap;
}

body.page-home .home-demo-badge-login {
    border-color: var(--omc-border);
    color: var(--omc-text-muted);
}

body.page-home .home-demo-item-go {
    display: inline-flex;
    align-items: center;
    color: var(--omc-text-muted);
}

body.page-home .home-demo-data-note {
    display: flex;
    align-items: flex-start;
    gap: var(--omc-sp-75);
    margin: var(--omc-sp-300) 0 0;
    padding-top: var(--omc-sp-300);
    border-top: 1px solid var(--omc-border);
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    color: var(--omc-text-muted);
}

body.page-home .home-demo-data-note svg {
    flex: none;
    width: 16px;
    height: 16px;
    color: var(--omc-brand-text);
}

/* ‼️ ปุ่มหลักของ hero เป็น <button> ได้แล้ว (เปิดกล่องสาธิต) - ต้องล้าง style ปริยายของปุ่มทิ้ง
   ไม่งั้นได้ฟอนต์และเส้นขอบของเบราว์เซอร์มาแทนที่ของธีม */
body.page-home button.home-btn-primary {
    font: inherit;
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    font-weight: 600;
    cursor: pointer;
}

/* ---------- ฉากหลังจุดแสงของ hero + จอตัวอย่างที่เดินคิวเอง (2026-09-24 - ดู public/assets/js/home3d.js) ----------
   ‼️ ตกแต่งล้วน: ไม่มี JS/WebGL = canvas ว่าง hero เหมือนเดิมทุกอย่าง */
body.page-home .home-hero {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    perspective: 1800px;
}

body.page-home .home-hero-fx {
    position: absolute;
    inset: 0;
    z-index: -1;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

body.page-home .home-hero .home-board {
    transform-style: preserve-3d;
    will-change: transform;
}

/* ตอนเรียกคิวถัดไป จอกะพริบขอบสีแบรนด์หนึ่งครั้ง - คู่กับคลื่นจุดแสงด้านหลัง */
body.page-home .home-board.is-calling {
    animation: home-board-call 1.2s var(--omc-ease);
}

@keyframes home-board-call {
    from {
        box-shadow: 0 0 0 2px var(--omc-brand), 0 0 48px color-mix(in srgb, var(--omc-brand) 45%, transparent), 0 24px 48px rgba(0, 0, 0, 0.28);
    }
}

@media (prefers-reduced-motion: reduce) {
    body.page-home .home-board.is-calling {
        animation: none;
    }
}

/* ---------- เส้นทางของคิว (เส้นเวลาแบบภาพ - 2026-09-24) ----------
   ‼️ เป็นลำดับจริงของคิวหนึ่งใบ จึงมีเลขขั้นและเส้นเชื่อม บัตรตัวอย่างวิ่งตามเส้นตอนเลื่อนถึง (home3d.js) */
body.page-home .home-journey {
    position: relative;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--omc-sp-300);
    margin: var(--omc-sp-600) 0 0;
    padding: 0;
    list-style: none;
}

body.page-home .home-journey-track {
    position: absolute;
    top: 28px;
    right: 12.5%;
    left: 12.5%;
    height: 2px;
    background: repeating-linear-gradient(to right, var(--omc-border) 0 8px, transparent 8px 14px);
}

body.page-home .home-journey-ticket {
    position: absolute;
    top: 50%;
    left: 0;
    padding: var(--omc-sp-25) var(--omc-sp-75);
    border-radius: var(--omc-r-lg);
    background: var(--omc-ink);
    color: var(--omc-on-ink);
    font-family: var(--disp-font-num, "Segoe UI", Arial, sans-serif);
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    transform: translate(-50%, -50%);
    opacity: 0;
}

body.page-home .home-journey-stop {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
}

body.page-home .home-journey-node {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border: 1px solid var(--omc-border);
    border-radius: 50%;
    background: var(--omc-surface);
    color: var(--omc-brand-deep);
    box-shadow: var(--omc-shadow);
    transition: background-color 300ms var(--omc-ease), color 300ms var(--omc-ease), transform 300ms var(--omc-ease);
}

body.page-home .home-journey-node svg {
    width: 24px;
    height: 24px;
}

body.page-home .home-journey-stop.is-reached .home-journey-node {
    background: var(--omc-brand-deep);
    color: var(--omc-on-ink);
    transform: scale(1.06);
}

body.page-home .home-journey-step {
    margin-top: var(--omc-sp-200);
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--omc-brand-text);
}

body.page-home .home-journey-title {
    margin: var(--omc-sp-50) 0 0;
    font-size: var(--omc-fs-lg);
    line-height: var(--omc-lh-lg);
    font-weight: 600;
    color: var(--omc-text);
}

body.page-home .home-journey-text {
    max-width: 26ch;
    margin: var(--omc-sp-50) 0 0;
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    color: var(--omc-text-muted);
}

/* ---------- ความสามารถ: แท็บ 3 กลุ่ม + ภาพประกอบ (2026-09-24) ----------
   ‼️ ไม่มี JS = ทุกกลุ่มและคำอธิบายทุกข้อแสดงเรียงกัน (ดู initHomeFeatures ใน public.js) */
body.page-home .home-feat-tabs {
    display: none;
    gap: var(--omc-sp-75);
    padding: var(--omc-sp-50);
    border: 1px solid var(--omc-border);
    border-radius: 999px;
    background: var(--omc-surface);
    width: max-content;
    max-width: 100%;
    overflow-x: auto;
    scrollbar-width: none;
}

body.page-home .home-feat.is-ready .home-feat-tabs {
    display: flex;
}

body.page-home .home-feat-tab {
    display: inline-flex;
    align-items: center;
    gap: var(--omc-sp-75);
    padding: var(--omc-sp-75) var(--omc-sp-200);
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--omc-text-muted);
    font: inherit;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color 200ms var(--omc-ease), color 200ms var(--omc-ease);
}

body.page-home .home-feat-tab svg {
    width: 18px;
    height: 18px;
}

body.page-home .home-feat-tab:hover {
    color: var(--omc-text);
}

body.page-home .home-feat-tab[aria-selected="true"] {
    background: var(--omc-ink);
    color: var(--omc-on-ink);
}

body.page-home .home-feat-tab:focus-visible,
body.page-home .home-feat-item-head:focus-visible {
    outline: 2px solid var(--omc-brand-deep);
    outline-offset: 2px;
}

body.page-home .home-feat-panel {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
    gap: var(--omc-sp-600);
    margin-top: var(--omc-sp-300);
}

body.page-home .home-feat.is-ready .home-feat-panel[hidden] {
    display: none;
}

body.page-home .home-feat.is-ready .home-feat-panel {
    animation: home-feat-in 400ms var(--omc-ease);
}

@keyframes home-feat-in {
    from {
        opacity: 0;
        transform: translateY(var(--omc-sp-75));
    }
}

/* ภาพประกอบ - พื้นเข้มกรอบเดียวกับ hero ให้ทั้งหน้าพูดภาษาภาพเดียวกัน */
body.page-home .home-feat-visual {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 360px;
    padding: var(--omc-sp-500);
    border-radius: var(--omc-r-3xl);
    background:
        radial-gradient(80% 70% at 50% 40%, var(--omc-ink-soft), transparent 75%),
        var(--omc-ink);
    color: var(--omc-on-ink);
    overflow: hidden;
}

/* โทรศัพท์ของลูกค้า */
body.page-home .home-feat-phone {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--omc-sp-75);
    width: 180px;
    padding: var(--omc-sp-300) var(--omc-sp-200) var(--omc-sp-400);
    border: 2px solid color-mix(in srgb, var(--omc-on-ink) 22%, transparent);
    border-radius: var(--omc-r-3xl);
    background: color-mix(in srgb, var(--omc-on-ink) 5%, transparent);
}

body.page-home .home-feat-phone-notch {
    width: 48px;
    height: 6px;
    margin-bottom: var(--omc-sp-200);
    border-radius: 999px;
    background: color-mix(in srgb, var(--omc-on-ink) 22%, transparent);
}

body.page-home .home-feat-phone-label,
body.page-home .home-feat-phone-ahead {
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    color: var(--omc-on-ink-muted);
}

body.page-home .home-feat-phone-no,
body.page-home .home-feat-tv-no {
    font-family: var(--disp-font-num, "Segoe UI", Arial, sans-serif);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
    line-height: 1;
}

body.page-home .home-feat-phone-no {
    font-size: var(--omc-fs-5xl);
}

body.page-home .home-feat-phone-bar {
    width: 100%;
    height: 6px;
    margin-top: var(--omc-sp-100);
    border-radius: 999px;
    background: color-mix(in srgb, var(--omc-on-ink) 12%, transparent);
    overflow: hidden;
}

body.page-home .home-feat-phone-bar span {
    display: block;
    width: 70%;
    height: 100%;
    border-radius: inherit;
    background: var(--omc-brand);
}

body.page-home .home-feat-bubble {
    position: absolute;
    right: var(--omc-sp-500);
    bottom: var(--omc-sp-600);
    display: inline-flex;
    align-items: center;
    gap: var(--omc-sp-75);
    max-width: 220px;
    padding: var(--omc-sp-100) var(--omc-sp-200);
    border-radius: var(--omc-r-2xl) var(--omc-r-2xl) var(--omc-r-2xl) var(--omc-r-lg);
    background: var(--omc-surface);
    color: var(--omc-text);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    box-shadow: 0 16px 32px rgba(0, 0, 0, 0.3);
}

body.page-home .home-feat-bubble svg {
    flex: none;
    width: 20px;
    height: 20px;
    color: var(--omc-brand-deep);
}

/* จอหน้าร้าน */
body.page-home .home-feat-tv {
    display: grid;
    grid-template-columns: 1.4fr 1fr;
    gap: var(--omc-sp-200);
    width: min(100%, 380px);
    padding: var(--omc-sp-200);
    border: 2px solid color-mix(in srgb, var(--omc-on-ink) 22%, transparent);
    border-radius: var(--omc-r-xl);
    background: color-mix(in srgb, var(--omc-on-ink) 5%, transparent);
}

body.page-home .home-feat-tv-main {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--omc-sp-75);
}

body.page-home .home-feat-tv-no {
    font-size: var(--omc-fs-5xl);
}

body.page-home .home-feat-tv-counter {
    align-self: flex-start;
    padding: var(--omc-sp-25) var(--omc-sp-100);
    border-radius: 999px;
    background: var(--omc-brand-deep);
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    font-weight: 600;
}

body.page-home .home-feat-tv-side {
    display: flex;
    flex-direction: column;
    gap: var(--omc-sp-75);
}

body.page-home .home-feat-tv-side span {
    padding: var(--omc-sp-50) var(--omc-sp-100);
    border-left: 3px solid var(--ticket-color, var(--omc-brand));
    border-radius: var(--omc-r-lg);
    background: color-mix(in srgb, var(--omc-on-ink) 6%, transparent);
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

body.page-home .home-feat-tv-stand {
    width: 72px;
    height: 10px;
    margin-top: var(--omc-sp-100);
    border-radius: 0 0 var(--omc-r-lg) var(--omc-r-lg);
    background: color-mix(in srgb, var(--omc-on-ink) 22%, transparent);
}

/* กราฟของเจ้าของกิจการ - ความสูงเป็นภาพแทน ไม่ใช่ข้อมูลจริง */
body.page-home .home-feat-chart {
    display: flex;
    align-items: flex-end;
    gap: var(--omc-sp-100);
    width: min(100%, 360px);
    height: 180px;
    padding-bottom: var(--omc-sp-75);
    border-bottom: 1px solid color-mix(in srgb, var(--omc-on-ink) 22%, transparent);
}

body.page-home .home-feat-chart-bar {
    flex: 1;
    height: 40%;
    border-radius: var(--omc-r-lg) var(--omc-r-lg) 0 0;
    background: color-mix(in srgb, var(--omc-brand) 55%, transparent);
    transform-origin: bottom;
}

body.page-home .home-feat-chart-bar:nth-child(1) { height: 28%; }
body.page-home .home-feat-chart-bar:nth-child(2) { height: 44%; }
body.page-home .home-feat-chart-bar:nth-child(3) { height: 62%; }
body.page-home .home-feat-chart-bar:nth-child(4) { height: 92%; background: var(--omc-brand); }
body.page-home .home-feat-chart-bar:nth-child(5) { height: 76%; }
body.page-home .home-feat-chart-bar:nth-child(6) { height: 52%; }
body.page-home .home-feat-chart-bar:nth-child(7) { height: 38%; }
body.page-home .home-feat-chart-bar:nth-child(8) { height: 66%; }
body.page-home .home-feat-chart-bar:nth-child(9) { height: 84%; }
body.page-home .home-feat-chart-bar:nth-child(10) { height: 58%; }

body.page-home .home-feat.is-ready .home-feat-panel:not([hidden]) .home-feat-chart-bar {
    animation: home-feat-bar 700ms var(--omc-ease) both;
}

body.page-home .home-feat-chart-bar:nth-child(even) {
    animation-delay: 60ms;
}

@keyframes home-feat-bar {
    from {
        transform: scaleY(0.1);
    }
}

body.page-home .home-feat-chart-legend {
    display: flex;
    gap: var(--omc-sp-300);
    margin-top: var(--omc-sp-200);
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    color: var(--omc-on-ink-muted);
}

/* รายการความสามารถ - เห็นคำอธิบายทีละข้อ */
body.page-home .home-feat-list {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: 0;
    list-style: none;
}

body.page-home .home-feat-item {
    border-bottom: 1px solid var(--omc-border);
}

body.page-home .home-feat-item-head {
    display: flex;
    align-items: center;
    gap: var(--omc-sp-200);
    width: 100%;
    padding: var(--omc-sp-200) 0;
    border: 0;
    background: none;
    color: var(--omc-text-muted);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

body.page-home .home-feat-item-title {
    font-size: var(--omc-fs-lg);
    line-height: var(--omc-lh-lg);
    font-weight: 600;
    transition: color 200ms var(--omc-ease);
}

body.page-home .home-feat-item-head:hover .home-feat-item-title,
body.page-home .home-feat-item.is-open .home-feat-item-title {
    color: var(--omc-text);
}

body.page-home .home-feat-item-text {
    margin: 0;
    padding: 0 0 var(--omc-sp-200) calc(40px + var(--omc-sp-200));
    font-size: var(--omc-fs-base);
    line-height: var(--omc-lh-base);
    color: var(--omc-text-muted);
}

body.page-home .home-feat.is-ready .home-feat-item:not(.is-open) .home-feat-item-text {
    display: none;
}

/* ---------- สามชั้น: แผนผังกล่องต่อกัน (2026-09-24) ---------- */
body.page-home .home-tiers {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--omc-sp-400);
    margin: var(--omc-sp-500) 0 0;
    padding: 0;
    list-style: none;
}

body.page-home .home-tier {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--omc-sp-200);
    padding: var(--omc-sp-300);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-2xl);
    background: var(--omc-surface);
}

/* ลูกศรเชื่อมระหว่างชั้น: OMC -> องค์กร -> สาขา */
body.page-home .home-tier + .home-tier::before {
    content: "";
    position: absolute;
    top: 50%;
    left: calc(-1 * var(--omc-sp-400));
    width: var(--omc-sp-400);
    height: 2px;
    background: var(--omc-border);
}

body.page-home .home-tier-copy {
    display: flex;
    flex-direction: column;
}

body.page-home .home-tier-name {
    font-size: var(--omc-fs-xl);
    line-height: var(--omc-lh-xl);
    font-weight: 700;
    color: var(--omc-text);
}

body.page-home .home-tier-role {
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    color: var(--omc-text-muted);
}

@media (max-width: 1024px) {
    body.page-home .home-feat-panel {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--omc-sp-300);
    }

    body.page-home .home-feat-visual {
        min-height: 280px;
    }
}

@media (max-width: 767.98px) {
    body.page-home .home-journey {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--omc-sp-300);
    }

    /* จอแคบ: เส้นเวลาเป็นแนวตั้ง ไอคอนอยู่ซ้าย ข้อความอยู่ขวา */
    body.page-home .home-journey-track {
        top: 28px;
        bottom: 28px;
        left: 27px;
        right: auto;
        width: 2px;
        height: auto;
        background: repeating-linear-gradient(to bottom, var(--omc-border) 0 8px, transparent 8px 14px);
    }

    body.page-home .home-journey-ticket {
        display: none;
    }

    body.page-home .home-journey-stop {
        display: grid;
        grid-template-columns: 56px minmax(0, 1fr);
        column-gap: var(--omc-sp-200);
        align-items: center;
        text-align: left;
    }

    body.page-home .home-journey-node {
        grid-row: span 3;
    }

    body.page-home .home-journey-step {
        margin: 0;
    }

    body.page-home .home-journey-text {
        max-width: none;
    }

    body.page-home .home-feat-tabs {
        width: 100%;
    }

    body.page-home .home-feat-tab {
        flex: 1;
        justify-content: center;
        padding: var(--omc-sp-75) var(--omc-sp-100);
    }

    body.page-home .home-feat-tab svg {
        display: none;
    }

    body.page-home .home-feat-bubble {
        right: var(--omc-sp-200);
        bottom: var(--omc-sp-300);
    }

    body.page-home .home-tiers {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--omc-sp-200);
    }

    body.page-home .home-tier + .home-tier::before {
        top: calc(-1 * var(--omc-sp-200));
        left: calc(var(--omc-sp-300) + 19px);
        width: 2px;
        height: var(--omc-sp-200);
    }
}

@media (prefers-reduced-motion: reduce) {
    body.page-home .home-feat.is-ready .home-feat-panel,
    body.page-home .home-feat.is-ready .home-feat-panel:not([hidden]) .home-feat-chart-bar {
        animation: none;
    }

    body.page-home .home-journey-node {
        transition: none;
    }
}

/* ---------- จอทีวีบน hero - ฝังจอแสดงคิวจริงของสาขาสาธิต (โหมดพรีวิว) 2026-09-24 ----------
   ‼️ ตั้งเฉียงหันหน้าไปทางซ้าย (เข้าหาข้อความของ hero) home3d.js เอียงเพิ่มตามเมาส์รอบมุมตั้งต้นนี้
   ‼️ จอจริงวาดที่ 1280x720 แล้วย่อลงด้วย --tv-scale (home3d.js คำนวณจากความกว้างจอ) ตัวอักษรบนจอจึงไม่เพี้ยน */
body.page-home .home-tv {
    /* ‼️ ขอบจอเป็นเทากราไฟต์ ไม่ใช่น้ำตาลเข้มแบบพื้น hero - ไม่งั้นทีวีกลืนไปกับพื้น (ผู้ใช้สั่ง 2026-09-24) */
    --tv-bezel: color-mix(in srgb, var(--omc-ink) 30%, #3f3f46);
    --tv-bezel-edge: color-mix(in srgb, var(--tv-bezel) 55%, #000000);
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    margin: 0;
    /* ‼️ figure แบน + มุมมองของตัวเอง: ให้ 3D อยู่แค่ในชุดจอ (.home-tv-stack)
       ถ้า figure เป็น preserve-3d ปุ่มสลับ/ป้ายใต้จอจะถูกวาดในฉาก 3D ด้วย ตัวอักษรเบลอ (ผู้ใช้เห็นเป็นฝ้าที่ปุ่ม 2026-09-25) */
    perspective: 1800px;
    transform-style: flat;
}

/* ไฟหลังทีวี (bias light) สีแบรนด์ - แยกตัวจอออกจากพื้นเข้ม วางที่ figure ซึ่งไม่เอียง แสงจึงไม่เบี้ยว
   ‼️ ต้องดันถอยลึกไปหลังจอ (translateZ) - figure เป็น preserve-3d แผ่นแสงที่ z=0 จะตัดผ่านจอที่เอียงอยู่
      ครึ่งจอที่ยื่นออกมาจะอยู่หน้าแสง อีกครึ่งอยู่หลังแสง เห็นเป็นแสงทับจอมีรอยตัดเฉียง (ผู้ใช้เจอ 2026-09-24) */
body.page-home .home-tv::before {
    content: "";
    position: absolute;
    inset: -4% -8% 4%;
    z-index: -1;
    transform: translateZ(-400px) scale(1.25);
    /* ผู้ใช้ขอให้สว่างขึ้น (2026-09-24) 34% -> 52% */
    background: radial-gradient(closest-side, color-mix(in srgb, var(--omc-brand) 52%, transparent), transparent);
    pointer-events: none;
}

body.page-home .home-tv-body {
    position: relative;
    width: 100%;
    /* ‼️ ขอบจอเป็น % ของความกว้างชุดทีวี (padding % คิดจากกล่องแม่ = .home-tv-stack ทั้งสองจอ) ไม่ใช่ px ตายตัว
       - px ตายตัวพอทีวีย่อบนจอเล็ก ขอบจะหนาเทอะทะเหมือนแท็บเล็ต ไม่ใช่ทีวีขอบบาง (ผู้ใช้เจอ 2026-09-24)
       มุมเกือบเหลี่ยมแบบทีวีจริง ไม่มนแบบแท็บเล็ต */
    padding: 1.1%;
    border-radius: 4px;
    background: linear-gradient(160deg, color-mix(in srgb, var(--tv-bezel) 70%, #ffffff), var(--tv-bezel) 22%, var(--tv-bezel-edge));
    transform-style: preserve-3d;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.28),
        inset 0 0 0 1px rgba(255, 255, 255, 0.12),
        inset 0 -2px 0 rgba(0, 0, 0, 0.35),
        0 0 0 1px rgba(255, 255, 255, 0.06),
        0 40px 60px -20px rgba(0, 0, 0, 0.7);
}

/* ความหนาของตัวเครื่องแบบกล่องทึบ: ผนังด้านข้าง (::after) พับลึกไปด้านหลัง 90 องศา + แผ่นหลัง (::before)
   ‼️ ทีวีหันเฉียงไปทางซ้าย ขอบขวาจึงใกล้คนดูกว่า ด้านที่มองเห็นคือ "ผนังขวา" - ผนังซ้ายไม่ต้องทำ
   ‼️ rotateY(-90deg) รอบขอบขวา = พับเข้าไปด้านหลังจอ (ถ้า +90 จะพับยื่นออกมาหน้าจอ)
   ‼️ ความลึกต้องเท่ากันทั้งผนังและแผ่นหลัง (--tv-depth) ไม่งั้นกล่องไม่ปิดมุม */
body.page-home .home-tv {
    --tv-depth: 46px;
}

body.page-home .home-tv-body::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: var(--tv-bezel-edge);
    transform: translateZ(calc(-1 * var(--tv-depth)));
}

body.page-home .home-tv-body::after {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: var(--tv-depth);
    /* ผนังข้างรับแสงจากด้านขวา สว่างกว่าหน้ากากเล็กน้อย - สีเดียวกับหน้ากากจะดูไม่ออกว่ามีความหนา */
    background: linear-gradient(to left, color-mix(in srgb, var(--tv-bezel) 65%, #ffffff), var(--tv-bezel) 55%, var(--tv-bezel-edge));
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22);
    transform-origin: right center;
    transform: rotateY(-90deg);
}

body.page-home .home-tv-screen {
    position: relative;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    border-radius: 2px;
    background: #000000;
}

body.page-home .home-tv-frame {
    display: block;
    width: 1280px;
    height: 720px;
    border: 0;
    transform: scale(var(--tv-scale, 0.4));
    transform-origin: 0 0;
    pointer-events: none;
}

/* แสงสะท้อนบนกระจกจอ */
body.page-home .home-tv-glare {
    position: absolute;
    inset: 0;
    background: linear-gradient(115deg, rgba(255, 255, 255, 0.14) 0%, rgba(255, 255, 255, 0.03) 28%, transparent 42%);
    pointer-events: none;
}

body.page-home .home-tv-note {
    margin-top: var(--omc-sp-300);
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    color: var(--omc-on-ink-muted);
}

/* ตอนจอเรียกคิวถัดไป ขอบจอกะพริบสีแบรนด์หนึ่งครั้ง */
body.page-home .home-tv.is-calling .home-tv-body {
    animation: home-tv-call 1.1s var(--omc-ease);
}

@keyframes home-tv-call {
    from {
        box-shadow:
            inset 0 0 0 1px rgba(255, 255, 255, 0.08),
            0 0 0 2px var(--omc-brand),
            0 0 56px color-mix(in srgb, var(--omc-brand) 45%, transparent),
            0 40px 60px -20px rgba(0, 0, 0, 0.6);
    }
}


@media (prefers-reduced-motion: reduce) {
    body.page-home .home-tv.is-calling .home-tv-body {
        animation: none;
    }
}

/* จอแสดงคิวตอนเป็นพรีวิวในกรอบทีวี - ไม่มีปุ่มเปิดเสียง (กดไม่ได้อยู่แล้ว) */
html.is-display-preview #btn-enable-sound {
    display: none;
}

/* ---------- สิ่งที่ร้านต้องมี (2026-09-24) ----------
   ป้าย "จำเป็น / แนะนำ / มีก็ได้" บอกน้ำหนักของอุปกรณ์แต่ละชิ้น - ไม่ใช่การตกแต่ง */
body.page-home .home-req {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: var(--omc-sp-200);
    margin: var(--omc-sp-500) 0 0;
    padding: 0;
    list-style: none;
}

/* จัดกลางทั้งหัวข้อและการ์ด (ผู้ใช้สั่ง 2026-09-25) - จอมือถือกลับเป็นไอคอนซ้าย/ข้อความขวา ดู media ด้านล่าง */
body.page-home #home-requirements .home-section-head {
    margin-inline: auto;
    text-align: center;
}

body.page-home .home-req-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: var(--omc-sp-300);
    border: 1px solid var(--omc-border);
    border-radius: var(--omc-r-2xl);
    background: var(--omc-surface);
    transition: border-color 0.2s var(--omc-ease);
}

/* ชี้แล้วขอบเป็นสีแบรนด์ (ผู้ใช้สั่ง 2026-09-25) - การ์ดไม่ใช่ลิงก์ จึงเปลี่ยนแค่ขอบ ไม่ยก/ไม่เปลี่ยนเคอร์เซอร์ */
body.page-home .home-req-item:hover {
    border-color: var(--omc-brand-text);
}

body.page-home .home-req-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: var(--omc-r-xl);
    /* พื้นเทาอ่อน + ไอคอนส้มเส้นหนา (ผู้ใช้สั่ง 2026-09-24) - --omc-brand-text สลับเป็นส้มสว่างเองในโหมดมืด */
    background: var(--omc-surface-2);
    box-shadow: inset 0 0 0 1px var(--omc-border);
    color: var(--omc-brand-text);
}

body.page-home .home-req-icon svg {
    width: 30px;
    height: 30px;
}

/* Phosphor ชุด regular เส้นบาง - ใส่เส้นขอบสีเดียวกันทับเพื่อให้เส้นหนาขึ้น (หน่วยเป็น viewBox 256) */
body.page-home .home-req-icon svg path {
    stroke: currentColor;
    stroke-width: 6;
    stroke-linejoin: round;
}

body.page-home .home-req-badge {
    margin-top: var(--omc-sp-200);
    padding: var(--omc-sp-25) var(--omc-sp-75);
    border-radius: 999px;
    font-size: var(--omc-fs-xs);
    line-height: var(--omc-lh-xs);
    font-weight: 700;
}

body.page-home .home-req-badge-required {
    background: var(--omc-brand-deep);
    color: var(--omc-on-ink);
}

body.page-home .home-req-badge-recommended {
    background: color-mix(in srgb, var(--omc-brand) 22%, transparent);
    color: var(--omc-brand-text);
}

body.page-home .home-req-badge-optional {
    background: var(--omc-surface-2);
    color: var(--omc-text-muted);
}

body.page-home .home-req-title {
    margin: var(--omc-sp-100) 0 0;
    font-size: var(--omc-fs-lg);
    line-height: var(--omc-lh-lg);
    font-weight: 600;
    color: var(--omc-text);
}

body.page-home .home-req-text {
    margin: var(--omc-sp-75) 0 0;
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    color: var(--omc-text-muted);
}

@media (max-width: 1199.98px) {
    body.page-home .home-req {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

@media (max-width: 767.98px) {
    body.page-home .home-req {
        grid-template-columns: minmax(0, 1fr);
    }

    /* จอแคบ: ไอคอนอยู่ซ้าย ข้อความอยู่ขวา ประหยัดความสูง */
    body.page-home .home-req-item {
        display: grid;
        grid-template-columns: 56px minmax(0, 1fr);
        column-gap: var(--omc-sp-200);
        align-items: start;
        text-align: left;
    }

    body.page-home .home-req-icon {
        grid-row: span 3;
    }

    body.page-home .home-req-badge {
        justify-self: start;
        margin-top: 0;
    }
}

/* ---------- hero เต็มความกว้างจอ ไม่อยู่ในกล่อง (ผู้ใช้สั่ง 2026-09-24) ----------
   ‼️ ดึงออกนอก padding ของ .home-page ด้วย margin ติดลบเท่ากับระยะขอบ แล้วใส่ระยะเดิมกลับเป็น padding ด้านใน
      เนื้อหาใน hero จึงยังตรงแนวกับหัวข้ออื่นของหน้า
   ‼️ ระยะขอบคำนวณจาก cqw (ความกว้างของ body) ไม่ใช้ --omc-gutter ตรง ๆ - % ใน --omc-gutter คิดจากกล่องที่ใช้งาน
      ใน hero จะได้ค่าผิด / ไม่ใช้ 100vw เพราะรวมความกว้าง scrollbar ทำให้หน้าเลื่อนซ้าย-ขวาได้ */
body.page-home {
    container-type: inline-size;
}

body.page-home .home-hero {
    --home-bleed: max(var(--omc-sp-300), calc((100cqw - var(--omc-container)) / 2));
    margin: calc(-1 * var(--omc-sp-600)) calc(-1 * var(--home-bleed)) 0;
    padding: var(--omc-sp-900) var(--home-bleed);
    border-radius: 0;
    /* สีพื้นเต็มจอแรกพอดี (ผู้ใช้สั่ง 2026-09-24) - 80px = ความสูงแถบเมนูด้านบน (วัดจริง) */
    min-height: calc(100svh - 80px);
}

@media (max-width: 1024px) {
    body.page-home .home-hero {
        padding: var(--omc-sp-700) var(--home-bleed);
    }
}

@media (max-width: 768px) {
    body.page-home .home-hero {
        margin-top: calc(-1 * var(--omc-sp-800));
    }
}

/* ---------- พื้น hero หน้าแรก + หัวหน้า "ราคา" / "ติดต่อเรา" = สีตั้งต้นก่อนเริ่มแก้ #241a10 (ผู้ใช้สั่ง 2026-09-24) ----------
   ‼️ ลองมาแล้ว: ส้มสด / ส้ม / ส้มไหม้ / เทียล / กรมท่า - ผู้ใช้กลับมาใช้สีเดิมนี้ อย่าเปลี่ยนเองอีก
   ‼️ สีเข้มหลักของหน้า (--omc-ink) เปลี่ยนเป็นกรมท่าไปแล้ว จึงต้องล็อกสีเดิมของสองส่วนนี้ไว้ที่ตัวแปรของมันเอง
      ตัวอักษรรองก็ใช้สีเทาอุ่นเดิม (#c4b39d คอนทราสต์บนพื้นนี้ 8.36) ไม่ใช่เทาอมฟ้าของ --omc-on-ink-muted ชุดใหม่ */
body.page-home {
    --home-hero-bg: #241a10;
    --home-hero-muted: #c4b39d;
}

body.page-home .home-hero,
body.page-home .home-pagehead {
    background: var(--home-hero-bg);
}

body.page-home .home-hero .home-lead,
body.page-home .home-hero .home-hero-note,
body.page-home .home-hero .home-tv-note,
body.page-home .home-pagehead-lead {
    color: var(--home-hero-muted);
}
/* ---------- หัวหน้า "ราคา" / "ติดต่อเรา" ไม่แบนเกินไป (ผู้ใช้สั่ง 2026-09-25) ----------
   สีพื้นเดิม #241a10 ไม่เปลี่ยน - เพิ่มแค่ดวงแสงส้มนุ่มจากมุม + ลายจุดจาง ๆ ให้มีมิติแบบเดียวกับ hero หน้าแรก
   ‼️ แสง/ลายอยู่ใน ::before/::after หลังเนื้อหา (z-index -1 ใต้ isolation) ไม่ทับตัวอักษร */
body.page-home .home-pagehead {
    position: relative;
    isolation: isolate;
    overflow: hidden;
}

body.page-home .home-pagehead::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
        radial-gradient(40% 70% at 12% 0%, color-mix(in srgb, var(--omc-brand) 30%, transparent), transparent 70%),
        radial-gradient(45% 80% at 90% 110%, color-mix(in srgb, var(--omc-brand-deep) 45%, transparent), transparent 70%);
    pointer-events: none;
}

body.page-home .home-pagehead::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background-image: radial-gradient(color-mix(in srgb, var(--omc-on-ink) 9%, transparent) 1px, transparent 1.5px);
    background-size: 22px 22px;
    -webkit-mask-image: radial-gradient(70% 90% at 50% 40%, #000000, transparent);
    mask-image: radial-gradient(70% 90% at 50% 40%, #000000, transparent);
    pointer-events: none;
}

/* ป้ายหัวข้อเป็นเม็ดยาแบบเดียวกับ hero / หัวเรื่องขาวล้วน ระยะบรรทัดพอสำหรับสระไทย */
body.page-home .home-pagehead .home-eyebrow {
    padding: var(--omc-sp-50) var(--omc-sp-200);
    border: 1px solid color-mix(in srgb, var(--omc-brand) 35%, transparent);
    border-radius: 999px;
    background: color-mix(in srgb, var(--omc-brand) 12%, transparent);
    color: var(--omc-brand);
    font-weight: 500;
    letter-spacing: 0;
}

body.page-home .home-pagehead-title {
    line-height: 1.3;
    letter-spacing: 0;
    background-image: none;
    color: var(--omc-on-ink);
}

/* ---------- ตัวอักษรใน hero (ผู้ใช้ขอให้สวยขึ้น 2026-09-24) ----------
   ‼️ หัวเรื่องภาษาไทยต้องมีระยะบรรทัดพอให้สระบน/วรรณยุกต์ (ให้ ที่ กี่) ไม่ชนบรรทัดบน - 1.1 แน่นเกิน
   ‼️ ห้ามระยะตัวอักษรติดลบกับอักษรไทย - สระ/วรรณยุกต์จะเบียดกัน
   หัวเรื่องขาวล้วน (เลิกไล่ขาว-เทาที่ทำให้บรรทัดล่างดูหม่น) / ป้ายบนหัวเรื่องเป็นเม็ดยาสีส้มจาง */
body.page-home .home-hero .home-eyebrow {
    padding: var(--omc-sp-50) var(--omc-sp-200) var(--omc-sp-50) var(--omc-sp-100);
    border: 1px solid color-mix(in srgb, var(--omc-brand) 35%, transparent);
    border-radius: 999px;
    background: color-mix(in srgb, var(--omc-brand) 12%, transparent);
    color: var(--omc-brand);
    font-weight: 500;
}

body.page-home .home-hero .home-title {
    margin-top: var(--omc-sp-100);
    line-height: 1.28;
    letter-spacing: 0;
    background-image: none;
    color: var(--omc-on-ink);
}

body.page-home .home-hero .home-lead {
    max-width: 34em;
    margin-top: var(--omc-sp-400);
    line-height: 1.75;
}

body.page-home .home-hero .home-actions {
    margin-top: var(--omc-sp-500);
}

body.page-home .home-hero .home-hero-note {
    color: color-mix(in srgb, var(--home-hero-muted) 80%, transparent);
}

/* คอลัมน์จอกว้างกว่าคอลัมน์ข้อความ ให้จอโชว์ใหญ่ (ผู้ใช้สั่ง 2026-09-24) */
@media (min-width: 1025px) {
    body.page-home .home-hero {
        grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
    }
}

/* ---------- ขนาดชุดทีวีตามจอ (ผู้ใช้เจอจอเล็ก/เตี้ยแล้วทีวีล้นจอ 2026-09-24) ----------
   ‼️ จอแนวตั้ง (โหมดที่สูงที่สุด) สูง 0.75 เท่าของความกว้างชุด + ปุ่มสลับแนวจอ + ป้ายใต้จอ ถ้าปล่อยกว้างเต็มคอลัมน์
      จอโน้ตบุ๊กที่เตี้ย (สูง 700-850px) จะเห็นทีวีโดนตัดล่าง / จอคอลัมน์เดียว (<=1024) ทีวีจะใหญ่ล้น hero
   -> คุมความกว้างจากความสูงจอ: เหลือพื้นที่หลังหักแถบเมนู + ระยะบน-ล่างของ hero + ปุ่มสลับ + ป้ายใต้จอ ราว 400px */
body.page-home .home-tv {
    width: 100%;
    /* คุมเฉพาะจอแนวนอน (สูง 0.6 เท่าของความกว้าง) - จอแนวตั้งคุมความสูงของตัวเองแยก */
    max-width: max(360px, calc((100svh - 300px) * 1.6));
    margin-inline: auto;
}

@media (max-width: 1024px) {
    body.page-home .home-tv {
        max-width: min(560px, 100%);
    }
}

/* ---------- โชว์ทีละจอ + ปุ่มสลับแนวจอ (ผู้ใช้เปลี่ยนแผน 2026-09-24: ไม่ซ้อนสองจอแล้ว ให้โชว์ใหญ่เต็มที่) ----------
   ‼️ กล่องจอสูงคงที่ = ความสูงจอแนวตั้ง (โหมดที่สูงที่สุด) ทั้งสองโหมด จอแนวนอนอยู่กลางกล่อง
      กดสลับแล้วปุ่มสลับ/ป้ายใต้จอ/ข้อความฝั่งซ้ายต้องไม่ขยับเลย (ผู้ใช้สั่ง 2026-09-25)
   ‼️ ความสูงคิดจากความกว้างชุดทีวีด้วย cqw (figure เป็น container) - % ใช้กับ height ไม่ได้
      --tv-portrait-w = ความกว้าง "จอภาพ" ของแนวตั้ง / ขอบจอ 1.1cqw ต่อด้าน (padding 1.1% ของ .home-tv-body)
      ใหญ่สุดเท่าที่ความสูงหน้าจอให้: หักแถบเมนู + ระยะบน-ล่าง hero + ปุ่มสลับ + ป้ายใต้จอ ราว 330px */
body.page-home .home-tv {
    container-type: inline-size;
    --tv-portrait-w: min(54cqw, calc((100svh - 330px) * 0.5625));
}

body.page-home .home-tv-stack {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: calc(var(--tv-portrait-w) * 16 / 9 + 2.2cqw);
    transform: rotateY(-14deg) rotateX(3deg);
    transform-style: preserve-3d;
}

body.page-home .home-tv-body-portrait {
    width: calc(var(--tv-portrait-w) + 2.2cqw);
}

/* จอมือถือ: หน้าจอสูงพอ ให้จอแนวตั้งใหญ่ขึ้น */
@media (max-width: 767.98px) {
    body.page-home .home-tv {
        --tv-portrait-w: min(60cqw, calc((100svh - 200px) * 0.5625));
    }
}

body.page-home .home-tv[data-tv-mode="landscape"] .home-tv-body-portrait,
body.page-home .home-tv[data-tv-mode="portrait"] .home-tv-body-landscape {
    display: none;
}

/* จอที่เพิ่งสลับมาค่อย ๆ ขึ้น (ใช้ translate แยกจาก transform - ไม่ทับมุมเอียงของตัวจอ) */
body.page-home .home-tv-body {
    animation: home-tv-in 0.5s var(--omc-ease);
}

@keyframes home-tv-in {
    from {
        opacity: 0;
        translate: 0 var(--omc-sp-200);
    }
}

body.page-home .home-tv-screen-portrait {
    aspect-ratio: 9 / 16;
}

body.page-home .home-tv-frame-portrait {
    width: 720px;
    height: 1280px;
}

body.page-home .home-tv-switch {
    display: inline-flex;
    gap: var(--omc-sp-50);
    margin-top: var(--omc-sp-400);
    padding: var(--omc-sp-50);
    border: 1px solid color-mix(in srgb, var(--omc-on-ink) 14%, transparent);
    border-radius: 999px;
    /* ‼️ พื้นทึบ ไม่ใช่ขาวโปร่ง - ดวงแสงของฉากหลัง (canvas) ลอยผ่านหลังปุ่มแล้วโผล่ทะลุเป็นฝ้าเบลอ (ผู้ใช้เจอ 2026-09-25) */
    background: color-mix(in srgb, var(--home-hero-bg) 92%, var(--omc-on-ink));
    isolation: isolate;
}

body.page-home .home-tv-switch[hidden] {
    display: none;
}

body.page-home .home-tv-switch-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--omc-sp-75);
    padding: var(--omc-sp-75) var(--omc-sp-200);
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--home-hero-muted);
    font: inherit;
    font-size: var(--omc-fs-sm);
    line-height: var(--omc-lh-sm);
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.2s var(--omc-ease), color 0.2s var(--omc-ease);
}

body.page-home .home-tv-switch-btn svg {
    width: 18px;
    height: 18px;
}

body.page-home .home-tv-switch-btn:hover {
    color: var(--omc-on-ink);
}

body.page-home .home-tv-switch-btn[aria-pressed="true"] {
    background: var(--omc-brand);
    color: var(--omc-ink);
}

body.page-home .home-tv-switch-btn:focus-visible {
    outline: 2px solid var(--omc-brand);
    outline-offset: 2px;
}

@media (max-width: 1024px) {
    body.page-home .home-tv-stack {
        transform: rotateY(-8deg) rotateX(2deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    body.page-home .home-tv-body {
        animation: none;
    }
}
/* ---------- จอแคบ ---------- */
@media (max-width: 1024px) {
    body.page-home .home-hero {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--omc-sp-600);
        padding: var(--omc-sp-600) var(--omc-sp-300);
    }

    body.page-home .home-features,
    body.page-home .home-flow {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    body.page-home .home-feature-wide {
        grid-column: span 2;
    }

    body.page-home .home-faq-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--omc-sp-400);
    }

    body.page-home .home-contact {
        grid-template-columns: minmax(0, 1fr);
    }

    /* ‼️ ไม่มี min-height แล้ว - อัตราส่วน 16/10 คุมความสูงเองทุกขนาดจอ ถ้าใส่ min-height คู่กันไว้
       บนจอแคบ min-height จะชนะแล้วแผนที่ยืดเป็นสี่เหลี่ยมจัตุรัสผิดสัดส่วน (วัดได้ตอนจอ 375px) */

    body.page-home .home-footer-top {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 768px) {
    body.page-home .home-page {
        padding: var(--omc-sp-800) var(--omc-gutter) var(--omc-sp-600);
    }


    body.page-home .home-section,
    body.page-home .home-tagline-section {
        margin-top: var(--omc-sp-700);
    }

    body.page-home .home-tagline-section {
        padding: var(--omc-sp-600) 0;
    }

    body.page-home .home-proof,
    body.page-home .home-compare,
    body.page-home .home-features,
    body.page-home .home-flow,
    body.page-home .home-layers {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--omc-sp-200);
    }

    body.page-home .home-proof {
        gap: var(--omc-sp-400);
    }

    body.page-home .home-feature-wide {
        grid-column: span 1;
    }

    body.page-home .home-priceband {
        padding: var(--omc-sp-400) var(--omc-sp-300);
    }

    body.page-home .home-final-inner {
        padding: var(--omc-sp-600) var(--omc-sp-200);
    }

    body.page-home .home-pagehead {
        padding: var(--omc-sp-700) var(--omc-sp-200) var(--omc-sp-800);
    }

    body.page-home .home-price-table th,
    body.page-home .home-price-table td {
        padding: var(--omc-sp-100);
    }

    body.page-home .home-footer-top {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--omc-sp-400);
    }
}

/* ---------- เคารพการตั้งค่าลดการเคลื่อนไหวของเครื่องผู้ใช้ ----------
   ไม่ได้ปิดทั้งหมด - คงการจางเข้าที่ช่วยให้รู้ว่าเนื้อหามาใหม่ ตัดเฉพาะการเลื่อนตำแหน่งกับเบลอ */
@media (prefers-reduced-motion: reduce) {
    html:has(body.page-home) {
        scroll-behavior: auto;
    }

    html.has-js body.page-home .home-reveal {
        transform: none;
        filter: none;
        transition: opacity 200ms ease;
    }

    body.page-home .home-menu-item {
        transform: none;
    }

    body.page-home .home-compare-card:hover,
    body.page-home .home-feature:hover,
    body.page-home .home-flow-item:hover,
    body.page-home .home-layer:hover {
        transform: none;
    }

    body.page-home .home-burger-line,
    body.page-home .home-faq-caret,
    body.page-home .home-tagline-word {
        transition-duration: 150ms;
    }
}
/* ======================================================
   แดชบอร์ดสาขา - หน้าสรุปเชิงจัดการ
   spec: docs/superpowers/specs/2026-09-02-branch-dashboard-redesign-design.md

   ‼️ แท่งกราฟไล่สีตามความหนาแน่นของคิวแบบไฟจราจร (ผู้ใช้สั่งไว้ 2026-09-02 - "เหมือน Google Maps
      ที่รถติด") คิวน้อยมาก/ไม่มี = น้ำเงินอ่อน ไล่ผ่านเขียวอ่อน-เหลือง-ส้ม จนคิวแน่นสุด = แดง
      เป็น "สเกลเรียงลำดับ" ไม่ใช่สีแยกประเภท

   ‼️ สีพื้นของแท่งใช้โทนสว่างสดโดยตั้งใจ (ผู้ใช้สั่ง - รอบก่อนใช้โทนเข้มแล้วโดนตีกลับว่า
      "เหลืองเป็นน้ำตาล ส้มเหมือนแดง") ผลคือสีพื้นเองมีคอนทราสต์กับพื้นขาวต่ำกว่า 3:1
      **จึงต้องมีเส้นขอบแท่ง (--bdash-load-N-edge) เสมอ** - เป็นสีเดียวกันแต่เข้มขึ้นหนึ่งขั้น
      และทุกเส้นขอบผ่าน 3:1 (3.68 / 3.30 / 2.94 / 3.56 / 4.83) ทำให้ "ขอบเขตของแท่ง" ยังมองเห็นชัด
      ตามเจตนาของ WCAG 1.4.11 แม้ตัวพื้นจะอ่อน  ‼️ ห้ามถอด borderColor/borderWidth ออกจากกราฟแท่ง
      เพราะจะเหลือแค่พื้นอ่อน ๆ ที่ขอบละลายไปกับการ์ดสีขาว
      ‼️ โหมดมืดสีพื้นผ่าน 3:1 อยู่แล้ว (5.75 / 8.40 / 9.55 / 6.46 / 5.29) เส้นขอบจึงตั้งให้เท่ากับสีพื้น
         = มองไม่เห็นเส้นขอบ ถ้าใส่เส้นเข้มบนพื้นมืดจะอ่านเป็นร่องดำรอบแท่งแทน

   ‼️ ลำดับของสเกลนี้อ่านจาก "สี" (ความรู้เดิมเรื่องไฟจราจร) ไม่ใช่จาก "ความสว่าง" - เขียวกับเหลือง
      สว่างเกือบเท่ากัน คนตาบอดสีแดง-เขียวจึงแยกสองขั้นแรกไม่ออก สีอย่างเดียวเลยไม่พอตามกฎ
      accessibility ต้องมีตัวเลขกำกับทุกแท่ง (bdashValueLabels) + คำอธิบายสี (.bdash-legend)
      + ตารางข้อมูล ควบคู่เสมอ ‼️ ห้ามถอดอย่างใดอย่างหนึ่งออก
   ‼️ --bdash-series (น้ำเงิน) ยังใช้กับกราฟเส้นเวลารอ ซึ่งเป็นข้อมูลชุดเดียวไม่มีระดับความหนาแน่น
      ‼️ ห้ามใช้ --bs-primary (#0f766e) แทน เพราะความอิ่มสี 0.086 ต่ำกว่าเกณฑ์ 0.1 = อ่านเหมือนสีเทา
   ====================================================== */
body.page-admin {
    --bdash-series: #2563eb;
    --bdash-load-1: #60a5fa;
    --bdash-load-2: #4ade80;
    --bdash-load-3: #facc15;
    --bdash-load-4: #fb923c;
    --bdash-load-5: #f87171;
    --bdash-load-1-edge: #3b82f6;
    --bdash-load-2-edge: #16a34a;
    --bdash-load-3-edge: #ca8a04;
    --bdash-load-4-edge: #ea580c;
    --bdash-load-5-edge: #dc2626;
    /* พื้นใต้เส้นกราฟ - โปร่งใส ไม่ใช่สีทึบ เพราะต้องมองทะลุเห็นเส้นกริดกับเส้นประอ้างอิงข้างหลัง */
    --bdash-series-fill: rgba(37, 99, 235, 0.12);
    --bdash-track: #f1f4f6;
    /* ‼️ เส้นกริดในกราฟจางกว่าเส้นขอบการ์ด (--nc-border) หนึ่งขั้น - เป็นเส้นอ้างอิงสายตา
       ไม่ใช่เส้นแบ่งโครงสร้าง ถ้าเข้มเท่ากันจะแย่งสายตาไปจากแท่งกับเส้นข้อมูล
       ‼️ ต้องเป็นค่าสีตรง ๆ ไม่ใช่ color-mix เพราะ Chart.js เอาไปวาดบน canvas ซึ่งอ่านค่าที่คำนวณแล้วเท่านั้น */
    --bdash-grid: #eef0f4;
}

html[data-bs-theme="dark"] body.page-admin {
    --bdash-grid: #2a3648;
    --bdash-series: #3b82f6;
    /* สีพื้นชุดเดียวกับโหมดสว่าง - บนพื้นมืดชุดนี้ผ่าน 3:1 ด้วยตัวเองอยู่แล้ว
       ‼️ เส้นขอบจึงตั้งเท่ากับสีพื้น (= ไม่มีเส้นขอบให้เห็น) ถ้าใส่สีเข้มจะกลายเป็นร่องดำรอบแท่ง */
    --bdash-load-1: #60a5fa;
    --bdash-load-2: #4ade80;
    --bdash-load-3: #facc15;
    --bdash-load-4: #fb923c;
    --bdash-load-5: #f87171;
    --bdash-load-1-edge: #60a5fa;
    --bdash-load-2-edge: #4ade80;
    --bdash-load-3-edge: #facc15;
    --bdash-load-4-edge: #fb923c;
    --bdash-load-5-edge: #f87171;
    --bdash-series-fill: rgba(59, 130, 246, 0.18);
    --bdash-track: #2a3648;
}

/* ‼️ กรอบครอบทั้งแดชบอร์ด - ผู้ใช้สั่งให้ "คงกรอบไว้แต่ทำให้ไม่เห็น"
   จึงเหลือเป็นตัวห่อสำหรับจัดกลุ่มอย่างเดียว ไม่มีเส้นขอบ ไม่มีพื้น ไม่มีเงา
   ‼️ ห้ามลบ div นี้ทิ้ง - โครงหน้ายังอิงอยู่ และถ้าอยากให้กรอบกลับมาเห็นก็แก้ที่นี่จุดเดียว */
body.page-admin .bdash-frame {
    /* ‼️ กรอบนี้กินพื้นที่ "ขนาดเท่าเดิมกับตอนที่ยังเห็นกรอบ" ตามที่ผู้ใช้กำหนด
       คือเต็มความกว้างของพื้นที่เนื้อหาและมีระยะในเท่าเดิม ต่างกันแค่มองไม่เห็นว่ามีอะไรคุมอยู่
       (ไม่มีเส้นขอบ ไม่มีพื้น ไม่มีเงา) - ห้ามใส่ max-width กลับมา เคยลองแล้วผู้ใช้ให้เอาออก */
    padding: 1.25rem 1.35rem 1.5rem;
    border: 0;
    background: transparent;
    box-shadow: none;
}

/* ‼️ หัวเรื่อง + วันที่ของช่วงที่เลือก - ตอนนี้ไม่มีปุ่มช่วงเวลาอยู่ข้างในแล้ว (ย้ายไป .bdash-filters)
   จึงเหลือลูกใบเดียว flex/space-between ยังคงไว้เผื่อวันหน้ามีของมาวางขวา และไม่ทำให้อะไรเสีย
   ‼️ margin-bottom เล็กกว่าระยะระหว่างบล็อก (0.9rem) โดยเจตนา - หัวเรื่องกับแถวตัวกรองใต้มัน
      เป็นของกลุ่มเดียวกัน (บอกว่า "กำลังดูช่วงไหน" คู่กับ "เลือกช่วง") ต้องอ่านเป็นก้อนเดียว
      ถ้าเว้น 1.5rem เท่าบล็อกอื่น ตัวกรองจะดูลอยไม่สังกัดหัวเรื่อง */
body.page-admin .bdash-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 0.9rem;
}

body.page-admin .bdash-title {
    margin: 0;
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--nc-text);
}

body.page-admin .bdash-org {
    font-weight: 600;
    color: var(--nc-text-muted);
}

body.page-admin .bdash-subtitle {
    margin: 0.25rem 0 0;
    font-size: 0.85rem;
    color: var(--nc-text-muted);
}

/* ตัวเลือกช่วงเป็นลิงก์ (ช่วงอยู่ใน URL) แต่หน้าตาเป็นปุ่มกลุ่มเดียวกัน */
body.page-admin .bdash-range {
    display: inline-flex;
    padding: 0.25rem;
    border: 1px solid var(--nc-border);
    border-radius: 10px;
    background: var(--nc-column-bg);
}

/* แถวตัวกรองของแดชบอร์ด - ‼️ ใช้ร่วมกันทั้งหน้าสาขาและหน้าองค์กร
     หน้าสาขา  : มีแค่ปุ่มช่วงเวลา -> ชิดขวาสุด
     หน้าองค์กร: ตัวสลับสาขา (ซ้าย) + ปุ่มช่วงเวลา (ขวาสุด)
   ‼️ ปุ่มช่วงเวลาถูกย้ายลงมาจาก .bdash-head ตามที่ผู้ใช้สั่ง (2026-09-03) เดิมอยู่มุมขวาบน
      ติดหัวเรื่อง ซึ่งผู้ใช้มองไม่เห็นจนไม่รู้ว่ากรองช่วงเวลาได้
      ‼️ ห้ามย้ายกลับขึ้นไปใน .bdash-head โดยไม่ถามผู้ใช้ก่อน
   ‼️ ตัวแถวกินความกว้างเต็ม 100% แต่ .bdash-range ข้างในเป็น inline-flex จึงกว้างเท่าเนื้อหาของมันเอง
      -> ขอบซ้าย/ขอบขวาของปุ่มตรงกับบล็อกอื่นทั้งหน้าพอดี
   ‼️ margin-bottom เท่ากับ .bdash-head / .bdash-tiles (1.5rem) ให้ระยะระหว่างบล็อกเท่ากันทั้งหน้า
   ‼️ align-items: flex-start - จอแคบที่ตัวสลับสาขาหักหลายบรรทัด ปุ่มช่วงเวลาต้องไม่ยืดตามความสูง */
body.page-admin .bdash-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.75rem 1rem;
    margin-bottom: 1.5rem;
}

/* ‼️ ปุ่มช่วงเวลาต้องชิดขวาสุดเสมอ (ผู้ใช้สั่ง) - ใช้ margin-left: auto ไม่พึ่ง justify-content เพียว ๆ
   เพราะสองกรณีนี้ space-between เอาไม่อยู่:
     - หน้าสาขามีลูกใบเดียว -> space-between วางไว้ชิดซ้าย
     - จอแคบที่ตัวสลับสาขาหักลงบรรทัดใหม่ -> ปุ่มช่วงเวลาขึ้นบรรทัดของตัวเองแล้วชิดซ้าย
   ‼️ ใช้ :last-child ไม่ใช่ :not(.orgdash-scope) เพื่อให้กฎเดียวใช้ได้ทั้งสองหน้า
      (ตัวสลับสาขาอยู่ก่อนปุ่มช่วงเวลาเสมอ ถ้าสลับลำดับในมาร์กอัป ต้องกลับมาทบทวนกฎนี้) */
body.page-admin .bdash-filters > .bdash-range:last-child {
    margin-left: auto;
}

body.page-admin .bdash-range-btn {
    padding: 0.35rem 0.9rem;
    border-radius: 8px;
    color: var(--nc-text-muted);
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
    transition: background .15s ease, color .15s ease;
}

body.page-admin .bdash-range-btn:hover {
    color: var(--nc-text);
}

body.page-admin .bdash-range-btn.active {
    background: var(--nc-surface);
    color: var(--nc-text);
    box-shadow: 0 1px 2px rgba(11, 31, 29, 0.08);
}

/* ---------- ตัวเลขหลัก 4 ใบ ---------- */
/* ‼️ auto-fit ไม่ใช่จำนวนคอลัมน์ตายตัว - เพิ่มหรือลด tile แล้วไม่ต้องมาแก้ CSS ตาม
   และไม่มีใบไหนตกไปอยู่แถวล่างเดียวดายเวลาจำนวนใบหารกับคอลัมน์ไม่ลงตัว */
/* ตัวเลขสรุป 6 ใบ (ทั้งหมด/สำเร็จ/ยังไม่ปิดจบ/ถูกข้าม/เวลารอเฉลี่ย/เรียกซ้ำ)
   ‼️ กำหนดจำนวนคอลัมน์ตายตัวตามความกว้าง ไม่ใช้ auto-fit เพราะ 6 ใบกับ auto-fit ทำให้เหลือใบเดียว
      ห้อยอยู่แถวล่าง (5+1) ซึ่งดูเหมือนหน้าพัง - 6 หารด้วย 2 และ 3 ลงตัว จึงไม่มีใบไหนโดดเดี่ยว
      ‼️ ถ้าเพิ่ม/ลดจำนวนใบ ต้องกลับมาทบทวนตัวเลขคอลัมน์ที่นี่ด้วย
   ‼️ 2 คอลัมน์ (3 แถว) ใช้ลงไปถึงจอเล็กสุด ไม่มีโหมดคอลัมน์เดียวอีกแล้ว - ผู้ใช้สั่งไว้ (2026-09-03)
      เพราะคอลัมน์เดียวทำให้ต้องเลื่อนหน้ายาวมากกว่าจะเห็นตัวเลขครบทั้ง 6 ใบ ทั้งที่แต่ละใบมีข้อความ
      ไม่กี่คำ ห้ามใส่ media query บีบกลับเป็นคอลัมน์เดียวโดยไม่ถามผู้ใช้ก่อน
      (ถ้าเนื้อในใบเบียดตอนจอแคบ ให้ไปย่อ .bdash-tile เอง เช่นลดขนาดตัวเลข/ระยะใน ไม่ใช่ลดคอลัมน์) */
body.page-admin .bdash-tiles {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
    margin-bottom: 1.5rem;
}

@media (min-width: 768px) {
    body.page-admin .bdash-tiles {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

@media (min-width: 1400px) {
    body.page-admin .bdash-tiles {
        grid-template-columns: repeat(6, minmax(0, 1fr));
    }
}

body.page-admin .bdash-tile {
    padding: 1rem 1.1rem;
    border: 1px solid var(--nc-border);
    border-radius: 12px;
    background: var(--nc-surface);
}

body.page-admin .bdash-tile-label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    color: var(--nc-text-muted);
}

/* ‼️ สีตรงนี้บอก "ชนิดของตัวเลข" ไม่ใช่ "ดีหรือแย่" - สีดี/แย่เป็นของลูกศรใต้ตัวเลข
   ลงสีที่ไอคอนอย่างเดียว ไม่ระบายทั้งใบ ไม่งั้นจะชนกับสีลูกศรจนสื่อความหมายขัดกันเอง
   ‼️ ทุกใบมีไอคอนกับป้ายข้อความกำกับเสมอ สีเป็นตัวช่วยอ่านเร็ว ไม่ใช่ตัวสื่อความหมายเดี่ยว ๆ */
body.page-admin .bdash-tile-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 7px;
    font-size: 0.8rem;
}

body.page-admin .bdash-tile-icon.is-brand {
    background: var(--bs-primary-soft);
    color: var(--bs-primary);
}

body.page-admin .bdash-tile-icon.is-success {
    background: var(--nc-success-soft);
    color: var(--nc-success);
}

body.page-admin .bdash-tile-icon.is-info {
    background: var(--nc-info-soft);
    color: var(--nc-info);
}

body.page-admin .bdash-tile-icon.is-warning {
    background: var(--nc-warning-soft);
    color: var(--nc-warning);
}

body.page-admin .bdash-tile-icon.is-danger {
    background: var(--nc-danger-soft);
    color: var(--nc-danger);
}

body.page-admin .bdash-tile-value {
    margin: 0.5rem 0 0.35rem;
    font-size: 1.9rem;
    font-weight: 700;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
    color: var(--nc-text);
}

body.page-admin .bdash-tile-unit {
    margin-left: 0.25rem;
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--nc-text-muted);
}

body.page-admin .bdash-tile-none {
    font-size: 1rem;
    font-weight: 500;
    color: var(--nc-text-faint);
}

/* ‼️ สีผูกกับ "ความหมาย" ไม่ใช่ทิศของลูกศร - เวลารอที่ลดลงคือข่าวดี ต้องเป็นสีเขียวทั้งที่ลูกศรชี้ลง
   และทุกใบมีคำกำกับ (ดีขึ้น/แย่ลง) เสมอ ห้ามสื่อด้วยสีอย่างเดียว */
/* ‼️ flex-wrap + nowrap ในแต่ละ span = ยอมให้ตัดบรรทัดได้ "ระหว่างตัวเลขกับคำ" อย่างเดียว
      ตอนใบแคบ (2 คอลัมน์บนมือถือ) ถ้าไม่ทำ จะได้อย่างใดอย่างหนึ่งที่แย่ทั้งคู่:
        - ปล่อยเป็นข้อความก้อนเดียว -> เบราว์เซอร์ตัดคำไทยกลางคำ ("แย่" / "ลง" คนละบรรทัด)
        - สั่ง nowrap ทั้งวรรค      -> ข้อความล้นออกนอกขอบการ์ดที่จอ 320px (วัดเจอจริง)
   ‼️ ไอคอนลูกศรอยู่นอก span โดยเจตนา - มันสั้นและต้องเกาะกับตัวเลขเสมอ ถ้าห่อรวมกับตัวเลข
      จะเสียระยะ gap ระหว่างลูกศรกับตัวเลขไป */
body.page-admin .bdash-delta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin: 0;
    font-size: 0.85rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

body.page-admin .bdash-delta > span {
    white-space: nowrap;
}

body.page-admin .bdash-delta.is-better {
    color: var(--nc-success);
}

body.page-admin .bdash-delta.is-worse {
    color: var(--nc-danger);
}

body.page-admin .bdash-delta.is-neutral {
    color: var(--nc-text-faint);
    font-weight: 500;
}

body.page-admin .bdash-compare {
    color: var(--nc-text-faint);
}

/* ‼️ ช่วงวันที่ในหัวเรื่องต้องเป็นก้อนเดียวตัดบรรทัดไม่ได้ - ตอนจอแคบเบราว์เซอร์จะตัดบรรทัดที่ขีดกลาง
      ของวันที่ ("2026-" ท้ายบรรทัด / "08-31" บรรทัดถัดไป) ซึ่งอ่านเป็นตัวเลขคนละชุด (เจอจริงที่ 613px)
   ‼️ วันที่ฝังอยู่ในข้อความแปล (bdash.range_hint / bdash.compare_range) จะห่อเฉพาะตัวเลขไม่ได้
      จึงกัน "ทั้งวรรค" ไม่ให้ตัด แล้วให้ทั้งวรรคย้ายลงบรรทัดใหม่พร้อมกันเมื่อที่ไม่พอ
   ‼️ ต้องเป็น inline-block ไม่ใช่ inline - inline ที่ nowrap ยังถูกตัดตรงช่องว่างระหว่างคำในวรรคเดียวกันได้
   ‼️ ห้ามตั้งชื่อคลาสนี้ว่า bdash-range-* - ตระกูลนั้นเป็นของ "ปุ่มเลือกช่วงเวลา" (bdash-range /
      -btn / -toggle) และเทสต์ข้อ "หัวเรื่องไม่มีปุ่มช่วงเวลาเหลืออยู่" ตรวจด้วยการค้นสตริง
      'bdash-range' ในบล็อกหัวเรื่อง ตั้งชื่อชนตระกูลแล้วเทสต์จะฟ้องผิดทันที (เคยเจอจริง) */
body.page-admin .bdash-dates,
body.page-admin .bdash-compare {
    display: inline-block;
    white-space: nowrap;
}

/* ‼️ เป็น dropdown ตามที่ผู้ใช้กำหนด ไม่ใช่แผงที่เด้งลงมาดันเนื้อหาทั้งหน้าให้เลื่อนลง */
body.page-admin .bdash-range .dropdown {
    display: inline-flex;
}

body.page-admin .bdash-range-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    border: 0;
    cursor: pointer;
    font-family: inherit;
}

body.page-admin .bdash-range-toggle i {
    font-size: 0.7rem;
}

body.page-admin .bdash-custom.dropdown-menu {
    min-width: 240px;
    padding: 0.9rem;
    border: 1px solid var(--nc-border);
    border-radius: 12px;
    background: var(--nc-surface);
    box-shadow: 0 8px 24px rgba(11, 31, 29, 0.12);
}

body.page-admin .bdash-custom .bdash-custom-field + .bdash-custom-field {
    margin-top: 0.6rem;
}

body.page-admin .bdash-custom .btn {
    margin-top: 0.9rem;
}

body.page-admin .bdash-custom-field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

body.page-admin .bdash-custom-field label {
    font-size: 0.78rem;
    color: var(--nc-text-muted);
}

body.page-admin .bdash-custom-field input {
    padding: 0.35rem 0.6rem;
    border: 1px solid var(--nc-border);
    border-radius: 8px;
    background: var(--nc-column-bg);
    color: var(--nc-text);
    font: inherit;
    font-size: 0.9rem;
}

body.page-admin .bdash-custom-note {
    margin: 0.6rem 0 0;
    font-size: 0.75rem;
    color: var(--nc-text-faint);
}

/* ---------- กราฟ ---------- */
body.page-admin .bdash-charts {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
}

body.page-admin .bdash-chart {
    padding: 1rem 1.1rem 1.1rem;
    border: 1px solid var(--nc-border);
    border-radius: 12px;
    background: var(--nc-surface);
}

body.page-admin .bdash-chart-wide {
    grid-column: 1 / -1;
}

body.page-admin .bdash-chart-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 0.75rem;
}

body.page-admin .bdash-chart-title {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--nc-text);
}

body.page-admin .bdash-chart-hint {
    margin: 0.2rem 0 0;
    font-size: 0.8rem;
    color: var(--nc-text-muted);
}

body.page-admin .bdash-table-toggle {
    flex: none;
    padding: 0.25rem 0.6rem;
    border: 1px solid var(--nc-border);
    border-radius: 8px;
    background: transparent;
    color: var(--nc-text-muted);
    font-size: 0.78rem;
    font-weight: 600;
    cursor: pointer;
    transition: background .15s ease, color .15s ease;
}

body.page-admin .bdash-table-toggle:hover {
    background: var(--nc-column-bg);
    color: var(--nc-text);
}

/* ความสูงคงที่ เพราะ Chart.js ต้องการกล่องที่มีขนาดแน่นอน ไม่งั้นกราฟจะยืดไม่หยุดตอน resize */
body.page-admin .bdash-canvas-wrap {
    position: relative;
    height: 240px;
}

body.page-admin .bdash-chart-wide .bdash-canvas-wrap {
    height: 260px;
}

/* คำอธิบายสเกลสีของแท่ง - ‼️ จำเป็น ไม่ใช่ของประดับ เพราะสีบอกระดับความหนาแน่น
   ถ้าไม่มีคำอธิบาย สีจะกลายเป็นแค่สีสวย ๆ ที่ไม่มีใครรู้ว่าแปลว่าอะไร */
body.page-admin .bdash-legend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.85rem;
    margin-top: 0.65rem;
    font-size: 0.75rem;
    color: var(--nc-text-muted);
}

body.page-admin .bdash-legend-title {
    font-weight: 600;
}

body.page-admin .bdash-legend-item {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

/* ‼️ ต้องมีเส้นขอบเหมือนแท่งในกราฟเป๊ะ ๆ ไม่งั้นตัวอย่างสีจะดูคนละสีกับของจริง */
body.page-admin .bdash-legend-swatch {
    width: 12px;
    height: 12px;
    border: 1px solid var(--bdash-load-1-edge);
    border-radius: 3px;
    background: var(--bdash-load-1);
}

body.page-admin .bdash-legend-swatch.is-load-2 { border-color: var(--bdash-load-2-edge); background: var(--bdash-load-2); }
body.page-admin .bdash-legend-swatch.is-load-3 { border-color: var(--bdash-load-3-edge); background: var(--bdash-load-3); }
body.page-admin .bdash-legend-swatch.is-load-4 { border-color: var(--bdash-load-4-edge); background: var(--bdash-load-4); }
body.page-admin .bdash-legend-swatch.is-load-5 { border-color: var(--bdash-load-5-edge); background: var(--bdash-load-5); }

body.page-admin .bdash-table {
    margin-top: 0.75rem;
    max-height: 260px;
    overflow-y: auto;
}

/* ---------- ข้อมูลไม่พอ / ยังไม่มีข้อมูล ----------
   ‼️ แสดงข้อความแทนกราฟเปล่า - กราฟจากข้อมูลสองวันตีความผิดได้ง่ายกว่าไม่มีกราฟเลย */
body.page-admin .bdash-empty {
    padding: 2.5rem 1.5rem;
    border: 1px dashed var(--nc-border);
    border-radius: 12px;
    background: var(--nc-surface);
    text-align: center;
}

body.page-admin .bdash-empty i {
    font-size: 1.75rem;
    color: var(--nc-text-faint);
}

body.page-admin .bdash-empty-title {
    margin: 0.6rem 0 0.25rem;
    font-size: 1rem;
    font-weight: 600;
    color: var(--nc-text);
}

body.page-admin .bdash-empty-text {
    margin: 0;
    font-size: 0.85rem;
    color: var(--nc-text-muted);
}

@media (max-width: 991.98px) {
    body.page-admin .bdash-charts {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ‼️ จอแคบมาก: ป้ายกับตัวเลขโควตาแย่งบรรทัดเดียวกันไม่ไหว - ที่ 390px ป้าย "ใช้ไปแล้วในรอบบิลนี้" ตัด
      เป็นสองบรรทัด และตัวเลขก็ตัด "คิว" ลงบรรทัดใหม่พร้อมกัน อ่านเป็นสี่ก้อนที่ไม่รู้ว่าอันไหนคู่กับอันไหน
      จึงเลิกจัดซ้าย-ขวาแล้ววางป้ายไว้เหนือตัวเลขแทน (ตัวเลขได้ความกว้างเต็มการ์ด ไม่ต้องตัดบรรทัด)
   ‼️ 575.98px = ขอบ sm ของ Bootstrap ไม่ใช่เลขที่ตั้งเอง - จุดที่การ์ดแคบเกินจะวางของสองอย่าง
      ข้างกันได้ ใช้ค่าเดียวกับ media query อื่นของหน้านี้ (เช่นชื่อผู้ใช้บนแถบบนที่ซ่อนที่ขอบเดียวกัน) */
@media (max-width: 575.98px) {
    body.page-admin .orgdash-quota-label {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.15rem;
    }

    body.page-admin .orgdash-quota-value {
        white-space: nowrap;
    }
}

/* ‼️ ตัวเลข 6 ใบเป็น 2 คอลัมน์ตลอดแม้จอแคบ (ผู้ใช้สั่ง) ที่ 390px แต่ละใบเหลือกว้าง ~134px จึงต้อง
      ย่อ "เนื้อใน" ใบให้พอดี - นี่คือทางที่ถูกเวลาใบเบียด ไม่ใช่ลดจำนวนคอลัมน์กลับเป็นใบเดียว
   ‼️ ลดทั้ง 4 ค่าพร้อมกันโดยเจตนา (ระยะใน + ช่องไฟกริด + ขนาดตัวเลข + ขนาดป้าย) ลดอย่างเดียวไม่พอ
      กับที่ที่ได้คืนมา - ก่อนย่อ ป้าย "คิวที่ยังไม่ปิดจบ" ตัดเป็นสองบรรทัดและคำว่า "แย่ลง" ถูกตัดกลางคำ
   ‼️ ตัวเลขยังต้องเป็นของที่เด่นที่สุดในใบ - 1.6rem ยังใหญ่กว่าป้าย (0.78rem) เกินสองเท่า
      ห้ามลดต่ำกว่านี้เพื่อเอาที่ว่าง ไม่งั้นใบจะไม่มีจุดสายตาและอ่านเป็นข้อความยาว ๆ ก้อนเดียว */
@media (max-width: 575.98px) {
    body.page-admin .bdash-tiles {
        gap: 0.7rem;
    }

    body.page-admin .bdash-tile {
        padding: 0.8rem 0.85rem;
    }

    body.page-admin .bdash-tile-label {
        gap: 0.3rem;
        font-size: 0.78rem;
    }

    body.page-admin .bdash-tile-value {
        margin-top: 0.4rem;
        font-size: 1.6rem;
    }
}

/* ======================================================
   แดชบอร์ดองค์กร - ส่วนที่หน้าสาขาไม่มี (ตารางเปรียบเทียบสาขา + แถบเตือน + แถบข้อมูลอ้างอิง)
   spec: docs/superpowers/specs/2026-09-02-organization-dashboard-design.md

   ‼️ ตัวเลข 6 ใบ กราฟ ปุ่มช่วงเวลา และคำอธิบายสี ใช้ class .bdash-* ร่วมกับหน้าสาขาทั้งหมด
      ที่นี่มีแต่ของที่หน้าสาขาไม่มีจริง ๆ ห้ามก็อป .bdash-* มาทำซ้ำเป็น .orgdash-*
   ‼️ ทุกค่าอ้างอิง token เดิม ห้ามใส่ค่าสี/ระยะห่างดิบ (CLAUDE.md ข้อ 7)
   ====================================================== */

/* ‼️ class นี้ไม่ได้คุมระยะห่างอีกแล้ว (.bdash-filters คุมให้) เหลือหน้าที่เดียวคือ "ยอมให้หักบรรทัด"
   ชื่อสาขายาวไม่เท่ากันและมีได้ถึง 6 ปุ่ม จอแคบต้องหักลงบรรทัดใหม่ได้ ไม่ใช่ดันปุ่มช่วงเวลาตกขอบ
   ‼️ ห้ามลบ class ออกจากมาร์กอัป - tests/organization-dashboard-test.php ยืนยันว่ามีอยู่ */
body.page-admin .orgdash-scope {
    flex-wrap: wrap;
    max-width: 100%;
}

/* ‼️ ตารางเปรียบเทียบสาขาเป็น "เนื้อหาหลัก" แสดงตลอด ต่างจาก .bdash-table ที่เป็นตารางสำรองของกราฟ
   ซึ่งซ่อนไว้ให้กดเปิด - คนละหน้าที่ ห้ามใช้ class ร่วมกัน */
/* ‼️ ระยะรอบตารางเปรียบเทียบ - ตอนนี้อยู่ "ระหว่าง" แถบเตือนสาขารอนานสุด กับกราฟ (ผู้ใช้จัดลำดับใหม่)
   จึงต้องมีระยะทั้งบนและล่าง 1.5rem เท่ากับระยะระหว่างบล็อกอื่นทั้งหน้า (.bdash-tiles / .bdash-filters)
   เดิมมีแต่ margin-top: 1rem เพราะเคยอยู่ท้ายสุดต่อจากกราฟ ไม่มีอะไรตามหลัง - พอย้ายมาอยู่กลาง
   การ์ดกราฟใต้มันจึงมาชิดจนดูเหมือนเป็นการ์ดเดียวกัน (ผู้ใช้ทักมา) */
body.page-admin .orgdash-compare {
    margin-top: 1.5rem;
    margin-bottom: 1.5rem;
    padding: 1rem 1.1rem 1.1rem;
    border: 1px solid var(--nc-border);
    border-radius: 12px;
    background: var(--nc-surface);
}

body.page-admin .orgdash-compare-scroll {
    max-height: 420px;
    overflow-x: auto;
    overflow-y: auto;
}

/* ‼️ min-width คือสิ่งที่ทำให้ overflow-x ด้านบนทำงานจริงบนจอแคบ - ถ้าไม่มี ตารางจะ "บีบ" ตัวเอง
   จนข้อความหักบรรทัดคำต่อคำและคอลัมน์สุดท้ายถูกตัดหาย (เจอจริงตอนถ่ายหน้าจอที่ 390px)
   ‼️ เซลล์ตัวเลขและหัวคอลัมน์ห้ามหักบรรทัด ปล่อยให้หักได้แค่ชื่อสาขาซึ่งยาวไม่เท่ากัน */
body.page-admin .orgdash-compare table {
    min-width: 36rem;
    font-variant-numeric: tabular-nums;
}

body.page-admin .orgdash-compare thead th,
body.page-admin .orgdash-compare td {
    white-space: nowrap;
}

/* หัวคอลัมน์ - จางกว่าข้อมูล เพราะเป็นป้ายกำกับ ไม่ใช่เนื้อหาที่ต้องอ่าน */
body.page-admin .orgdash-compare thead th {
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--nc-text-muted);
}

/* ชื่อสาขาในตาราง - เป็น "ตัวระบุแถว" ที่กดเข้าไปดูสาขานั้นได้
   ‼️ ห้ามปล่อยเป็นลิงก์ default (น้ำเงินขีดเส้นใต้) - ในตารางที่มีหลายแถวจะกลายเป็นเส้นน้ำเงินพาดกัน
      เต็มคอลัมน์แล้วแย่งสายตาจากตัวเลขซึ่งเป็นเนื้อหาจริงของตาราง (60-30-10 rule)
   ‼️ ยังต้องรู้ว่ากดได้: ขีดเส้นใต้แบบจาง ๆ ไว้ตลอดด้วย text-decoration-color แล้วเข้มขึ้นตอน hover
      ไม่ได้ถอดสัญญาณว่ากดได้ออกไปเฉย ๆ */
body.page-admin .orgdash-compare tbody th a {
    color: var(--nc-text);
    font-weight: 600;
    text-decoration: underline;
    text-decoration-color: color-mix(in srgb, var(--nc-text-muted) 45%, transparent);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    transition: color .15s ease, text-decoration-color .15s ease;
}

body.page-admin .orgdash-compare tbody th a:hover,
body.page-admin .orgdash-compare tbody th a:focus-visible {
    color: var(--bs-primary);
    text-decoration-color: currentColor;
}

/* แถวสลับโทนอ่อน ๆ - ตารางเปรียบเทียบมีหลายคอลัมน์ตัวเลข ต้องกวาดตาตามแถวได้ไม่หลุด */
body.page-admin .orgdash-compare tbody tr:hover > * {
    background: var(--nc-column-bg);
}

/* แถบเล็กในเซลล์ เทียบสัดส่วนกับสาขาที่มากสุด - ใช้รางเดียวกับกราฟแท่งแนวนอน */
body.page-admin .orgdash-bar {
    display: block;
    height: 6px;
    margin-top: 0.25rem;
    border-radius: 3px;
    background: var(--bdash-track);
}

body.page-admin .orgdash-bar > span {
    display: block;
    height: 100%;
    border-radius: 3px;
    background: var(--bdash-load-3);
}

/* ‼️ สีผูกกับ "ความหมาย" เหมือน .bdash-delta และมาคู่กับลูกศรเสมอ ห้ามสื่อด้วยสีอย่างเดียว */
body.page-admin .orgdash-trend {
    font-weight: 600;
}

body.page-admin .orgdash-trend.is-better {
    color: var(--nc-success);
}

body.page-admin .orgdash-trend.is-worse {
    color: var(--nc-danger);
}

body.page-admin .orgdash-trend.is-neutral {
    color: var(--nc-text-faint);
    font-weight: 500;
}

/* ‼️ ป้ายแย่สุดต้องมีคำกำกับเสมอ ห้ามสื่อความหมายด้วยสีอย่างเดียว (คนตาบอดสีต้องอ่านออก) */
body.page-admin .orgdash-worst-badge {
    display: inline-block;
    margin-left: 0.35rem;
    padding: 0.1rem 0.4rem;
    border: 1px solid var(--bdash-load-5-edge);
    border-radius: 4px;
    background: var(--bdash-load-5);
    color: var(--nc-text);
    font-size: 0.7rem;
    font-weight: 600;
    white-space: nowrap;
}

/* ‼️ แถบเตือนสาขาที่รอนานสุด - เป็นตัวกันค่าเฉลี่ยถ่วงน้ำหนักกลบสาขาที่มีปัญหา
   (aggregate masking) ห้ามถอดออกโดยไม่ทบทวนวิธีเฉลี่ยใหม่ทั้งหมด - ดู spec ข้อ 4.1 */
/* ‼️ margin-bottom เป็น 0 เพราะบล็อกที่ตามหลัง (.orgdash-compare) มี margin-top ของตัวเองแล้ว
   ถ้าใส่ทั้งสองฝั่ง margin จะรวมกันเป็นช่องว่างกว้างผิดจังหวะกับที่อื่นในหน้า */
body.page-admin .orgdash-alert {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 0.9rem;
    border: 1px solid var(--bdash-load-4-edge);
    border-radius: 10px;
    background: var(--nc-surface);
    color: var(--nc-text);
    font-size: 0.875rem;
    text-decoration: none;
}

body.page-admin .orgdash-alert:hover {
    border-color: var(--bdash-load-5-edge);
}

/* ‼️ การ์ดโครงสร้างองค์กร - ตัวเลขอ้างอิง (สาขา/บัญชี/โมดูล) จึงเป็นตัวเลขขนาดกลาง ไม่ใช่ใบใหญ่
   แบบ .bdash-tiles ด้านบนที่เป็นตัวเลขคิวซึ่งต้องดูทุกวัน - ลำดับความสำคัญต่างกัน ห้ามทำให้เท่ากัน
   ‼️ ใช้เป็นกริดร่วมของทั้ง "แพ็กเกจ|กระเป๋าเงิน" (ใส่ .is-pair) และ "โครงสร้างองค์กร" (ใบเดียว)
      ค่าตั้งต้นคือคอลัมน์เดียวเต็มความกว้าง การวางสองใบข้างกันเป็นหน้าที่ของ .is-pair เท่านั้น */
/* ‼️ margin-top 1.5rem = ค่าเดียวกับระยะระหว่างบล็อกอื่นทั้งหน้า (.bdash-tiles / .bdash-filters /
   .orgdash-compare) การ์ดโครงสร้างท้ายหน้าจึงมีจังหวะเท่ากับที่เหลือ ไม่ชิดกราฟกว่าที่อื่น */
body.page-admin .orgdash-cards {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1rem;
    margin-top: 1.5rem;
}

/* ‼️ กริดคู่ (แพ็กเกจ|กระเป๋าเงิน) อยู่ใต้หัวเรื่อง "สรุปภาพรวมองค์กร" และเหนือแถวตัวกรอง
   ‼️ margin-top 1.5rem = จังหวะมาตรฐานระหว่างบล็อกของหน้านี้ - ค่านี้ยุบรวม (collapse) กับ
      margin-bottom 0.9rem ของ .bdash-head ที่อยู่ติดกันข้างบน เหลือระยะจริง 24px ตามจังหวะหน้า
      (ห้ามไปลด margin-bottom ของ .bdash-head เพื่อจัดระยะที่นี่ - .bdash-head ใช้ร่วมกับหน้าสาขา
       ซึ่งหัวเรื่องยังต่อด้วยแถวตัวกรองทันที ระยะ 0.9rem ที่นั้นถูกต้องอยู่แล้ว)
   ‼️ margin-bottom 2rem ไม่ใช่ 1.5rem โดยเจตนา - เป็นรอยต่อระหว่าง "เรื่องเงิน" (ล็อกกับรอบบิล)
      กับ "เรื่องคิว" (เปลี่ยนตามปุ่มช่วงเวลา) ซึ่งเป็นสองเรื่องคนละเรื่องบนหน้าเดียว ระยะต้องกว้าง
      กว่าระยะระหว่างบล็อกภายในเรื่องเดียวกัน ไม่งั้นแถวตัวกรองจะดูเหมือนตัวกรองของการ์ดด้านบน */
body.page-admin .orgdash-cards.is-pair {
    margin-top: 1.5rem;
    margin-bottom: 2rem;
}

/* ‼️ สองการ์ดในกริดคู่ต้องสูงเท่ากัน (ผู้ใช้สั่ง 2026-09-03) จึงไม่มี align-items: start ในกริด
      ปล่อยให้ stretch ตามค่าตั้งต้น ใบที่เนื้อหาน้อยกว่าจะยืดตามใบที่สูงกว่าเอง
   ‼️ ความสูงเท่ากันเฉย ๆ ยังไม่พอ - ถ้าไม่ทำอะไรต่อ ใบที่เตี้ยกว่าจะมีที่ว่างค้างท้ายการ์ดเฉย ๆ
      ดูเหมือนเนื้อหาหาย จึงทำการ์ดเป็นคอลัมน์ flex แล้วดันบล็อกล่างสุดลงไปชนก้นการ์ด
      ผลคือบรรทัดล่างสุดของทั้งสองใบอยู่ระดับเดียวกัน (สาขาที่ใช้โควตามากสุด | ความเคลื่อนไหวล่าสุด)
   ‼️ ที่ว่างไปโป่ง "เหนือ" บล็อกล่างสุด ไม่ใช่ใต้มัน - บล็อกล่างสุดของทั้งสองใบเป็นข้อมูลประกอบที่มี
      เส้นคั่นของตัวเองอยู่แล้ว การถ่างระยะเหนือเส้นคั่นจึงอ่านเป็นการเว้นวรรค ไม่ใช่การ์ดพัง */
body.page-admin .orgdash-cards.is-pair > .orgdash-card {
    display: flex;
    flex-direction: column;
}

/* ‼️ ระบุบล็อกที่ "ยอมให้ดันลงล่าง" ตรง ๆ ห้ามใช้ :last-child ลอย ๆ - ลูกใบสุดท้ายของการ์ดไม่ได้เป็น
      บล็อกท้ายเสมอ (กรณียังไม่มีแพ็กเกจ การ์ดซ้ายมีแต่ <header> ถ้าโดนดันลงล่างหัวการ์ดจะลอยไปก้นการ์ด)
   ‼️ .orgdash-account-foot ในการ์ดกระเป๋าเงิน (แถวเตือนเงินไม่พอ) ตั้งใจไม่ให้ auto - ถ้าใส่ทั้งสองที่
      flex จะหารที่ว่างให้ทั้งคู่ กลายเป็นช่องโหว่สองรู จึงให้ .orgdash-wallet กินที่ว่างคนเดียว */
body.page-admin .orgdash-cards.is-pair > .orgdash-account > .orgdash-account-foot,
body.page-admin .orgdash-cards.is-pair > .orgdash-wallet-card > .orgdash-wallet {
    margin-top: auto;
}

/* ‼️ ระยะเหนือเส้นคั่นย้ายมาเป็น margin-bottom ของพี่ที่อยู่ติดกันข้างบน เพราะบนใบที่สูงกว่าไม่มีที่ว่าง
      เหลือให้กิน margin-top: auto จะเหลือ 0 - ถ้าฝากระยะไว้ที่ margin-top ใบที่สูงกว่าจะมีเนื้อหาชิดเส้น
   ‼️ ใช้ :has(+ ...) เลือก "พี่ที่อยู่ก่อนบล็อกล่างสุด" เท่านั้น ห้ามใส่ระยะให้ทุกบล็อก - .orgdash-wallet-note
      ต้องเกาะติด .orgdash-wallet-figures ที่อยู่เหนือมัน (0.2rem) ถ้าใส่หมดจะถ่างออกจากกันผิดจังหวะ */
body.page-admin .orgdash-cards.is-pair > .orgdash-account > :has(+ .orgdash-account-foot),
body.page-admin .orgdash-cards.is-pair > .orgdash-wallet-card > :has(+ .orgdash-wallet) {
    margin-bottom: 0.9rem;
}

body.page-admin .orgdash-card {
    padding: 1rem 1.1rem 1.1rem;
    border: 1px solid var(--nc-border);
    border-radius: 12px;
    background: var(--nc-surface);
}

body.page-admin .orgdash-card-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.35rem 0.75rem;
    margin-bottom: 0.9rem;
}

body.page-admin .orgdash-card-title {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--nc-text);
}

body.page-admin .orgdash-card-hint {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--nc-text-muted);
}

/* ป้ายหมวดเหนือหัวเรื่องของการ์ด (เช่น "แพ็กเกจ" เหนือชื่อแพ็กเกจที่มาจากฐานข้อมูล)
   ‼️ ต่างจาก .orgdash-card-hint ที่เป็นคำอธิบาย "ใต้" หัวเรื่อง - อันนี้เป็นชื่อหมวดที่มาก่อน
      จึงต้องเล็กและจางกว่าหัวเรื่องชัดเจน ไม่ใช่แข่งกับมัน
   ‼️ ไม่ใช้ text-transform: uppercase เพราะป้ายเป็นภาษาไทยด้วย (uppercase ไม่มีผลกับไทยแต่จะทำให้
      สองภาษาดูไม่เท่ากันเวลาสลับ TH/EN) */
body.page-admin .orgdash-card-eyebrow {
    margin: 0 0 0.1rem;
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: var(--nc-text-muted);
}

body.page-admin .orgdash-card-links {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 0.5rem;
    margin-left: auto;
    font-size: 0.8125rem;
}

/* ลิงก์บนหัวการ์ด (ดูรายละเอียด / จัดการสาขา ฯลฯ)
   ‼️ ยืมหน้าตาของ .bdash-table-toggle มาทั้งชุด เพราะเป็น "ปุ่มรองบนหัวการ์ด" หน้าที่เดียวกันเป๊ะ
      สองอย่างนี้อยู่บนหน้าเดียวกัน ถ้าหน้าตาไม่เหมือนกันจะดูเหมือนคนละระบบ
   ‼️ เอาขีดเส้นใต้ของลิงก์ออกโดยตั้งใจ แต่ยังบอกว่ากดได้ด้วย "กรอบ + cursor + สีเปลี่ยนตอน hover"
      ไม่ได้บอกด้วยสีอย่างเดียว (ข้อกำหนดเรื่อง contrast/accessibility) */
body.page-admin .orgdash-card-links a {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.25rem 0.6rem;
    border: 1px solid var(--nc-border);
    border-radius: 8px;
    background: transparent;
    color: var(--nc-text-muted);
    font-size: 0.78rem;
    font-weight: 600;
    text-decoration: none;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}

body.page-admin .orgdash-card-links a::after {
    /* ‼️ ลูกศรเป็น ::after ไม่ใช่ไอคอน bootstrap-icons ในมาร์กอัป - ลิงก์พวกนี้อยู่หลายที่
       ใส่ที่ CSS จุดเดียวแล้วได้ทั้งหมด และ screen reader ไม่ต้องอ่านลูกศรเป็นข้อความ */
    content: '›';
    font-size: 1.05em;
    line-height: 1;
    opacity: 0.6;
}

body.page-admin .orgdash-card-links a:hover,
body.page-admin .orgdash-card-links a:focus-visible {
    border-color: var(--nc-text-muted);
    background: var(--nc-column-bg);
    color: var(--nc-text);
    text-decoration: none;
}

/* ตัวเลขโครงสร้าง - auto-fit เหมือน .bdash-tiles เพิ่ม/ลดตัวเลขแล้วไม่ต้องแก้ CSS ตาม
   ‼️ 96px ตั้งไว้ให้ตัวเลขทั้ง 5 ตัวลงได้ในแถวเดียวบนจอกว้าง - ถ้าตั้งกว้างกว่านี้จะตกเป็น 3 แถว
   แล้วการ์ดโครงสร้างสูงกว่าการ์ดบัญชีข้าง ๆ มาก (เคยเป็นแบบนั้น) ป้ายหักบรรทัดได้ ไม่เป็นไร */
body.page-admin .orgdash-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
    gap: 0.5rem;
    margin: 0;
}

body.page-admin .orgdash-stat {
    padding: 0.55rem 0.65rem;
    border-radius: 10px;
    background: var(--nc-column-bg);
}

body.page-admin .orgdash-stat dt {
    margin: 0;
    font-size: 0.75rem;
    font-weight: 400;
    color: var(--nc-text-muted);
}

body.page-admin .orgdash-stat dd {
    margin: 0.1rem 0 0;
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--nc-text);
    font-variant-numeric: tabular-nums;
}

/* ป้ายชื่อโมดูลที่เปิดใช้ - ชื่อมาจากฐานข้อมูล จึงเป็นป้ายกลาง ๆ ไม่ผูกความหมายกับสี */
body.page-admin .orgdash-modules {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin: 0.9rem 0 0;
    padding-top: 0.8rem;
    border-top: 1px solid var(--nc-border);
}

body.page-admin .orgdash-module-badge {
    padding: 0.15rem 0.55rem;
    border: 1px solid var(--nc-border);
    border-radius: 999px;
    background: var(--nc-column-bg);
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--nc-text);
}

/* ลิงก์ที่ดันไปชิดขวาสุดของแถวเตือนท้ายการ์ด */
/* ปุ่มลัดท้ายแถวเตือน (ไปหน้ากระเป๋าเงินตอนเงินไม่พอ)
   ‼️ เป็นปุ่มโทนเตือน ไม่ใช่ลิงก์จาง ๆ เหมือน .orgdash-card-links เพราะเป็นสิ่งที่ต้องกดจริง
      ไม่ใช่ทางเลือกให้ดูเพิ่ม - ใช้ token ชุดเดียวกับขอบการ์ดที่เตือน ไม่มีสีดิบใหม่ */
body.page-admin .orgdash-strip-cta {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.25rem 0.7rem;
    border: 1px solid var(--bdash-load-5-edge);
    border-radius: 8px;
    color: var(--nc-text);
    font-size: 0.78rem;
    font-weight: 600;
    text-decoration: none;
    transition: background .15s ease;
}

body.page-admin .orgdash-strip-cta::after {
    content: '›';
    font-size: 1.05em;
    line-height: 1;
    opacity: 0.7;
}

body.page-admin .orgdash-strip-cta:hover,
body.page-admin .orgdash-strip-cta:focus-visible {
    background: var(--nc-column-bg);
    color: var(--nc-text);
    text-decoration: none;
}

/* ‼️ เงินในกระเป๋าไม่พอ = เรื่องที่ต้องลงมือทำ ไม่ใช่ข้อมูลเฉย ๆ จึงเปลี่ยนขอบเป็นโทนเตือน
   ‼️ มาคู่กับข้อความเสมอ ห้ามใช้สีอย่างเดียวบอกว่ามีปัญหา (การ์ดมีทั้งข้อความ "ไม่พอสำหรับรอบนี้"
      ยอดที่ขาด และแถวเตือนท้ายการ์ด) */
/* ‼️ ตอนนี้อยู่บนการ์ดกระเป๋าเงิน (.orgdash-wallet-card) เพราะ "เงินไม่พอ" เป็นเรื่องของกระเป๋า
   ไม่ใช่ของแพ็กเกจ - คง .orgdash-account ไว้ในลิสต์ด้วยเผื่อการ์ดแพ็กเกจต้องเตือนอะไรในอนาคต */
body.page-admin .orgdash-account.is-warning,
body.page-admin .orgdash-wallet-card.is-warning {
    border-color: var(--bdash-load-5-edge);
}

/* ‼️ การ์ดสถานะบัญชี - เป็นเรื่องเงินที่ผู้ใช้ต้องลงมือทำ (คุมปริมาณก่อนขึ้นขั้นราคา / เติมเงิน)
   จึงเป็นการ์ดเต็ม เปลือกการ์ด (padding/ขอบ/พื้น) มาจาก .orgdash-card ที่ใช้ร่วมกับการ์ดโครงสร้าง
   ห้ามประกาศซ้ำที่นี่ ไม่งั้นสองการ์ดจะเริ่มต่างกันทีละนิดโดยไม่มีใครสังเกต
   ‼️ ตัวเลขทุกตัวมาจาก BillingService::preview() ที่เรียกอยู่แล้ว ยกเว้นความเคลื่อนไหวกระเป๋า
      ที่เป็น query เดียวที่ยิงเพิ่ม (ดู OrganizationDashboardService::accountStatus) */
body.page-admin .orgdash-account-period {
    font-size: 0.8125rem;
    color: var(--nc-text-muted);
    font-variant-numeric: tabular-nums;
}

/* สองคอลัมน์: ซ้าย = โควตา (สิ่งที่ต้องเฝ้า) ขวา = ตัวเลขเงิน (สิ่งที่ต้องจ่าย) */
body.page-admin .orgdash-account-body {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
    gap: 1rem 1.75rem;
}

body.page-admin .orgdash-quota-label {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
    margin: 0 0 0.35rem;
    font-size: 0.8125rem;
    color: var(--nc-text-muted);
}

body.page-admin .orgdash-quota-value {
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--nc-text);
    font-variant-numeric: tabular-nums;
}

/* รางเดียวกับกราฟแท่งแนวนอนและแถบในตารางเปรียบเทียบ - ผู้ใช้จะอ่านว่าเป็นสเกลเดียวกัน */
body.page-admin .orgdash-quota-bar {
    display: block;
    height: 10px;
    border-radius: 5px;
    background: var(--bdash-track);
    overflow: hidden;
}

body.page-admin .orgdash-quota-bar > span {
    display: block;
    height: 100%;
    border-radius: 5px;
    background: var(--bdash-load-2);
}

/* ‼️ ใกล้ขึ้นขั้นราคา = เปลี่ยนสีแถบตามสเกลความหนาแน่นชุดเดิม (เขียว -> เหลือง -> ส้ม)
   ‼️ มาคู่กับข้อความ "อีก N คิว" เสมอ ห้ามสื่อด้วยสีอย่างเดียว */
body.page-admin .orgdash-quota-bar.is-near > span {
    background: var(--bdash-load-3);
}

body.page-admin .orgdash-quota-bar.is-close > span {
    background: var(--bdash-load-4);
}

body.page-admin .orgdash-quota-note {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0.5rem 0 0;
    font-size: 0.8125rem;
    color: var(--nc-text-muted);
}

body.page-admin .orgdash-account-figures {
    display: grid;
    gap: 0.45rem;
    margin: 0;
    font-size: 0.8125rem;
}

body.page-admin .orgdash-account-figures > div {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.75rem;
}

/* ‼️ ป้ายห้ามหักบรรทัด - ป้ายสั้นกว่าค่าอยู่แล้ว ถ้าปล่อยให้หักจะได้ "ขั้นราคาที่ / ใช้" สองบรรทัด
   ทั้งที่มีที่เหลือ (เจอจริงตอนถ่ายหน้าจอ) ให้ค่าเป็นตัวที่หักบรรทัดแทนถ้าจำเป็น */
body.page-admin .orgdash-account-figures dt {
    flex: none;
    font-weight: 400;
    color: var(--nc-text-muted);
    white-space: nowrap;
}

body.page-admin .orgdash-account-figures dd {
    text-align: right;
}

body.page-admin .orgdash-account-figures dd {
    margin: 0;
    font-weight: 600;
    color: var(--nc-text);
    font-variant-numeric: tabular-nums;
}

/* ตัวเลขเงินสองช่องของการ์ดกระเป๋าเงิน: ยอดคงเหลือ | ตัดไปแล้วในรอบนี้
   ‼️ วางข้างกันโดยเจตนา เพราะสองเลขนี้ต้องอ่านคู่กัน ("มีเท่านี้ ตัดไปแล้วเท่านี้")
      ‼️ ไม่ใช่รูปแบบเดียวกับ .orgdash-account-figures ที่เป็น ป้าย-ค่า ในบรรทัดเดียว - อันนั้นเป็น
      ตัวเลขอ้างอิง อันนี้เป็นตัวเลขหลักของการ์ด จึงเป็นป้ายเล็กบนค่าตัวใหญ่
   ‼️ ไม่มีแพ็กเกจ = มีช่องเดียว (ไม่มีรอบบิลให้คิดยอดที่ตัด) กริดยืดให้เต็มเองด้วย 1fr */
body.page-admin .orgdash-wallet-figures {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
    gap: 0.75rem 1.25rem;
    margin: 0;
}

body.page-admin .orgdash-wallet-figures dt {
    margin-bottom: 0.15rem;
    font-size: 0.75rem;
    font-weight: 400;
    color: var(--nc-text-muted);
}

body.page-admin .orgdash-account-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.75rem;
    margin: 0.9rem 0 0;
    padding-top: 0.75rem;
    border-top: 1px solid var(--nc-border);
    font-size: 0.8125rem;
    color: var(--nc-text-muted);
}

body.page-admin .orgdash-account-foot strong {
    color: var(--nc-text);
}

/* ‼️ ส่วน "ความเคลื่อนไหวล่าสุด" ในการ์ดกระเป๋าเงิน - ล่าสุดเท่านั้น ไม่ใช่ ledger
   ledger เต็มอยู่ที่ /{slug}/wallet ซึ่งลิงก์ "ดูรายละเอียด" บนหัวการ์ดพาไปให้แล้ว
   ห้ามขยายส่วนนี้เป็นตารางเต็ม
   ‼️ คอลัมน์เดียว - ยอดเงินย้ายไปอยู่ .orgdash-wallet-figures ด้านบนแล้ว (การ์ดถูกแยกออกมาเป็น
      การ์ดของตัวเอง ไม่ได้ซ้อนอยู่ในการ์ดแพ็กเกจเหมือนเดิม) */
body.page-admin .orgdash-wallet {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.75rem 1.75rem;
    margin-top: 0.9rem;
    padding-top: 0.85rem;
    border-top: 1px solid var(--nc-border);
}

body.page-admin .orgdash-wallet-label {
    display: block;
    margin-bottom: 0.15rem;
    font-size: 0.75rem;
    color: var(--nc-text-muted);
}

body.page-admin .orgdash-wallet-balance {
    margin: 0;
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--nc-text);
    font-variant-numeric: tabular-nums;
}

body.page-admin .orgdash-wallet-note {
    margin: 0.2rem 0 0;
    font-size: 0.75rem;
    color: var(--nc-text-muted);
}

body.page-admin .orgdash-wallet-list {
    display: grid;
    gap: 0.3rem;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0.8125rem;
}

body.page-admin .orgdash-wallet-list li {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    /* ‼️ แถวเหลือสองชิ้น (วันที่ | จำนวนเงิน) จึงดันจำนวนเงินไปชิดขวาแทนการมีช่องกลางมาคั่น */
    justify-content: space-between;
}

body.page-admin .orgdash-wallet-when {
    color: var(--nc-text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ‼️ เงินเข้า/ออกมีเครื่องหมาย + / - นำหน้าเสมอ ห้ามบอกด้วยสีอย่างเดียว
   ‼️ ใช้ token เดียวกับลูกศรดีขึ้น/แย่ลงของตัวเลข 6 ใบ เพื่อไม่ให้ผู้ใช้ต้องจำสองระบบสี */
body.page-admin .orgdash-wallet-amount {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

body.page-admin .orgdash-wallet-amount.is-in {
    color: var(--nc-success);
}

body.page-admin .orgdash-wallet-amount.is-out {
    color: var(--nc-danger);
}

@media (min-width: 1200px) {
    /* จอกว้างพอ วางสองการ์ดข้างกัน - ‼️ เฉพาะ .is-pair (แพ็กเกจ|กระเป๋าเงิน) การ์ดใบเดียวต้องเต็มความกว้าง
       ‼️ ฝั่งแพ็กเกจกว้างกว่าเพราะมีแถบโควตา + ตัวเลขสามช่อง ส่วนกระเป๋าเงินมีแค่ยอดเงินกับรายการล่าสุด */
    body.page-admin .orgdash-cards.is-pair {
        grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
    }
}

@media (max-width: 767.98px) {
    body.page-admin .orgdash-account-body,
    body.page-admin .orgdash-wallet,
    body.page-admin .orgdash-wallet-figures {
        grid-template-columns: minmax(0, 1fr);
    }

    body.page-admin .orgdash-card-links {
        margin-left: 0;
    }
}

/* ---------- ตกแต่งแถบบน (Phase 19.2 รอบปรับดีไซน์) ----------
   โปร่งแสง + เบลอพื้นหลังเล็กน้อย ให้เนื้อหาที่เลื่อนผ่านใต้แถบยังรู้สึกต่อเนื่อง
   ไม่ใส่เงาหนา/สีเข้ม เพราะ sidebar เป็นตัวถือน้ำหนักภาพหลักอยู่แล้ว (ข้อ 2 visual hierarchy) */
body.page-admin .app-navbar {
    background: color-mix(in srgb, var(--nc-surface) 82%, transparent);
    backdrop-filter: blur(10px);
}

/* ปุ่มสลับภาษา TH/EN บนแถบบน - โครงเดียวกับเมนูผู้ใช้ข้าง ๆ (กดเปิดเมนูก่อนค่อยเลือก)
   ‼️ ไม่ได้ออกแบบแถบบนใหม่ - ใช้ขนาด/ความมน/สีชุดเดียวกับ .app-user-menu-toggle */
.app-lang-menu .app-lang-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    height: 36px;
    padding: 0 0.5rem;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--nc-text);
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
}

.app-lang-menu .app-lang-toggle:hover,
.app-lang-menu .app-lang-toggle[aria-expanded="true"] {
    background: var(--nc-column-bg);
}

.app-lang-menu .app-lang-toggle:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}

.app-lang-caret {
    font-size: 0.7rem;
    transition: transform .15s ease;
}

.app-lang-toggle[aria-expanded="true"] .app-lang-caret {
    transform: rotate(180deg);
}

.app-lang-list {
    min-width: 8rem;
    padding: 0.25rem;
    /* ‼️ ไม่งั้นภาษาที่เลือกอยู่เป็นพื้นน้ำเงิน #0d6efd ของ Bootstrap (หน้าที่ไม่มีธีมโซนทับ เช่นหน้า login) */
    --bs-dropdown-link-active-bg: var(--bs-primary);
}

/* ‼️ หน้าสาธารณะของ OMC ใช้จานสีของตัวเอง (--omc-*) - --bs-primary ตรงนี้ยังเป็น teal ของหลังบ้าน
   ภาษาที่เลือกอยู่จึงขึ้นพื้นเขียวบนหน้าส้ม ใช้ --omc-brand-deep (คู่ตัวอักษรขาว คอนทราสต์ 5.56) แทน */
body.page-home .app-lang-list {
    --bs-dropdown-link-active-bg: var(--omc-brand-deep);
}

.app-lang-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    border-radius: 8px;
    font-weight: 600;
}


/* ชื่อเมนูที่กำลังเปิดอยู่ ข้างปุ่ม ☰ - ตัวหนาพอให้รู้ว่าอยู่หน้าไหน แต่ไม่แย่งสายตาไปจากเนื้อหา */
body.page-admin .app-navbar-title {
    min-width: 0;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--nc-text);
}

/* ปุ่ม ☰ ไม่มีกรอบและไม่มีพื้นหลังตามที่ผู้ใช้กำหนด - เหลือแค่ไอคอนลอยบนแถบ ให้แถบบนดูโล่งเป็นระเบียบ
   ขนาด/ความมนเท่ากับ .icon-btn ฝั่งขวา (36px / 10px) เพื่อให้ไอคอนทั้งแถบอยู่ในจังหวะเดียวกัน
   พื้นหลังโผล่เฉพาะตอน hover/focus เท่านั้น (ยังต้องรู้ว่ากดได้ และ focus ต้องเห็นชัดสำหรับคีย์บอร์ด) */
body.page-admin .app-navbar-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 10px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--nc-text-muted);
    font-size: 1.15rem;
    cursor: pointer;
    transition: background .15s ease, color .15s ease;
}

body.page-admin .app-navbar-toggle:hover,
body.page-admin .app-navbar-toggle:focus-visible {
    background: var(--bs-primary-soft);
    color: var(--bs-primary);
}

/* ---------- สถานะ "ย่อเมนูข้าง" (กดปุ่ม ☰) ----------
   คลาสอยู่ที่ <html> เพราะถูกใส่คืนตั้งแต่ก่อนวาดหน้าโดย render_theme_init_script() (กัน sidebar กระพริบ)
   ตัวอักษรในเมนูเป็น text node เปล่า ๆ ไม่มี element ครอบ จึงซ่อนด้วย font-size: 0 ที่ตัวปุ่ม
   แล้วคืนขนาดให้เฉพาะ <i> ข้างใน - ข้อความจริงยังอยู่ครบสำหรับ screen reader และ title ที่ JS เติมให้ */
/* ‼️ ทั้งบล็อกนี้อยู่ใน @media (min-width: 768px) โดยเจตนา - สถานะ "ย่อเหลือไอคอน" เป็นเรื่องของ
   เมนูที่อยู่ในสายเลย์เอาต์ (จอกว้าง) เท่านั้น จอเล็กเมนูเป็นลิ้นชักที่เปิด-ปิด ไม่ใช่ย่อ-ขยาย
   ถ้าไม่กั้นไว้ คนที่ย่อเมนูค้างไว้บนจอกว้างแล้วเปิดหน้าเดิมบนมือถือ (คลาสถูกจำใน localStorage)
   จะได้ลิ้นชักที่มีแต่ไอคอนไม่มีชื่อเมนู และเมนูย่อยทุกกลุ่มถูกซ่อนหมด */
@media (min-width: 768px) {
    body.page-admin .admin-sidebar {
        transition: flex-basis .18s ease;
    }

    html.sidebar-collapsed body.page-admin .admin-sidebar {
        flex-basis: 76px;
    }

    html.sidebar-collapsed body.page-admin .admin-sidebar-brand {
        justify-content: center;
        padding-inline: 0.5rem;
    }

    html.sidebar-collapsed body.page-admin .admin-sidebar-brand .brand-stack {
        display: none;
    }

    html.sidebar-collapsed body.page-admin .admin-sidebar-brand-icon img {
        width: 44px;
        height: 44px;
    }

    html.sidebar-collapsed body.page-admin .admin-sidebar-nav {
        padding-inline: 0.5rem;
    }

    html.sidebar-collapsed body.page-admin .admin-sidebar-nav .nav-link {
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 0;
        padding-inline: 0;
    }

    html.sidebar-collapsed body.page-admin .admin-sidebar-nav .nav-link > i:first-child {
        width: auto;
        margin: 0 !important;
        font-size: 1.1rem;
    }

    html.sidebar-collapsed body.page-admin .admin-sidebar-group-chevron {
        display: none;
    }

    /* ‼️ ย่อ sidebar แล้วต้องเห็นเฉพาะ "เมนูหลัก" ตามที่ผู้ใช้กำหนด - เมนูย่อยของกลุ่มถูกซ่อนทั้งก้อน
       ไม่งั้นไอคอนของเมนูย่อยจะไหลมาเรียงต่อกับเมนูหลักจนแยกไม่ออกว่าอันไหนเป็นหัวข้ออะไร
       (หัวข้อกลุ่มยังเด่นอยู่ตอนมีลูก active เพราะ :has() ด้านบนดูจากโครง DOM ไม่ได้ดูว่ามองเห็นไหม)
       กดขยาย sidebar กลับมาเมื่อไหร่ เมนูย่อยก็กางให้เหมือนเดิมทันที */
    html.sidebar-collapsed body.page-admin .admin-sidebar-group {
        display: none;
    }

    /* ---------- ย่อเมนูแล้ว: เลขไปเกาะมุมขวาล่างของไอคอน ----------
       ‼️ ตอนย่อ แถวเมนูเหลือความกว้างแค่ไอคอน เลขที่เคยชิดขวาสุดด้วย margin-left:auto จะไปยืนข้างไอคอน
          ดันให้ไอคอนของสองเมนูนั้นไม่ตรงกลางเหมือนเมนูอื่น (ผู้ใช้แจ้ง 2026-09-16) จึงถอดออกจาก
          สายเลย์เอาต์มาวางทับเป็น overlay แทน ไอคอนทุกแถวจึงอยู่กึ่งกลางเท่ากันหมด
       ‼️ จุดอ้างอิงคือ "กึ่งกลางแถว" ไม่ใช่ขอบขวา เพราะ <i> กว้างตามตัวอักษร ไม่มีความกว้างตายตัวให้
          ยึดขอบขวาได้ - ขยับเล็กน้อยจากกึ่งกลางจึงตรงมุมไอคอนพอดีทุกไอคอน
       ‼️ วงแหวนสีพื้นรอบเลขคือสิ่งที่ทำให้อ่านออกตอนทับเส้นไอคอน ต้องเปลี่ยนตามพื้นของแถว
          (แถวธรรมดา = พื้น sidebar · แถวที่เปิดอยู่ = สีหลัก) ไม่งั้นจะเห็นเป็นวงสีผิดลอยอยู่ */
    html.sidebar-collapsed body.page-admin .admin-sidebar-nav .nav-link:has(.admin-sidebar-badge) {
        position: relative;
        justify-content: center;
    }

    html.sidebar-collapsed body.page-admin .admin-sidebar-badge {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(1px, 1px);
        margin-left: 0;
        min-width: 16px;
        height: 16px;
        padding: 0 4px;
        font-size: 0.62rem;
        box-shadow: 0 0 0 2px var(--nc-surface);
    }

    html.sidebar-collapsed body.page-admin .admin-sidebar-nav .nav-link.active .admin-sidebar-badge {
        box-shadow: 0 0 0 2px var(--bs-primary);
    }
}


@media (prefers-reduced-motion: reduce) {
    body.page-admin .admin-sidebar,
    body.page-admin .admin-sidebar-backdrop {
        transition: none;
    }
}

/* ‼️ คอลัมน์สวิตช์เปิด/ปิดโมดูลอยู่หน้าสุด - บีบให้แคบเท่าที่สวิตช์ต้องการ ไม่งั้นตารางจะเฉลี่ย
   ความกว้างให้เท่า ๆ กันแล้วดันชื่อโมดูลถอยไปไกลจากขอบซ้าย */
body.page-admin .org-module-switch-col {
    width: 1%;
    white-space: nowrap;
}

/* ---------- ศูนย์การเรียนรู้ (Knowledge) - Phase 25 ----------
   ‼️ ใช้ token ของระบบทั้งหมด (--nc-* / --space-* / --bs-primary) ไม่มีสีหรือระยะห่างดิบ
      จึงได้โหมดมืดและสีของแต่ละร้านมาเองโดยไม่ต้องเขียนกฎซ้ำ (CLAUDE.md ข้อ 7)
   ‼️ ความกว้างของเนื้อหาถูกจำกัดไว้ที่ 72ch โดยตั้งใจ - บรรทัดยาวเกินนี้สายตาหาบรรทัดถัดไปไม่เจอ
      ซึ่งเป็นเรื่องใหญ่สำหรับหน้าที่คนตั้งใจมา "อ่าน" ไม่ใช่มา "กด" */
/* ‼️ 2026-09-24 ผู้ใช้สั่ง: ครอบเนื้อหาทั้งหน้า (ทั้งหน้ารวมและหน้าบทความ ทุกโซน) ด้วยกรอบเดียว
      เดิมเนื้อหาลอยอยู่บนพื้นหลังเปล่า ๆ ดูไม่เป็นหน้าเดียวกับหน้าอื่นของระบบที่อยู่ในการ์ดทั้งหมด */
/* ‼️ กรอบเต็มความกว้างของพื้นที่เนื้อหา (ผู้ใช้กำหนด 2026-09-24 - เดิมจำกัด 960px แล้วอยู่กลางจอ) */
body.page-admin .kb-page {
    padding: var(--space-4);
    background: var(--nc-surface);
    border: 1px solid var(--nc-border);
    border-radius: 14px;
}

@media (min-width: 768px) {
    body.page-admin .kb-page {
        padding: var(--space-5);
    }
}

body.page-admin .kb-head {
    margin-bottom: var(--space-5);
}

body.page-admin .kb-title {
    margin: 0;
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--nc-text);
}

body.page-admin .kb-lead {
    margin: var(--space-2) 0 0;
    color: var(--nc-text-muted);
}

body.page-admin .kb-meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin: var(--space-3) 0 0;
    font-size: 0.8125rem;
    color: var(--nc-text-faint);
}

/* ---------- หมวดและการ์ดบทความ ---------- */
body.page-admin .kb-category {
    margin-bottom: var(--space-5);
}

body.page-admin .kb-category-title {
    margin: 0 0 var(--space-3);
    font-size: 0.8125rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--nc-text-faint);
}

/* ‼️ auto-fill ไม่ใช่ auto-fit - หมวดที่มีบทความเดียวจะได้การ์ดกว้างเท่าการ์ดในหมวดอื่น
   ไม่ใช่ยืดเต็มแถวจนดูเป็นคนละชนิดของ */
body.page-admin .kb-card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: var(--space-3);
}

body.page-admin .kb-card {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-4);
    border: 1px solid var(--nc-border);
    border-radius: 12px;
    background: var(--nc-surface);
    color: inherit;
    text-decoration: none;
    transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}

body.page-admin .kb-card:hover,
body.page-admin .kb-card:focus-visible {
    border-color: var(--bs-primary);
    box-shadow: 0 6px 18px rgba(var(--bs-primary-rgb), 0.10);
    transform: translateY(-1px);
}

body.page-admin .kb-card-title {
    font-weight: 700;
    color: var(--nc-text);
}

/* ไอคอนประจำบทความ (docs/learning/index.php คีย์ icon) - อยู่ในบรรทัดเดียวกับชื่อ ไม่ใช่กล่องไอคอนแยกเหนือหัวข้อ */
body.page-admin .kb-card-icon {
    margin-right: var(--space-2);
    color: var(--bs-primary);
    font-size: 1.05em;
}

body.page-admin .kb-card-text {
    grid-column: 1;
    font-size: 0.875rem;
    line-height: 1.6;
    color: var(--nc-text-muted);
}

body.page-admin .kb-card-go {
    grid-row: 1;
    grid-column: 2;
    color: var(--nc-text-faint);
    transition: color .15s ease, transform .15s ease;
}

body.page-admin .kb-card:hover .kb-card-go {
    color: var(--bs-primary);
    transform: translateX(2px);
}

/* ---------- สารบัญ ---------- */
body.page-admin .kb-toc {
    margin: 0 0 var(--space-5);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--nc-border);
    border-radius: 12px;
    background: var(--nc-column-bg);
}

body.page-admin .kb-toc-title {
    display: block;
    margin-bottom: var(--space-2);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--nc-text-faint);
}

body.page-admin .kb-toc-list {
    margin: 0;
    padding-left: var(--space-4);
    color: var(--nc-text-muted);
}

body.page-admin .kb-toc-list li + li {
    margin-top: var(--space-1);
}

/* ---------- เนื้อบทความ ---------- */
/* ‼️ 2026-09-24 ผู้ใช้สั่ง: เนื้อหาเต็มกรอบ (เดิมจำกัด 72ch แล้วเหลือที่ว่างด้านขวาของกรอบ) */
body.page-admin .kb-body {
    color: var(--nc-text);
    line-height: 1.8;
}

body.page-admin .kb-body .kb-h2 {
    margin: var(--space-6) 0 var(--space-3);
    padding-top: var(--space-4);
    border-top: 1px solid var(--nc-border);
    font-size: 1.15rem;
    font-weight: 700;
}

/* ‼️ หัวข้อแรกไม่ต้องมีเส้นคั่นและระยะห่างด้านบน - มันอยู่ติดใต้หัวบทความอยู่แล้ว */
body.page-admin .kb-body > .kb-h2:first-child {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}

body.page-admin .kb-body .kb-h3 {
    margin: var(--space-4) 0 var(--space-2);
    font-size: 1rem;
    font-weight: 700;
}

body.page-admin .kb-body p {
    margin: 0 0 var(--space-3);
}

body.page-admin .kb-body .kb-list {
    margin: 0 0 var(--space-3);
    padding-left: var(--space-4);
}

body.page-admin .kb-body .kb-list li + li {
    margin-top: var(--space-2);
}

/* ---------- ตารางในบทความ ----------
   ‼️ .kb-body ถูกจำกัดความกว้างไว้ที่ 72ch เพื่อให้อ่านง่าย ตารางจึงล้นได้เป็นปกติ
      ตัวครอบที่เลื่อนแนวนอนจึงจำเป็น ไม่ใช่ของประดับ - ไม่มีแล้วจอแคบจะบีบตารางจนคอลัมน์ท้ายหาย */
body.page-admin .kb-body .kb-table-wrap {
    margin: 0 0 var(--space-4);
    overflow-x: auto;
}

body.page-admin .kb-body .kb-table {
    width: 100%;
    border-collapse: collapse;
    /* ‼️ ต้องมี min-width ไม่งั้น overflow-x ด้านบนไม่ทำงานจริง ตารางจะบีบตัวเองจนหักบรรทัดคำต่อคำ */
    min-width: 32rem;
    font-size: 0.9375rem;
    line-height: 1.6;
}

body.page-admin .kb-body .kb-table th,
body.page-admin .kb-body .kb-table td {
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--nc-border);
    text-align: left;
    vertical-align: top;
}

/* หัวตารางใช้พื้นอ่อนกับเส้นล่างหนาขึ้น แทนการใช้สีเข้มทึบ - ให้เข้ากับทิศทาง corporate clean */
body.page-admin .kb-body .kb-table thead th {
    border-bottom: 2px solid var(--nc-border);
    background: var(--nc-column-bg);
    color: var(--nc-text);
    font-weight: 600;
    white-space: nowrap;
}

body.page-admin .kb-body .kb-table tbody tr:last-child td {
    border-bottom: 0;
}

body.page-admin .kb-body code {
    padding: 0.1em 0.35em;
    border-radius: 5px;
    background: var(--nc-column-bg);
    color: var(--bs-primary);
    font-size: 0.875em;
}

/* หมายเหตุ - เส้นหนาด้านซ้ายบอกว่าเป็นของแทรก ไม่ใช่เนื้อความปกติ */
body.page-admin .kb-body .kb-note,
body.page-admin .kb-note {
    margin: 0 0 var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-left: 3px solid var(--bs-primary);
    border-radius: 0 10px 10px 0;
    background: var(--bs-primary-soft);
    color: var(--nc-text);
}

body.page-admin .kb-note-warning {
    border-left-color: var(--nc-warning);
    background: var(--nc-warning-soft);
}

/* ---------- ภาพประกอบ ---------- */
body.page-admin .kb-figure {
    margin: 0 0 var(--space-4);
}

/* ‼️ 2026-09-24 - เดิม width: 100% ทำให้ภาพเล็ก/ภาพแคบถูก "ขยาย" เต็มความกว้างบทความ
      ภาพแถบเมนูข้าง (76x900) จึงกลายเป็นยาวหลายพันพิกเซล และภาพกล่องเล็ก ๆ ก็แตกเบลอ
      ตอนนี้ไม่ขยายเกินขนาดจริง (width: auto) และจำกัดความสูงไว้ ภาพจึงอยู่ในกรอบเสมอ */
body.page-admin .kb-figure img {
    display: block;
    width: auto;
    max-width: 100%;
    max-height: 480px;
    height: auto;
    margin-inline: auto;
    object-fit: contain;
    border: 1px solid var(--nc-border);
    border-radius: 10px;
    background: var(--nc-column-bg);
}

body.page-admin .kb-figure figcaption {
    margin-top: var(--space-2);
    text-align: center;
    font-size: 0.8125rem;
    color: var(--nc-text-faint);
}

/* ---------- หน้าคู่มือของโซนพนักงาน (ไม่มี sidebar) ---------- */
body.page-admin .kb-standalone {
    padding: var(--space-4);
}

@media (min-width: 768px) {
    body.page-admin .kb-standalone {
        padding: var(--space-5);
    }
}

/* ‼️ ผู้ใช้กำหนด 2026-09-24: หน้าคู่มือของ console (ไม่มี sidebar จอจึงกว้างมาก) ไม่ขยายเต็ม
      ใช้ความกว้างเดิม 960px และอยู่กลางจอ - โซนอื่นยังเต็มพื้นที่เนื้อหาเหมือนเดิม */
body.page-admin .kb-standalone .kb-page {
    max-width: 960px;
    margin-inline: auto;
}

@media (prefers-reduced-motion: reduce) {
    body.page-admin .kb-card,
    body.page-admin .kb-card-go {
        transition: none;
    }

    body.page-admin .kb-card:hover,
    body.page-admin .kb-card:focus-visible {
        transform: none;
    }

    body.page-admin .kb-card:hover .kb-card-go {
        transform: none;
    }
}
/* ---------- ลิงก์ "กลับไปรายการ..." ของหน้ารายละเอียด (partials/back-link.php) ----------
   ‼️ เดิมเป็น <a> เปล่า ๆ สีลิงก์ ดูไม่ออกว่าเป็นทางกลับหรือลิงก์ในเนื้อหา (ผู้ใช้แจ้ง 2026-09-16)
      ทำเป็นชิปขอบมนแทน - เห็นว่ากดได้ตั้งแต่ยังไม่ชี้เมาส์ แต่ไม่แย่งสายตาไปจากปุ่มหลักของหน้า
      จึงใช้พื้นการ์ดกับตัวอักษรสีรอง ไม่ใช่สีหลัก (DESIGN_GUIDELINE ข้อ 2 - หนึ่งหน้ามี CTA หลักเดียว)
   ‼️ ลูกศรขยับไปทางซ้ายตอนชี้เมาส์ ทิศเดียวกับสิ่งที่ปุ่มทำจริง */
body.page-admin .admin-back-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--nc-border);
    border-radius: 999px;
    background: var(--nc-surface);
    color: var(--nc-text-muted);
    font-size: 0.875rem;
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}

body.page-admin .admin-back-link i {
    font-size: 1rem;
    line-height: 1;
    transition: transform .15s ease;
}

body.page-admin .admin-back-link:hover,
body.page-admin .admin-back-link:focus-visible {
    border-color: var(--bs-primary);
    background: var(--bs-primary-soft);
    color: var(--bs-primary);
}

body.page-admin .admin-back-link:hover i,
body.page-admin .admin-back-link:focus-visible i {
    transform: translateX(-2px);
}

body.page-admin .admin-back-link:active {
    background: var(--nc-column-bg);
}

@media (prefers-reduced-motion: reduce) {
    body.page-admin .admin-back-link,
    body.page-admin .admin-back-link i {
        transition: none;
    }

    body.page-admin .admin-back-link:hover i,
    body.page-admin .admin-back-link:focus-visible i {
        transform: none;
    }
}

/* ---------- กล่องพรีวิวรูปในฟอร์มตั้งค่าแบรนด์ (partials/branding-card.php) ---------- */
body.page-admin .branding-preview {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 96px;
    padding: var(--space-3);
    border-radius: 12px;
    border: 1px dashed var(--nc-border);
    background: var(--nc-column-bg);
}

body.page-admin .branding-preview-wide {
    min-height: 120px;
}

body.page-admin .branding-preview img {
    max-width: 100%;
    max-height: 88px;
    object-fit: contain;
}

body.page-admin .branding-preview-empty {
    color: var(--nc-text-muted);
    font-size: 0.88rem;
}

/* ป้ายรหัสย่อธนาคาร (SCB/KKP/KBANK) ข้างชื่อธนาคารในตารางบัญชีรับเงิน /admin/payments
   ‼️ ไม่ใช้ .bg-secondary ของ Bootstrap เพราะเทาทึบแย่งสายตาจากชื่อธนาคารซึ่งเป็นข้อมูลหลัก
      ต้องการโทนอ่อนของสีธีม (--bs-primary-soft) ที่อ่านออกแต่ไม่ดังกว่าข้อความข้าง ๆ
   ‼️ tabular-nums + letter-spacing เพราะเป็นตัวย่อภาษาอังกฤษล้วน อ่านเป็นก้อนได้ง่ายกว่า */
.badge.payment-bank-code {
    background: var(--bs-primary-soft);
    color: var(--bs-primary-strong);
    padding: var(--space-1) var(--space-2);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    font-variant-numeric: tabular-nums;
    vertical-align: middle;
}

/* ---------- ฟอร์มแจ้งโอนเงินในหน้ากระเป๋าเงินขององค์กร (pages/organization/wallet.php) ----------
   ‼️ ช่องกรอกที่ยืดเต็มความกว้าง modal อ่านยากและดูไม่จบเป็นชุด - คุมความกว้างของ "ตัวฟอร์ม"
      ไว้ที่นี่จุดเดียว ไม่ใช่ไปใส่ style ในหน้า (CLAUDE.md ข้อ 7)
   ‼️ ใช้ max-width จึงยุบตามจอเล็กเองอยู่แล้ว - ที่ ~390px ฟอร์มกว้างเต็มคอลัมน์ตามปกติ ไม่ล้น */
/* ---------- หน้าอัปสลิปจากมือถือ (สาธารณะ หลังสแกน QR) ----------
   ‼️ ใช้ token ของธีมทั้งหมด ห้าม hardcode สี - หน้านี้ต้องดูเป็นส่วนหนึ่งของระบบ ไม่ใช่หน้าโล้น ๆ */
.topup-upload-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: var(--bs-primary-soft);
    color: var(--bs-primary-strong);
    font-size: 1.75rem;
}

.topup-upload-icon-success {
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
}

.topup-upload-icon-warning {
    background: var(--bs-warning-bg-subtle);
    color: var(--bs-warning-text-emphasis);
}

/* พื้นที่กดเลือกรูป - ทำให้ใหญ่พอกดด้วยนิ้วโป้งบนมือถือได้สบาย */
.topup-upload-drop {
    display: block;
    padding: var(--space-4) var(--space-3);
    border: 1px dashed var(--bs-border-color);
    border-radius: var(--bs-border-radius-lg);
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    cursor: pointer;
    transition: border-color 0.15s ease-in-out, background-color 0.15s ease-in-out;
}

.topup-upload-drop:hover,
.topup-upload-drop:focus-within {
    border-color: var(--bs-primary);
    background: var(--bs-primary-soft);
}

.topup-upload-preview {
    margin-top: var(--space-3);
    text-align: center;
}

.topup-upload-preview img {
    max-width: 100%;
    max-height: 14rem;
    border-radius: var(--bs-border-radius);
}

/* พรีวิวสลิปในฟอร์มเติมเงินฝั่งคอม - ต้องเห็นรูปจริงก่อนกดยืนยัน ไม่ใช่แค่ข้อความว่าแนบแล้ว
   ‼️ ทับ max-height ของ .branding-preview img ที่เล็กเกินจะดูสลิปออก */
/* กล่องยืนยันกลาง (แทน window.confirm) - วงกลมไอคอนใช้โทนเดียวกับ .topup-upload-icon ของหน้ามือถือ
   ‼️ ต้องเด่นพอที่ผู้ใช้จะ "หยุดอ่าน" ก่อนกด (ผู้ใช้กำหนด 2026-09-15) - กล่องนี้ขวางหน้างานที่
      ย้อนไม่ได้ทั้งหมดของระบบ ทั้งลบข้อมูล ตัดเงิน และอนุมัติเติมเงิน */
.app-confirm-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 5rem;
    height: 5rem;
    border-radius: 50%;
    background: var(--bs-primary-bg-subtle);
    color: var(--bs-primary);
    font-size: 2.5rem;
    line-height: 1;
    /* วงแหวนจาง ๆ รอบนอก - ให้วงกลมดูลอยขึ้นมาจากพื้นการ์ดโดยไม่ต้องใช้เงาหนา (DESIGN_GUIDELINE ข้อ 0) */
    box-shadow: 0 0 0 0.6rem color-mix(in srgb, var(--bs-primary) 8%, transparent);
}

.app-confirm-icon-danger {
    background: var(--bs-danger-bg-subtle);
    color: var(--bs-danger);
    box-shadow: 0 0 0 0.6rem color-mix(in srgb, var(--bs-danger) 8%, transparent);
}

.app-confirm-title {
    font-size: 1.25rem;
    font-weight: 600;
}

/* ‼️ ไม่มีเส้นคั่นเหนือปุ่ม (ผู้ใช้กำหนด 2026-09-15) - กล่องนี้มีข้อความเดียวกับปุ่มเดียว
   เส้นคั่นของ Bootstrap เลยแบ่งสิ่งที่ไม่ได้แยกกันอยู่แล้ว ทำให้กล่องดูหนักโดยไม่จำเป็น */
.app-confirm .modal-footer {
    justify-content: center;
    gap: var(--space-3);
    border-top: 0;
    padding-block: 0 var(--space-5);
}

/* ‼️ กันไม่ให้กล่องแคบจนข้อความไทยตัดบรรทัดกลางคำ แต่ยังไม่กว้างเกินจอมือถือ */
.app-confirm #app-confirm-message {
    max-width: 26rem;
    margin-inline: auto;
}

/* ปุ่มใหญ่พอกดพลาดยาก และกว้างเท่ากันทั้งคู่ - ทางเลือกสองทางต้องดูมีน้ำหนักเท่ากัน */
.app-confirm .modal-footer .btn {
    min-width: 9rem;
    padding-block: var(--space-2);
    font-size: 1rem;
    font-weight: 500;
}

/* ช่องกรอกยอดจริงบนหน้า /admin/topups - กว้างพอสำหรับตัวเลขเงินเท่านั้น ปล่อยเต็มความกว้าง
   แล้วปุ่มอนุมัติ/ปฏิเสธจะถูกดันตกบรรทัดจนคอลัมน์สูงผิดรูป */
.topup-amount-input {
    max-width: 8rem;
}

/* กล่องดูภาพ (QR บัญชีรับเงิน / สลิปของลูกค้า) - ภาพสูงได้ไม่เกินความสูงหน้าจอที่มองเห็น */
.image-preview-body img {
    max-width: 100%;
    max-height: 70vh;
    border-radius: var(--bs-border-radius);
}

.topup-slip-preview {
    text-align: center;
}

.topup-slip-preview img {
    max-width: 100%;
    max-height: 16rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
}

body.page-admin .topup-form-body {
    max-width: 30rem;
    margin-inline: auto;
}

/* QR ต้องใหญ่พอสแกนจากกล้องมือถือได้จริง (~160px ขึ้นไป) แต่ไม่ต้องกินความกว้างทั้ง modal
   ‼️ ทับ max-height ของ .branding-preview img ที่ตั้งไว้ 88px ซึ่งเล็กเกินจะสแกนได้ */
body.page-admin .topup-qr-box {
    max-width: 11rem;
    margin-inline: auto;
    padding: var(--space-2);
}

body.page-admin .topup-qr-box img {
    max-height: 9.5rem;
}

/* ชื่อไฟล์สลิปที่เลือกไว้ - ชื่อจากมือถือยาวมากจนดันความกว้างฟอร์มได้ ต้องตัดด้วย ... เสมอ */
body.page-admin .topup-slip-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ‼️ กล่องครอบตัดรูป (app/Views/partials/image-crop-modal.php) ใช้ร่วมทุกโซน จึงไม่ผูกกับ
   body.page-admin - หน้า settings ของสาขา/องค์กร/OMC ใช้กล่องเดียวกันนี้
   ‼️ ความสูงคุมที่นี่ ไม่ใช่ inline style ในหน้า (ของเดิมเป็น style="max-height:400px" ฝังในหน้าสาขา) */
.crop-stage {
    max-height: 60vh;
    overflow: hidden;
    border-radius: 10px;
    background: var(--nc-column-bg, #f1f4f6);
}

/* ‼️ Cropper.js ต้องได้ block-level img ที่กว้างเต็มกล่อง ไม่งั้นคำนวณกรอบเพี้ยน */
.crop-stage > img {
    display: block;
    max-width: 100%;
}

.crop-hint {
    margin: 0 0 0.6rem;
    font-size: 0.85rem;
    color: var(--nc-text-muted, #71768a);
}

/* Phase 20 - ตัวเลขราคาในหน้าแพ็กเกจ/subscription ของโซนหลังบ้าน */
body.page-admin .plan-price {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--nc-text);
}

/* ---------- modal ที่เนื้อหายาว (แพ็กเกจ/ราคา ฯลฯ) ----------
   จำกัดความสูงของเนื้อหาด้วย vh ตรง ๆ แทนการพึ่ง .modal-dialog-scrollable ซึ่งคิดความสูงเป็น %
   ของลูกโซ่ element ข้างบน - ถ้าลูกโซ่นั้นเพี้ยนเมื่อไร ปุ่มท้าย modal จะหลุดออกนอกจอทันที
   ตัวเลข 11rem = พื้นที่ของหัว modal + ปุ่มท้าย + ระยะขอบบน-ล่าง */
body.page-admin .modal-body {
    max-height: calc(100vh - 11rem);
    overflow-y: auto;
}

@media (max-height: 640px) {
    body.page-admin .modal-body {
        max-height: calc(100vh - 8rem);
    }
}

/* ---------- กล่องผลคำนวณในหน้าต่างออกบิล (Phase 24) ----------
   ‼️ ยอดที่ต้องจ่ายคือตัวเลขที่คนเปิดหน้าต่างนี้มาดู จึงเป็นตัวเดียวที่ใหญ่ ส่วนคิวที่ใช้/ยอดกระเป๋า
      เป็น "ที่มาของตัวเลขนั้น" ต้องอ่านได้แต่ต้องไม่แย่งสายตา (DESIGN_GUIDELINE ข้อ 2)
   ‼️ ตัวเลขทุกตัวใช้ tabular-nums - ตัวเลขเงินที่ความกว้างไม่เท่ากันทำให้หลักไม่ตรงกันเวลาอ่านเทียบ */
body.page-admin .billing-preview {
    padding: var(--space-3);
    border: 1px solid var(--nc-border);
    border-radius: 0.5rem;
    background: var(--nc-column-bg);
}

body.page-admin .billing-preview-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    flex-wrap: wrap;
}

body.page-admin .billing-preview-period {
    font-size: 0.8125rem;
    color: var(--nc-text-muted);
    font-variant-numeric: tabular-nums;
}

body.page-admin .billing-preview-amount {
    margin-top: var(--space-1);
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.2;
    color: var(--nc-text);
    font-variant-numeric: tabular-nums;
}

body.page-admin .billing-preview-amount small {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--nc-text-muted);
}

/* ‼️ grid ไม่ใช่ flex - ป้ายกับตัวเลขของทุกแถวต้องตรงคอลัมน์กันเพื่อให้กวาดตาลงมาอ่านได้ */
body.page-admin .billing-preview-rows {
    display: grid;
    gap: var(--space-1);
    margin: var(--space-3) 0 0;
    font-size: 0.8125rem;
}

body.page-admin .billing-preview-rows > div {
    display: flex;
    justify-content: space-between;
    gap: var(--space-3);
}

body.page-admin .billing-preview-rows dt {
    font-weight: 400;
    color: var(--nc-text-muted);
}

body.page-admin .billing-preview-rows dd {
    margin: 0;
    font-weight: 600;
    color: var(--nc-text);
    font-variant-numeric: tabular-nums;
}

body.page-admin .billing-preview-note {
    margin: var(--space-3) 0 0;
    font-size: 0.75rem;
    color: var(--nc-text-faint);
}

/* ---------- ตารางบิลของโซน OMC (Phase 24) ----------
   ‼️ องค์กรที่ยังไม่มีแพ็กเกจออกบิลไม่ได้เลย - หรี่ทั้งแถวลงเพื่อให้สายตากวาดข้ามไปหาแถวที่ทำงานได้จริง
      ‼️ ไม่ได้ซ่อน เพราะยังต้องเห็นว่ามีองค์กรนี้อยู่และยังไม่ได้ผูกแพ็กเกจ (นั่นคืองานที่ต้องทำ)
   ‼️ หรี่ด้วย opacity ไม่ใช่เปลี่ยนสีข้อความ - เปลี่ยนสีจะทำให้ contrast ของป้ายสถานะข้างในเพี้ยนตามไปด้วย
      และพอชี้เมาส์ก็คืนความชัดเต็มที่ ไม่ได้อ่านยากถาวร (DESIGN_GUIDELINE ข้อ 10) */
body.page-admin .billing-table .billing-row-idle > * {
    opacity: 0.65;
}

body.page-admin .billing-table .billing-row-idle:hover > * {
    opacity: 1;
}

/* ‼️ ตัวเลขชิดขวาของคอลัมน์ยอดกระเป๋า ไปจ่อกับข้อความชิดซ้ายของคอลัมน์ถัดไปพอดี อ่านแล้วเหมือน
   ตัวเลขชุดเดียวกัน (ผู้ใช้แจ้ง 2026-09-16) - เว้นช่องไฟฝั่งขวาให้ขอบคอลัมน์ชัดด้วยตัวเอง
   ‼️ ใช้ระยะจาก scale กลาง (--space-*) ไม่ใช่ตัวเลขสุ่ม ตาม DESIGN_GUIDELINE ข้อ 3 */
body.page-admin .billing-table .billing-col-wallet {
    padding-inline-end: var(--space-5);
}

/* ‼️ คอลัมน์ปุ่มแคบที่สุดเท่าที่ปุ่มพอดี - พื้นที่ที่เหลือควรตกเป็นของชื่อองค์กรซึ่งยาวไม่เท่ากัน
   ‼️ แต่ต้องมีช่องไฟฝั่งขวาด้วย ไม่งั้นปุ่มไปจ่อกับชื่อองค์กรจนดูเป็นก้อนเดียวกัน (ผู้ใช้แจ้ง 2026-09-16)
      ใช้ระยะจาก scale กลางเหมือนคอลัมน์ยอดกระเป๋า ไม่ใช่ตัวเลขสุ่ม (DESIGN_GUIDELINE ข้อ 3) */
body.page-admin .billing-table thead th:first-child,
body.page-admin .billing-table tbody td:first-child {
    width: 1%;
    white-space: nowrap;
    padding-inline-end: var(--space-4);
}

/* ---------- กระเป๋าเงินองค์กร (Phase 21) ----------
   ยอดเงินใช้ tabular-nums ให้หลักตัวเลขตรงกันทุกแถว (อ่านตารางการเงินได้เร็วกว่ามาก)
   สีเข้า/ออกอ้างจาก token กลางของธีม ไม่ hardcode ค่าสี */
body.page-admin .wallet-balance {
    font-size: 2.2rem;
    font-weight: 700;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
    color: var(--nc-text);
}

body.page-admin .wallet-currency {
    font-size: 1rem;
    font-weight: 500;
    color: var(--nc-text-muted);
    margin-left: 0.35rem;
}

body.page-admin .wallet-amount {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

body.page-admin .wallet-amount-in {
    color: var(--nc-success);
}

body.page-admin .wallet-amount-out {
    color: var(--nc-danger);
}

/* ---------- โปรไฟล์ผู้ใช้ + ปุ่มออกจากระบบบนแถบบน ----------
   ย้ายมาจากมุมซ้ายล่างของ sidebar ตามที่ผู้ใช้กำหนด - อยู่ที่แถบบนที่เดียวของทุกโซนแล้ว
   จอแคบซ่อนเฉพาะ "ชื่อ" ไว้ (ไอคอนกับปุ่มออกจากระบบยังอยู่) เพราะชื่อยาวจะเบียดปุ่มจนกดยาก */
/* ปุ่มสลับธีมบนแถบบนไม่มีกรอบตามที่ผู้ใช้กำหนด (ปุ่มออกจากระบบยังมีกรอบเหมือนเดิม เพราะเป็นปุ่มที่ต้อง
   สังเกตเห็นชัด) - ยังคงพื้นที่กดขนาดเท่าเดิมจาก .icon-btn และมีพื้นหลังตอน hover ให้รู้ว่ากดได้ */
.app-navbar #btn-theme-toggle {
    background: transparent;
    border-color: transparent;
}

.app-navbar #btn-theme-toggle:hover {
    background: var(--bs-primary-soft);
    border-color: transparent;
}

body.page-admin .app-navbar-user {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
    max-width: 220px;
}

body.page-admin .app-navbar-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border-radius: 10px;
    background: var(--bs-primary-soft);
    color: var(--bs-primary);
    font-size: 0.95rem;
}

/* ---------- เมนูผู้ใช้บนแถบบน (partials/app-user-menu.php) ----------
   ‼️ ออกจากระบบต้องกดสองจังหวะตามที่ผู้ใช้กำหนด: กดชิปผู้ใช้ให้เมนูกางก่อน แล้วค่อยกด "ออกจากระบบ"
   ชิปเป็น <button> จึงต้องล้างหน้าตา default ของปุ่มออกให้เหลือรูปทรงเดิมของ .app-navbar-user
   ใช้ทั้งแถบบนของ OMC/องค์กร/สาขา และแถบหัวของ Console (.app-topbar) - ทุกที่หน้าตาเดียวกัน
   ‼️ พื้นแถบหัวของ Console ไม่ใช่สีแบรนด์ (--header-bg เป็นค่าคงที่) อย่าเขียนกฎเฉพาะให้มันอีก */
body.page-admin .app-user-menu-toggle {
    border: 1px solid transparent;
    background: transparent;
    padding: 0.15rem 0.35rem 0.15rem 0.15rem;
    border-radius: 10px;
    color: inherit;
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease;
}

body.page-admin .app-user-menu-toggle:hover,
body.page-admin .app-user-menu-toggle[aria-expanded="true"] {
    background: var(--bs-primary-soft);
}

/* ลูกศรชี้ลง = เมนูยังปิด / ชี้ขึ้น = เมนูกางอยู่ (ผู้ใช้จะได้รู้ว่ากดแล้วมีอะไรต่อ) */
body.page-admin .app-user-menu-caret {
    font-size: 0.7rem;
    color: var(--nc-text-muted);
    transition: transform .15s ease;
}

body.page-admin .app-user-menu-toggle[aria-expanded="true"] .app-user-menu-caret {
    transform: rotate(180deg);
}

body.page-admin .app-user-menu-list {
    min-width: 220px;
    border: 1px solid var(--nc-border);
    border-radius: 12px;
    background: var(--nc-surface);
    /* ใช้ค่าเงาชุดเดียวกับการ์ด/แผงอื่นในไฟล์นี้ (rgba(20, 22, 40, ...)) ไม่สร้างค่าสีเงาชุดใหม่ */
    box-shadow: 0 8px 20px rgba(20, 22, 40, .12);
    padding: 0.35rem;
}

body.page-admin .app-user-menu-logout {
    display: flex;
    align-items: center;
    border-radius: 8px;
    color: var(--nc-danger);
}

body.page-admin .app-user-menu-logout:hover,
body.page-admin .app-user-menu-logout:focus {
    background: var(--nc-danger-soft);
    color: var(--nc-danger);
}

/* ---------- Console: แถบหัวพื้นสีแบรนด์ จึงต้องใช้สีของ header ----------
   ‼️ ตัวเมนูที่กางออกมาเป็นการ์ดพื้นขาว/เข้มตามธีมเหมือนโซนอื่น ไม่ใช่สีแบรนด์ - อ่านง่ายกว่า */
body.page-admin .app-topbar .app-user-menu-toggle {
    color: var(--header-text);
}

body.page-admin .app-topbar .app-user-menu-caret {
    color: rgba(var(--header-overlay-rgb), 0.75);
}

body.page-admin .app-topbar .app-user-menu-toggle:hover,
body.page-admin .app-topbar .app-user-menu-toggle[aria-expanded="true"] {
    background: rgba(var(--header-overlay-rgb), 0.18);
}

/* ปุ่มสลับธีมบนแถบหัวของ Console ไม่มีกรอบเหมือนโซนอื่นแล้ว (ผู้ใช้ให้ทำให้เหมือนหน้าอื่น) */
body.page-admin .app-topbar #btn-theme-toggle {
    background: transparent;
    border-color: transparent;
}

body.page-admin .app-topbar #btn-theme-toggle:hover {
    background: rgba(var(--header-overlay-rgb), 0.18);
    border-color: transparent;
}
body.page-admin .app-navbar-user-name {
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--nc-text);
    min-width: 0;
}

@media (max-width: 575.98px) {
    body.page-admin .app-navbar-user-name {
        display: none;
    }
}



/* ======================================================
   Phase 23.7 - หน้าตาของจอแสดงคิว
   ------------------------------------------------------
   ‼️ กติกาสำคัญที่สุดของบล็อกนี้: **แนวนอน (landscape) ต้องหน้าตาเหมือนเดิมทุกประการ**
      จอที่ร้านตั้งไว้แล้วต้องไม่เปลี่ยนไปเอง ไม่ว่าจะเลือกแม่แบบอะไรก็ตาม
      -> กฎที่เปลี่ยนหน้าตาทุกข้อในบล็อกนี้จึงขึ้นต้นด้วย html[data-mode="portrait"] เสมอ
      -> บล็อกใหม่ (รออีก/คิวถัดไป/แถบประชาสัมพันธ์) ถูกซ่อนในแนวนอนเสมอ
   ‼️ แม่แบบใหม่ (modern/signage) เป็นงานออกแบบของ "แนวตั้ง 9:16" ตามภาพอ้างอิงที่ผู้ใช้ให้มา
      แนวนอนยังเป็นแบบเดิมซึ่งถือเป็น baseline ที่ต้องเข้ากันได้ย้อนหลัง
   ‼️ ทั้งสามแม่แบบใช้ DOM ชุดเดียวกัน (app/Views/pages/display.php) เปลี่ยนแค่การจัดวางด้วย CSS
      จึงไม่มีตรรกะวาดคิวชุดที่สองในระบบ
   ====================================================== */

.page-display {
    --disp-primary: #0f766e;
    --disp-accent: #f59e0b;
    --disp-recent-columns: 2;
}

/* ‼️ ค่าเริ่มต้นของจอแนวนอนปิดบล็อกเหล่านี้ไว้ (ดู DisplayConfigService::DEFAULTS_LANDSCAPE)
   จอเดิมจึงหน้าตาเหมือนเดิมทุกประการ - markup ส่งมาพร้อม .d-none อยู่แล้วจึงไม่มีการกะพริบตอนโหลด
   ถ้าร้านเปิดบล็อกพวกนี้เองทีหลัง ถือเป็นการเลือกของร้านซึ่งจอแนวนอนก็รองรับได้ */

/* ---------- บล็อกใหม่: เห็นเฉพาะแนวตั้ง ---------- */
body.page-display .display-ahead {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 1.2vw;
    padding: 1.2vh 2vw;
    border-radius: 1.4vh;
    background: var(--disp-card-bg);
    border: 1px solid var(--disp-card-border);
}

body.page-display .display-upcoming {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1.2vw;
    padding: 1.2vh 2vw;
    border-radius: 1.4vh;
    background: var(--disp-card-bg);
    border: 1px solid var(--disp-card-border);
    border-left: .6vh solid var(--disp-accent);
}

body.page-display .display-promo {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 3vw;
    padding: 2vh 3vw;
    background: var(--disp-header-bg);
    border-top: 1px solid var(--disp-card-border);
}

/* ‼️ จอแนวตั้ง: แบนเนอร์ต้อง "ชิดขอบจอ" จริง ๆ ตามที่ผู้ใช้กำหนด จึงเอา padding ซ้าย-ขวาของแถบออก
   แล้วให้แบนเนอร์กินเต็มความกว้าง ส่วนข้อความ/QR ย้ายมาอยู่แถวล่างที่มี padding ของตัวเอง
   ใช้ grid แทน flex เพื่อจัดสองแถวนี้โดยไม่ต้องเพิ่ม element ครอบใน markup */
html[data-mode="portrait"] body.page-display .display-promo {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas:
        "media media"
        "text  qr";
    align-items: center;
    gap: 0;
    padding: 0;
}

html[data-mode="portrait"] body.page-display .display-promo-text {
    grid-area: text;
    padding: 1.6vh 3vw;
}

html[data-mode="portrait"] body.page-display .display-promo-qr {
    grid-area: qr;
    margin: 1.6vh 3vw;
}

/* ‼️ .d-none ของ Bootstrap ต้องชนะกฎด้านบนเสมอ (JS ใช้ .d-none เปิด/ปิดบล็อกตามค่าตั้งค่า) */
body.page-display .display-ahead.d-none,
body.page-display .display-upcoming.d-none,
body.page-display .display-promo.d-none {
    display: none;
}

.display-ahead-label,
.display-upcoming-label {
    font-size: 2vh;
    letter-spacing: 1px;
    color: color-mix(in srgb, var(--disp-text) 72%, transparent);
}

.display-upcoming-label {
    letter-spacing: 2px;
    text-transform: uppercase;
}

.display-ahead-value {
    font-family: var(--disp-font-num);
    font-size: 5.5vh;
    font-weight: 800;
    line-height: 1;
    color: var(--disp-accent);
    font-variant-numeric: tabular-nums;
}

.display-upcoming-value {
    font-family: var(--disp-font-num);
    font-size: 6vh;
    font-weight: 800;
    line-height: 1;
    letter-spacing: 2px;
    font-variant-numeric: tabular-nums;
}

.display-promo-text {
    display: flex;
    flex-direction: column;
    gap: .6vh;
    min-width: 0;
}

.display-promo-title {
    font-size: 3.4vh;
    font-weight: 800;
    line-height: 1.15;
    color: var(--disp-accent);
}

.display-promo-desc {
    font-size: 2.2vh;
    line-height: 1.4;
    color: color-mix(in srgb, var(--disp-text) 82%, transparent);
}

.display-promo-qr {
    flex: 0 0 auto;
    padding: 1vh;
    border-radius: 1vh;
    background: #fff;
    line-height: 0;
}

.display-promo-qr img,
.display-promo-qr canvas {
    display: block;
    width: 12vh;
    height: 12vh;
}

/* คำโปรยใต้ชื่อร้าน - ใช้ได้ทั้งสองแนว เพราะเป็นการ "เพิ่มบรรทัด" ไม่ใช่การเปลี่ยนโครงเดิม
   (ร้านที่ไม่กรอกคำโปรยจะไม่มี element นี้แสดงเลย จอเดิมจึงเหมือนเดิมทุกประการ) */
.display-brand-text {
    display: flex;
    flex-direction: column;
    line-height: 1.15;
    min-width: 0;
}

.display-shop-subtitle {
    font-size: 1.6vh;
    color: color-mix(in srgb, var(--disp-text) 66%, transparent);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ======================================================
   แม่แบบของ "แนวตั้ง" เท่านั้น
   ‼️ ทุกกฎด้านล่างขึ้นต้นด้วย html[data-mode="portrait"] - แนวนอนไม่ได้รับผลกระทบใด ๆ
   ‼️ ต้องอ้าง id เดียวกับกฎเดิม (#display-shell/#display-left/#previous-queues) ไม่งั้นแพ้ specificity
      (กฎเดิมของจอเขียนด้วย id ทั้งหมด - เคยเขียนด้วยคลาสล้วนแล้วไม่มีผลเลย เป็นบั๊กที่เจอตอนเปิดจอจริง)
   ====================================================== */

/* ---------- แนวตั้ง + คลาสสิก = โครงแนวตั้งเดิมของระบบ (ไม่แตะ) ---------- */

/* ---------- แนวตั้ง + โมเดิร์น ---------- */
html[data-mode="portrait"] body.display-tpl-modern {
    background: var(--disp-bg, #0b1220);
}

html[data-mode="portrait"] body.display-tpl-modern .display-calling-queueno {
    font-size: min(17vh, 30vw);
    line-height: 1;
    letter-spacing: -.02em;
}

/* ---------- แนวตั้ง + ป้ายดิจิทัล (ตามภาพอ้างอิง 9:16) ----------
   เรียงบนลงล่าง: หัว -> คิวที่กำลังเรียก -> รออีกกี่คิว -> ช่องที่เพิ่งเรียก -> คิวถัดไป -> แถบประชาสัมพันธ์ */
html[data-mode="portrait"] body.display-tpl-signage {
    background: var(--disp-bg, #0b1220);
}

/* ‼️ มี 2 แถว: เนื้อหาคิว แล้วต่อด้วยแถบสถิติ (รอคิว/รอเฉลี่ย) คั่นก่อนถึงแบนเนอร์ล่างสุด
   เดิมมีแถวเดียวแล้วซ่อนแผงสถิติทิ้ง จอแนวตั้งจึงไม่มีตัวเลข "รอคิว/รอเฉลี่ย" ให้ลูกค้าดูเลย
   ทั้งที่ค่ามีอยู่แล้วและจอแนวนอนก็แสดงอยู่ (ผู้ใช้แจ้ง 2026-09-09)
   แถวสถิติเป็น auto = สูงตามเนื้อหา ไม่แย่งพื้นที่เลขคิวซึ่งยังต้องใหญ่ที่สุดบนจอ */
html[data-mode="portrait"] body.display-tpl-signage #display-shell {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(0, 1fr) auto;
    grid-template-areas:
        "left"
        "media";
}

html[data-mode="portrait"] body.display-tpl-signage #display-left {
    display: flex;
    flex-direction: column;
    gap: 1.6vh;
    min-height: 0;
}

/* ‼️ #display-right เป็นแค่กล่องห่อของเลย์เอาต์แนวนอน - ในแม่แบบนี้ต้องเป็น contents ไม่ใช่ none
   ถ้า none ลูกข้างใน (#display-media-panel ซึ่งเป็นที่อยู่ของแถบสถิติ) จะหายไปด้วยทั้งก้อน
   ซึ่งเป็นต้นเหตุที่จอแนวตั้งไม่มี "รอคิว/รอเฉลี่ย" ให้ดูเลย */
html[data-mode="portrait"] body.display-tpl-signage #display-right {
    display: contents;
}

/* แถบสถิติของจอแนวตั้ง - ไม่มีพื้นหลัง/กรอบการ์ด เพราะวางเป็นแถบคาดเต็มความกว้างคั่นระหว่าง
   ช่องคิวกับแบนเนอร์ล่าง (กล่องสไลด์ในแผงนี้ถูกซ่อนไว้แล้วในโหมดแนวตั้ง เหลือแต่ตัวเลข) */
html[data-mode="portrait"] body.display-tpl-signage #display-media-panel {
    grid-area: media;
    display: flex;
    min-height: 0;
    background: transparent;
    border-radius: 0;
}

html[data-mode="portrait"] body.display-tpl-signage #calling-card,
html[data-mode="portrait"] body.display-tpl-signage #calling-board-empty,
html[data-mode="portrait"] body.display-tpl-signage #previous-queues,
html[data-mode="portrait"] body.display-tpl-signage #display-ahead,
html[data-mode="portrait"] body.display-tpl-signage #display-upcoming {
    grid-area: auto;
}

/* ‼️ flex-basis ต้องเป็น 0 ไม่ใช่ auto - ถ้าเป็น auto กล่องจะยึดความสูงตามเนื้อหาก่อน แล้วดันบล็อกท้าย ๆ
   (คิวถัดไป/แถบประชาสัมพันธ์) ตกขอบจอไป ส่วน 0 ทำให้การ์ดคิวกับกริดช่องคิว "แบ่ง" พื้นที่ที่เหลือกันจริง ๆ
   บั๊กนี้เจอจากการเปิดจอจริงดู ไม่ได้เจอจากการอ่านโค้ด */
html[data-mode="portrait"] body.display-tpl-signage #calling-card {
    flex: 3 1 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: .6vh;
    min-height: 0;
    overflow: hidden;
    padding: 2vh 3vw;
    border-radius: 2.4vh;
    /* ‼️ ใช้สีประจำประเภทคิวของคิวที่กำลังเรียก ไม่ใช่สีที่ตั้งในหน้าตั้งค่าจอ (ผู้ใช้เอาสีหลักออกแล้ว) */
    background: color-mix(in srgb, var(--cat-color, var(--disp-primary)) 14%, transparent);
    border-color: color-mix(in srgb, var(--cat-color, var(--disp-primary)) 40%, transparent);
}

html[data-mode="portrait"] body.display-tpl-signage .display-calling-queueno {
    font-size: min(11vh, 24vw);
    line-height: 1.02;
    letter-spacing: -.03em;
}

html[data-mode="portrait"] body.display-tpl-signage .display-calling-label {
    font-size: 2.2vh;
    letter-spacing: 4px;
}

/* กล่อง "เชิญที่ช่อง" อยู่ใต้เลขคิวและเล็กลง เพื่อให้เลขคิวเป็นพระเอกคนเดียว */
html[data-mode="portrait"] body.display-tpl-signage .display-calling-counter {
    flex-direction: row;
    align-items: baseline;
    gap: 1vw;
    padding: .6vh 1.6vw;
}

html[data-mode="portrait"] body.display-tpl-signage .display-counter-no {
    font-size: 4.4vh;
}

html[data-mode="portrait"] body.display-tpl-signage #display-ahead,
html[data-mode="portrait"] body.display-tpl-signage #display-upcoming {
    flex: 0 0 auto;
}

html[data-mode="portrait"] body.display-tpl-signage #previous-queues {
    flex: 2 1 0;
    min-height: 0;
}

html[data-mode="portrait"] body.display-tpl-signage .display-previous-slot {
    border-radius: 1.8vh;
    padding: 1vh .8vw;
    min-width: 0;
    min-height: 0;
    overflow: hidden;
}

/* ป้ายดิจิทัลไม่ต้องมีบรรทัด "ช่อง N" ในช่องคิวย่อย - เคาน์เตอร์อ่านได้จากการ์ดคิวหลักอยู่แล้ว */
/* ‼️ เดิมซ่อน "ช่อง N" ใต้เลขคิวทิ้งตายตัวในแม่แบบป้ายดิจิทัล ตั้งแต่ตอนที่ยังเลือกแม่แบบเองได้
   ตอนนี้จอแนวตั้งถูกล็อกเป็นแม่แบบนี้เสมอ กฎนั้นเลยกลายเป็น "จอแนวตั้งไม่มีช่องให้ดูเลย"
   ทั้งที่หน้าตั้งค่ามีสวิตช์ แสดงกล่อง "เชิญที่ช่อง" เปิดอยู่ - ปล่อยให้ตามสวิตช์เหมือนจอแนวนอน
   (การซ่อนจริงทำผ่านคลาส display-counter-hidden ที่ JS ใส่ให้ตามค่า show_counter) */
html[data-mode="portrait"] body.display-tpl-signage .display-previous-counter {
    font-size: 1.6vh;
}

html[data-mode="portrait"] body.display-tpl-signage .display-upcoming-value {
    font-size: 5.6vh;
}

/* ป้ายหน้าร้านมักแขวนสูงและดูจากไกล - ชื่อร้านจึงใหญ่กว่าจอในสำนักงาน */
html[data-mode="portrait"] body.display-tpl-signage .display-shop-name {
    font-size: 3.4vh;
}

/* จุดกะพริบหน้าป้าย "กำลังเรียกคิว" มีวงกระเพื่อมแบบ absolute อยู่รอบตัว - ป้ายถูกจัดกึ่งกลางในแม่แบบนี้
   จึงต้องเว้นระยะให้วงนั้นไม่ทับตัวอักษรตัวแรก */
html[data-mode="portrait"] body.display-tpl-signage .display-pulse-dot {
    margin-right: 2.4vh;
}

/* จำนวนคอลัมน์ของช่อง "เรียกล่าสุด" มาจากค่าตั้งค่าของสาขา (แนวตั้งเท่านั้น)
   ‼️ เพดานขนาดตัวเลขต้องพอดีกับความสูงของช่องที่ได้รับจริง ไม่งั้นตัวเลขโดนตัดครึ่ง */
html[data-mode="portrait"] body.display-tpl-modern #previous-queues,
html[data-mode="portrait"] body.display-tpl-signage #previous-queues {
    grid-template-columns: repeat(var(--disp-recent-columns), minmax(0, 1fr));
    gap: 1.4vh 1.2vw;
}

/* ‼️ เพดานต้องมี "ความสูงของช่อง" (cqh) อยู่ด้วย ไม่ใช่แค่ vw/vh
   เดิมเพดานเป็น 4.2vh ล้วน ซึ่งไม่รู้จักความสูงช่องที่ได้รับจริงเลย พอปรับจำนวนคอลัมน์แล้วจำนวนแถว
   เปลี่ยน ช่องเตี้ยลงจนกล่องตัวเลขถูก flex บีบให้เตี้ยกว่าบรรทัดตัวอักษร (วัดจริง: กล่อง 40px แต่
   บรรทัดต้องใช้ 41.8px) แล้วโดน overflow:hidden ตัดหัว-ท้ายตัวอักษรหาย
   - ผู้ใช้แจ้ง 2026-09-09 "ปรับเป็น 2 คอลัมน์แล้วอักษรโดนบีบหาย"
   40cqh = 40% ของความสูงช่อง เหลือที่ให้บรรทัด "ช่อง -" ใต้ตัวเลขพอดีทุกค่าคอลัมน์ (1-4) */
html[data-mode="portrait"] body.display-tpl-modern .display-previous-queueno,
html[data-mode="portrait"] body.display-tpl-signage .display-previous-queueno {
    /* ‼️ เอา cqh ไว้ "ข้างใน" clamp ไม่ใช่ครอบข้างนอก - ถ้าครอบข้างนอกด้วย min() แล้วช่องเตี้ยมาก
       (1 คอลัมน์ + หลายช่อง) ตัวเลขจะยุบเหลือ 4.7px จนอ่านไม่ออกเลย วัดเจอจริงตอนทดสอบ
       แบบนี้ clamp ยังการันตีพื้นขั้นต่ำ 2.2vh เสมอ ส่วน 40cqh แค่กันไม่ให้ใหญ่เกินช่อง */
    font-size: clamp(2.2vh, min(calc(22vw / var(--disp-recent-columns)), 40cqh), 4.2vh);
    line-height: 1.1;
    max-width: 100%;
    overflow: hidden;
    white-space: nowrap;
    /* ‼️ ห้ามถูก flex บีบ - ถ้าที่ไม่พอให้บรรทัด "ช่อง" ย่อแทน ตัวเลขคิวต้องอ่านออกเสมอ */
    flex: 0 0 auto;
}

/* ‼️ "ปิดเพราะตั้งค่าไม่ให้แสดง" - แยกจาก .d-none ที่แปลว่า "ซ่อนเพราะยังไม่มีข้อมูล" โดยเจตนา
   ถ้าใช้คลาสเดียวกัน ตัวที่ poll ค่าตั้งค่า (5 วิ) กับตัวที่ poll ข้อมูลคิว (2 วิ) จะแย่งกันเปิด-ปิด
   element เดียวกันจนจอกระพริบ (บั๊กที่เจอจริงบนจอแนวนอน)
   ต้องมี !important เพราะบล็อกพวกนี้ถูกกำหนดหน้าตาด้วย selector ที่มี id ซึ่งชนะคลาสเปล่า ๆ เสมอ */
.display-section-off { display: none !important; }

/* ---------- ธีมสว่าง ----------
   ‼️ มีผลกับทั้งสองจอ เพราะแนวนอนกับแนวตั้งเก็บค่าตั้งค่าแยกกันคนละชุดแล้ว และค่าเริ่มต้นของแนวนอน
      ยังเป็นโหมดมืด จอที่ใช้อยู่จึงไม่เปลี่ยนสีเองจนกว่าเจ้าของร้านจะเลือกสว่างเอง
   ‼️ เส้นขอบ/พื้นการ์ดคำนวณใหม่จากสีตัวอักษรที่ผู้ใช้เลือก แทน rgba ขาวเดิมซึ่งจะจางจนมองไม่เห็นบนพื้นสว่าง */
body.display-theme-light {
    --disp-card-bg: color-mix(in srgb, var(--disp-text) 5%, transparent);
    /* ‼️ ไม่เขียนทับ --disp-card-border ที่นี่ - สีเส้นขอบเป็นค่าที่ผู้ใช้ตั้งเองแล้ว
       ถ้าตั้งทับตรงนี้ ธีมสว่างจะไปลบล้างสีที่เจ้าของร้านเลือกไว้ */
    --disp-header-bg: color-mix(in srgb, var(--disp-text) 4%, transparent);
    background: var(--disp-bg, #f8fafc);
    color: var(--disp-text, #0f172a);
}

/* ======================================================
   Phase 23.7 - หน้าตั้งค่าจอแสดงผล (ตัวควบคุม + ตัวอย่างสด)
   ====================================================== */

.display-customizer {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--nc-gap, 1rem);
    align-items: start;
}

@media (min-width: 1200px) {
    .display-customizer {
        /* ตัวควบคุมกว้างกว่าเล็กน้อยเพราะมีฟอร์มหลายบรรทัด ส่วนตัวอย่างแค่ต้องเห็นสัดส่วนจอชัดพอ */
        grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    }

    /* ‼️ ผู้ใช้ขอให้คอลัมน์ขวา "นิ่งสนิท" ไม่ขยับเลย - ตัวที่เลื่อนจริงคือ .admin-content (overflow-y: auto)
       แผงนี้จึงเกาะขอบบนของกล่องนั้น ไม่ใช่ขอบบนของหน้าต่าง
       ‼️ ระยะเกาะต้องเท่ากับ ความสูง navbar + margin ใต้ navbar พอดี ด้วยเหตุผล 2 ข้อ:
       1) น้อยกว่านี้ = จุดเกาะไปอยู่ใต้ .app-navbar (sticky top: 0) หัวการ์ดจะมุดหายหลัง navbar
       2) ตรงพอดี = ตำแหน่งที่มันอยู่ตั้งแต่ scroll 0 อยู่แล้ว แผงจึงไม่ขยับแม้แต่พิกเซลเดียว
          (ถ้าตั้งน้อยกว่าตำแหน่งเริ่มต้น แผงจะเลื่อนตามหน้าจอช่วงสั้น ๆ ก่อนค่อยหยุด = ยังดูขยับอยู่)
       max-height + overflow-y กันกรณีจอเตี้ยกว่าตัวแผงเอง ไม่งั้นปุ่มบันทึกด้านล่างจะกดไม่ถึง */
    .display-customizer-preview {
        position: sticky;
        top: calc(var(--nc-navbar-h) + var(--nc-navbar-gap));
        max-height: calc(100vh - var(--nc-navbar-h) - var(--nc-navbar-gap) - 1rem);
        overflow-y: auto;
    }
}

/* ‼️ สองหัวข้อที่ผู้ใช้ขอให้อยู่บรรทัดเดียวกัน (ส่วนที่แสดง + สีและธีม)
   auto-fit ทำให้ตกลงมาเรียงบนล่างเองเมื่อคอลัมน์ฟอร์มแคบกว่าราว 38rem โดยไม่ต้องเขียน media query ซ้อน
   (คอลัมน์นี้แคบลงทั้งจากขนาดจอและจากการที่แผงตัวอย่างกินพื้นที่ด้านขวาอยู่แล้ว) */
.display-settings-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
    gap: var(--nc-gap, 1rem);
    /* ‼️ stretch (ไม่ใช่ start) เพื่อให้การ์ดสองใบสูงเท่ากันตามที่ผู้ใช้ขอ - start จะปล่อยให้แต่ละใบ
       สูงตามเนื้อหาของตัวเอง แล้วใบที่เนื้อหาน้อยกว่าจะสั้นกว่าจนดูไม่เป็นแถวเดียวกัน */
    align-items: stretch;
}

.display-customizer-controls {
    display: flex;
    flex-direction: column;
    gap: var(--nc-gap, 1rem);
    min-width: 0;
}

.display-customizer-preview { min-width: 0; }

/* ---------- ตัวเลือกแม่แบบ ---------- */
.display-template-picker {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: .75rem;
}

.display-template-option {
    position: relative;
    display: block;
    margin: 0;
    cursor: pointer;
}

.display-template-option input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.display-template-card {
    display: flex;
    flex-direction: column;
    gap: .3rem;
    height: 100%;
    padding: .9rem .8rem;
    border: 1px solid var(--nc-border);
    border-radius: .6rem;
    background: var(--nc-surface);
    transition: border-color .15s ease, background-color .15s ease;
}

.display-template-card i {
    font-size: 1.35rem;
    color: var(--bs-primary);
}

.display-template-name { font-weight: 600; }

.display-template-hint {
    font-size: .8125rem;
    color: var(--bs-secondary-color);
    line-height: 1.35;
}

.display-template-option input:checked + .display-template-card {
    border-color: var(--bs-primary);
    background: var(--bs-primary-soft);
}

.display-template-option input:focus-visible + .display-template-card {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}

/* ---------- ช่องเลือกสี ---------- */
/* ชุดสีสำเร็จรูป - กดแล้วเติมค่าลงช่องเลือกสีทั้ง 4 ช่องให้เลย ผู้ใช้ค่อยปรับต่อเองได้ */
.display-preset-row {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-bottom: .85rem;
}

.display-preset-btn {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .4rem .7rem;
    border: 1px solid var(--nc-border);
    border-radius: .5rem;
    background: var(--nc-surface);
    color: inherit;
    font-size: .8125rem;
    transition: border-color .15s ease;
}

.display-preset-btn:hover { border-color: var(--bs-primary); }

/* ตัวอย่างสีในปุ่ม - วงกลมซ้อนกันสองวง (พื้นหลัง + สีหลัก) ให้เห็นคู่สีจริงก่อนกด */
.display-preset-swatch {
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 50%;
    border: 1px solid var(--nc-border);
    flex: 0 0 auto;
}

/* สีตัวอย่างของแต่ละชุด ‼️ ต้องตรงกับ DISPLAY_COLOR_PRESETS ใน app.js ซึ่งเป็นค่าที่กดแล้วเติมลงฟอร์มจริง */
.display-preset-swatch-dark-bg { background: #0b1220; }
.display-preset-swatch-dark-primary { background: #0f766e; }
.display-preset-swatch-light-bg { background: #f8fafc; }
.display-preset-swatch-light-primary { background: #0f766e; }

.display-color-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
    gap: .75rem;
}

.display-color-field { min-width: 0; }

.display-color-field .form-control-color {
    width: 100%;
    height: 2.5rem;
}

/* ---------- ตัวอย่างสด ----------
   ‼️ ตัวอย่างนี้เป็นภาพจำลองในหน้าแอดมิน ไม่ใช่จอจริง จึงใช้หน่วยอิงกล่องตัวเอง (cqw/%) แทน vh
      ถ้าใช้ vh เหมือนจอจริง ตัวเลขจะล้นกล่องพรีวิวทันทีบนจอโน้ตบุ๊ก */
/* ‼️ container-type: size (ไม่ใช่ inline-size) เพื่อให้ใช้หน่วย cqh ได้ - ขนาดตัวอักษรในตัวอย่าง
   ต้องอิง "ความสูง" ของกล่องเหมือนจอจริงที่อิง vh ไม่งั้นพอเปิดครบทุกบล็อกจะล้นออกมาทับกัน */
.display-preview {
    container-type: size;
    display: flex;
    flex-direction: column;
    gap: 1.5cqh;
    aspect-ratio: 16 / 9;
    padding: 2cqh 2cqw;
    overflow: hidden;
    border-radius: .6rem;
    overflow: hidden;
    background: var(--disp-bg, #0b1220);
    color: var(--disp-text, #f8fafc);
    font-family: "Segoe UI", "Leelawadee UI", "Noto Sans Thai", Tahoma, sans-serif;
}

.display-preview-header {
    display: flex;
    align-items: center;
    gap: 1.5cqw;
    padding-bottom: 1.2cqh;
    border-bottom: 1px solid var(--disp-border, color-mix(in srgb, currentColor 18%, transparent));
    flex: 0 0 auto;
}

.display-preview-logo {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 9cqh;
    height: 9cqh;
    flex: 0 0 auto;
    color: var(--cat-color, var(--disp-primary));
}

.display-preview-logo img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

.display-preview-brand {
    display: flex;
    flex-direction: column;
    line-height: 1.15;
    min-width: 0;
    margin-right: auto;
}

.display-preview-shop {
    font-weight: 700;
    font-size: 4.2cqh;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.display-preview-subtitle {
    font-size: 2.9cqh;
    opacity: .72;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.display-preview-clock {
    font-size: 3.2cqh;
    opacity: .8;
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
}

.display-preview-body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 1.4cqh;
    flex: 1 1 0;
    min-height: 0;
    overflow: hidden;
}

.display-preview-label {
    font-size: 2.7cqh;
    letter-spacing: .08em;
    text-transform: uppercase;
    opacity: .7;
}

/* วางเหมือน #calling-card ของจริง: เนื้อหาหลักชิดซ้าย กล่องเคาน์เตอร์ชิดขวา */
.display-preview-current {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 2cqw;
    /* สัดส่วนเดียวกับจอจริง: #calling-card flex 1.5 ต่อ #previous-queues flex 1 */
    flex: 1.5 1 0;
    min-height: 0;
    overflow: hidden;
    padding: 1.5cqh 2cqw;
    border-radius: 1.5cqh;
    background: color-mix(in srgb, var(--cat-color, var(--disp-primary)) 10%, color-mix(in srgb, currentColor 8%, transparent));
    border: 1px solid var(--disp-border, color-mix(in srgb, currentColor 16%, transparent));
    /* ‼️ แถบสีซ้ายเหมือน #calling-card ของจอแนวนอนจริง (border-left 10px ด้วย --cat-color
       ที่ตกกลับมาเป็นสีหลัก) - ตัวอย่างจะได้สื่อว่าสีหลักไปโผล่ตรงไหนจริง ๆ */
    border-left: 1.2cqh solid var(--cat-color, var(--disp-primary));
}

/* แนวตั้งใช้แม่แบบป้ายดิจิทัล ซึ่งการ์ดคิวเป็นพื้นสีหลักทั้งใบ ไม่ใช่แถบซ้ายแบบคลาสสิก */
.display-preview-portrait .display-preview-current {
    background: color-mix(in srgb, var(--cat-color, var(--disp-primary)) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--cat-color, var(--disp-primary)) 40%, transparent);
    border-left-width: 1px;
}

.display-preview-current-main {
    display: flex;
    flex-direction: column;
    gap: .4cqh;
    min-width: 0;
    overflow: hidden;
}

.display-preview-number {
    font-size: 17cqh;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
    /* ‼️ กันเลขคิวตัดบรรทัดกลางตัว (เคยเห็น "A02" กลายเป็น "A0" / "2" ในตัวอย่างแนวตั้ง) */
    white-space: nowrap;
}

.display-preview-counter {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .3cqh;
    flex: 0 0 auto;
    padding: 1cqh 1.6cqw;
    border-radius: 1.2cqh;
    background: color-mix(in srgb, var(--disp-bg) 62%, transparent);
    border: 1px solid var(--disp-border, color-mix(in srgb, currentColor 16%, transparent));
}

.display-preview-counter-label {
    font-size: 2.2cqh;
    opacity: .7;
    white-space: nowrap;
}

.display-preview-counter-no {
    font-size: 5cqh;
    font-weight: 800;
    line-height: 1;
    /* ตรงกับ .display-counter-no ของจอจริงที่ใช้สีประจำประเภทคิว */
    color: var(--cat-color, var(--disp-primary));
    font-variant-numeric: tabular-nums;
}

/* การ์ดสื่อ + แถบสถิติ = การ์ดเดียวกันเหมือน #display-media-panel ของจริง */
.display-preview-media {
    display: flex;
    flex-direction: column;
    min-height: 0;
    border-radius: 1cqh;
    overflow: hidden;
    background: color-mix(in srgb, currentColor 6%, transparent);
    border: 1px solid var(--disp-border, color-mix(in srgb, currentColor 16%, transparent));
}

.display-preview-stats {
    display: flex;
    flex: 0 0 auto;
    border-top: 1px solid var(--disp-border, color-mix(in srgb, currentColor 16%, transparent));
}

.display-preview-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .2cqh;
    flex: 1 1 0;
    padding: .8cqh .3cqw;
    min-width: 0;
}

.display-preview-stat:first-child {
    border-right: 1px solid var(--disp-border, color-mix(in srgb, currentColor 16%, transparent));
}

.display-preview-stat-value {
    font-size: 3.4cqh;
    font-weight: 800;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.display-preview-stat-label {
    font-size: 2.1cqh;
    opacity: .65;
    white-space: nowrap;
}

/* แถบท้ายจอเหมือนของจริง - ป้ายแดงแบรนด์ + ข้อความประกาศ */
.display-preview-footer {
    display: flex;
    align-items: center;
    gap: 1.5cqw;
    flex: 0 0 auto;
    padding-top: 1.2cqh;
    border-top: 1px solid var(--disp-border, color-mix(in srgb, currentColor 16%, transparent));
    overflow: hidden;
}

.display-preview-badge {
    flex: 0 0 auto;
    background: #e8394a;
    color: #fff;
    font-weight: 800;
    font-size: 2.2cqh;
    padding: .5cqh 1.2cqw;
    border-radius: 999px;
}

.display-preview-ticker {
    font-size: 2.4cqh;
    opacity: .9;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.display-preview-ahead {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 1.5cqw;
    flex: 0 0 auto;
}

.display-preview-ahead-value {
    font-size: 6cqh;
    font-weight: 800;
    line-height: 1;
    color: var(--disp-accent);
    font-variant-numeric: tabular-nums;
}

.display-preview-recent {
    display: grid;
    grid-template-columns: repeat(var(--disp-recent-columns, 2), minmax(0, 1fr));
    grid-auto-rows: minmax(0, 1fr);
    gap: 1cqh 1cqw;
    flex: 1 1 0;
    align-content: stretch;
    min-height: 0;
    overflow: hidden;
}

/* ‼️ ช่องคิวถูกยืดความสูงให้เท่ากันทุกแถว (grid-auto-rows: 1fr) ตัวเลขจึงต้องจัดกลางแนวตั้งด้วย
   ไม่งั้นตัวเลขจะไปเกาะขอบบนของช่อง เหลือที่ว่างค้างด้านล่าง ไม่เหมือนจอจริง */
.display-preview-slot {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: .5cqh .4cqw;
    text-align: center;
    font-size: 4.4cqh;
    font-weight: 700;
    line-height: 1.15;
    border-radius: 1cqh;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: clip;
    background: color-mix(in srgb, currentColor 8%, transparent);
    border: 1px solid var(--disp-border, color-mix(in srgb, currentColor 16%, transparent));
    font-variant-numeric: tabular-nums;
}

.display-preview-upcoming {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1.5cqw;
    flex: 0 0 auto;
    padding: 1cqh 1.5cqw;
    border-radius: 1.2cqh;
    border-left: 1cqh solid var(--disp-accent);
    background: color-mix(in srgb, currentColor 7%, transparent);
}

.display-preview-upcoming-value {
    font-size: 6.5cqh;
    font-weight: 800;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.display-preview-promo {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 2cqw;
    padding-top: 1.2cqh;
    overflow: hidden;
    border-top: 1px solid color-mix(in srgb, currentColor 18%, transparent);
    flex: 0 0 auto;
}

.display-preview-promo-text {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    min-width: 0;
}

.display-preview-promo-title {
    font-size: 3.4cqh;
    font-weight: 700;
    color: var(--disp-accent);
    line-height: 1.2;
}

.display-preview-promo-desc {
    font-size: 2.6cqh;
    opacity: .82;
    line-height: 1.3;
}

.display-preview-promo-qr {
    flex: 0 0 auto;
    padding: .2rem;
    border-radius: .25rem;
    background: #fff;
    line-height: 0;
}

.display-preview-promo-qr img,
.display-preview-promo-qr canvas {
    display: block;
    width: 12cqh;
    height: 12cqh;
}

/* แม่แบบในตัวอย่าง: คลาสสิกไม่มีสองบล็อกนี้เหมือนจอจริง */
.display-preview.display-tpl-classic .display-preview-ahead,
.display-preview.display-tpl-classic .display-preview-upcoming {
    display: none;
}

/* ป้ายดิจิทัลเป็นแนวตั้งกว่า - ให้สัดส่วนพรีวิวสูงขึ้นและเลขคิวใหญ่ขึ้นตามของจริง */
.display-preview.display-tpl-signage {
    aspect-ratio: 4 / 3;
}

.display-preview.display-tpl-signage .display-preview-number { font-size: 15cqh; }

.display-preview.display-tpl-modern .display-preview-number { font-size: 22cqh; }


/* ======================================================
   Phase 23.7 - พื้นที่โฆษณาแยกตามแนวการวางจอ
   ------------------------------------------------------
   ‼️ จอแนวนอน: พื้นที่โฆษณายังเป็นกล่องสไลด์เดิมที่คอลัมน์ขวา (ภาพจัตุรัส/ใกล้จัตุรัส) - ไม่มีอะไรเปลี่ยน
   ‼️ จอแนวตั้ง: พื้นที่โฆษณาเป็น "แบนเนอร์แนวนอน ~16:9" อยู่แถบล่าง คนละชุดภาพกับแนวนอน
   ‼️ ใช้ object-fit เพื่อการแสดงผลเท่านั้น ไฟล์ต้นฉบับไม่ถูกครอบตัด/แก้ไขใด ๆ
   ====================================================== */

.display-promo-media-box {
    display: none;
}

/* ‼️ เต็มความกว้างจอ ไม่มีเส้นขอบ ไม่มีมุมโค้ง ไม่มี margin - "ชิดขอบไปเลย" ตามที่ผู้ใช้กำหนด
   ความสูงมาจากสัดส่วน 16:9 ของความกว้างจอ จึงไม่ต้องกำหนด max-height มาบีบให้แคบลงอีก */
html[data-mode="portrait"] body.page-display .display-promo-media-box {
    grid-area: media;
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    /* ‼️ คุมความสูงไว้ 22vh เท่าตอนที่ยังมีกรอบ ตามที่ผู้ใช้กำหนดว่าต้องการแค่ "ชิดขอบซ้าย-ขวา" ไม่ใช่ใหญ่ขึ้น
       - 16:9 เต็มความกว้างจอกินพื้นที่ราว 1 ใน 3 ของจอ ซึ่งเบียดส่วนคิวจนดูแปลก
       เมื่อ max-height มีผล กล่องจะเตี้ยกว่า 16:9 แล้ว object-fit: cover จะตัดขอบบน-ล่างของภาพบ้าง
       จึงต้องคู่กับคำแนะนำสัดส่วนภาพในหน้าตั้งค่า (แบนเนอร์ 16:9 โดนตัดน้อยที่สุด) */
    max-height: 22vh;
    border-radius: 0;
    overflow: hidden;
}

html[data-mode="portrait"] body.page-display .display-promo-media-box.d-none {
    display: none;
}

.display-promo-media {
    position: relative;
    width: 100%;
    height: 100%;
}

.display-promo-media img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    /* contain (ไม่ใช่ cover) = เห็นแบนเนอร์เต็มใบเสมอ ไม่ตัดข้อความโฆษณาทิ้ง
       ถ้าสัดส่วนไม่ตรง 16:9 พอดีจะเหลือขอบว่างแทนที่จะกินเนื้อหาหาย ตามที่ผู้ใช้กำหนด */
    object-fit: cover;
    opacity: 0;
    transition: opacity .6s ease;
}

.display-promo-media img.active {
    opacity: 1;
}

/* แถบล่างของจอแนวตั้งจัดวางเป็น: แบนเนอร์ (ยืดได้) + ข้อความ + QR */
/* ‼️ ห้ามใส่ gap ที่นี่ - กฎนี้อยู่หลังกฎ grid ด้านบนและ specificity เท่ากัน จึงเขียนทับ gap: 0 ของกฎนั้น
   ผลคือมีช่องว่างค้างใต้แบนเนอร์เท่ากับ gap (วัดได้ 14.4px) แม้แถวข้อความจะถูกซ่อนจนสูง 0 แล้วก็ตาม
   เพราะ grid ยังนับ gap ระหว่างแถวอยู่ดี (บั๊กที่ผู้ใช้เจอ: แบนเนอร์ไม่ชิดแถบประกาศท้ายจอ) */
html[data-mode="portrait"] body.page-display .display-promo {
    align-items: stretch;
}

html[data-mode="portrait"] body.page-display .display-promo-text {
    justify-content: center;
    flex: 0 1 auto;
}

/* ---------- กล่องตัวอย่างในหน้าตั้งค่า ----------
   สัดส่วนต้องเปลี่ยนตามแนวจอที่เลือก เพื่อให้เห็นความสัมพันธ์ แนวจอ + เลย์เอาต์ + ช่องโฆษณา ตามจริง */
.display-preview.display-preview-portrait {
    aspect-ratio: 9 / 16;
    max-height: 30rem;
    margin-inline: auto;
}

.display-preview.display-preview-landscape {
    aspect-ratio: 16 / 9;
}

/* ช่องโฆษณาจำลองในตัวอย่าง - แนวนอนเป็นจัตุรัสที่คอลัมน์ขวา แนวตั้งเป็นแบนเนอร์ 16:9 ด้านล่าง */
.display-preview-adslot {
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 1cqh;
    border: 1px dashed color-mix(in srgb, currentColor 30%, transparent);
    background: color-mix(in srgb, currentColor 6%, transparent);
    font-size: 2.4cqh;
    opacity: .75;
    text-align: center;
    padding: .5cqh;
    overflow: hidden;
}

/* สัดส่วนความกว้างใกล้เคียงกับคอลัมน์ขวาของจอแนวนอนจริง (~38% ของพื้นที่) เพื่อให้ตัวอย่างสื่อของจริง */
/* ‼️ ขนาดคุมที่ "การ์ดสื่อ" ไม่ใช่ที่ช่องภาพ เพราะการ์ดมีแถบสถิติต่อท้ายอยู่ด้วย
   ถ้าไปคุมที่ช่องภาพ แถบสถิติจะดันการ์ดให้สูงเกินสัดส่วนที่ตั้งใจ */
.display-preview-adslot { flex: 1 1 auto; min-height: 0; border: 0; background: none; }

.display-preview-landscape .display-preview-media {
    align-self: stretch;
    width: 38%;
    flex: 0 0 auto;
}

/* ‼️ ลำดับของจอแนวตั้งจริงคือ แถบสถิติ -> แบนเนอร์เต็มขอบ (ดู #display-media-panel กับ #display-promo)
   markup เรียง ภาพ -> สถิติ อยู่แล้วเพราะแนวนอนใช้ลำดับนั้น จึงกลับด้านด้วย column-reverse
   แทนการเพิ่ม element ซ้ำ และตัดกรอบ/มุมโค้งออกให้แบนเนอร์ชิดขอบเหมือนจอจริง */
.display-preview-portrait .display-preview-media {
    width: 100%;
    flex: 0 0 auto;
    flex-direction: column-reverse;
    border: 0;
    background: none;
    border-radius: 0;
}

.display-preview-portrait .display-preview-adslot {
    margin-inline: -2cqw;
    border-radius: 0;
    background: color-mix(in srgb, currentColor 6%, transparent);
}

.display-preview-portrait .display-preview-stats {
    border-top: 0;
    border-bottom: 1px solid var(--disp-border, color-mix(in srgb, currentColor 16%, transparent));
}

.display-preview-portrait .display-preview-adslot {
    aspect-ratio: 16 / 9;
    flex: 0 0 auto;
}

/* ‼️ ตัวอย่างต้องสลับข้างตามค่า media_side ด้วย ไม่งั้นผู้ใช้ต้องกดบันทึกแล้วไปเปิดจอจริงถึงจะรู้ผล
   ใช้ row-reverse ไม่ใช่สลับลำดับใน HTML - โครง DOM ของตัวอย่างจึงเหมือนเดิมทุกกรณี */
.display-preview-landscape.display-preview-media-left .display-preview-body {
    flex-direction: row-reverse;
}

/* แนวนอน: ช่องโฆษณาอยู่ข้างเนื้อหาคิว (เหมือนคอลัมน์ขวาของจอจริง) */
.display-preview-landscape .display-preview-body {
    flex-direction: row;
    align-items: stretch;
    gap: 2cqw;
}

.display-preview-landscape .display-preview-queue {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 1.4cqh;
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
}

.display-preview-portrait .display-preview-queue {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 1.4cqh;
    flex: 1 1 auto;
    min-height: 0;
}

/* ‼️ กล่องแนวตั้งสูงกว่ากว้างเกือบ 2 เท่า ขนาดที่คิดจาก cqh (ความสูง) จึงล้นความกว้างเสมอ
   ทุกอย่างในโหมดนี้ต้องคิดจาก cqw (ความกว้าง) แทน เหมือนจอจริงที่เลขคิวถูกจำกัดด้วยความกว้างจอ */
.display-preview-portrait .display-preview-current {
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.5cqw;
    padding: 2.5cqw 2cqw;
}

.display-preview-portrait .display-preview-current-main {
    align-items: center;
    gap: 1cqw;
}

.display-preview-portrait .display-preview-label { font-size: 3.4cqw; }
.display-preview-portrait .display-preview-number { font-size: 21cqw; }
.display-preview-portrait .display-preview-counter { padding: 1.2cqw 3cqw; border-radius: 2cqw; }
.display-preview-portrait .display-preview-counter-label { font-size: 2.8cqw; }
.display-preview-portrait .display-preview-counter-no { font-size: 6cqw; }
.display-preview-portrait .display-preview-slot { font-size: 6cqw; border-radius: 1.6cqw; }
.display-preview-portrait .display-preview-shop { font-size: 5cqw; }
.display-preview-portrait .display-preview-subtitle { font-size: 3.4cqw; }
.display-preview-portrait .display-preview-clock { font-size: 3.8cqw; }
.display-preview-portrait .display-preview-logo { width: 10cqw; height: 10cqw; }
.display-preview-portrait .display-preview-stat-value { font-size: 4.4cqw; }
.display-preview-portrait .display-preview-stat-label { font-size: 2.8cqw; }
.display-preview-portrait .display-preview-badge { font-size: 2.8cqw; padding: .8cqw 2cqw; }
.display-preview-portrait .display-preview-ticker { font-size: 3cqw; }
.display-preview-portrait .display-preview-adslot { font-size: 3cqw; }

/* ‼️ แนวนอนในตัวอย่างต้องสื่อว่า "หน้าตาเดิม" - บล็อกที่เพิ่มมาในเฟสนี้จึงไม่โผล่ในตัวอย่างแนวนอนเช่นกัน */
.display-preview-landscape .display-preview-ahead,
.display-preview-landscape .display-preview-upcoming,
.display-preview-landscape .display-preview-promo {
    display: none;
}

/* ---------- แท็บเลือกจอในหน้าตั้งค่า ----------
   ‼️ จอแนวนอนกับจอแนวตั้งเป็นคนละจอกันโดยสมบูรณ์ (คนละ URL คนละค่าตั้งค่า คนละชุดภาพ)
      แท็บนี้จึงไม่ใช่แค่ "มุมมอง" แต่คือการเลือกว่ากำลังตั้งค่าจอเครื่องไหนอยู่ */
/* ‼️ อยู่ในการ์ด "ตัวอย่างหน้าจอ" ซึ่งแคบกว่าความกว้างเต็มหน้าเดิมมาก - บังคับ 2 คอลัมน์ตายตัว
   (ไม่ใช้ auto-fit เพราะ minmax 13rem จะตกลงมาซ้อนกันเป็นแถวเดียวจนดูไม่ออกว่าเป็นตัวเลือกคู่กัน) */
.display-screen-picker {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .5rem;
    margin-bottom: .85rem;
}

.display-screen-tab {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    padding: .6rem .75rem;
    text-align: left;
    border: 1px solid var(--nc-border);
    border-radius: .6rem;
    background: var(--nc-surface);
    color: inherit;
    transition: border-color .15s ease, background-color .15s ease;
}

.display-screen-tab i {
    font-size: 1.05rem;
    color: var(--bs-primary);
}

.display-screen-name { font-weight: 600; }

.display-screen-hint {
    font-size: .75rem;
    color: var(--bs-secondary-color);
    line-height: 1.3;
}

.display-screen-tab.is-active {
    border-color: var(--bs-primary);
    background: var(--bs-primary-soft);
}

.display-screen-tab:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}

/* ---------- ช่องเลือกเวลาแบบ 24 ชั่วโมง (app/Views/partials/time-select-24h.php) ----------
   ‼️ ทำไมไม่ใช้ <input type="time">: เบราว์เซอร์เลือกรูปแบบ AM/PM เองตามภาษาของ "ตัวเบราว์เซอร์"
      ไม่ใช่ lang ของหน้าเว็บ บังคับไม่ได้ จึงต้องเป็น dropdown คู่ (ผู้ใช้ทักว่าอ่าน AM/PM ยาก)
   ‼️ ปัญหาที่กฎชุดนี้แก้: ตัวควบคุมนี้อยู่ในคอลัมน์ที่ซ้อนกันสามชั้น (การ์ดครึ่งจอ -> แผงครึ่งการ์ด)
      ความกว้างที่เหลือจึงน้อยมากจนลูกศรของ .form-select กินที่จนตัวเลขโดนบีบหาย
      => คุมความกว้างขั้นต่ำของแต่ละช่อง + ลดที่ว่างซ้าย/ขวาให้พอดีตัวเลขสองหลัก */
/* ‼️ หุ้มพอดีเนื้อหา ไม่ยืดเต็มคอลัมน์ - .input-group ปกติเป็น flex กว้าง 100% */
body.page-admin .time-select-24h {
    display: inline-flex;
    width: auto;
}

body.page-admin .time-select-24h .form-select {
    /* ‼️ ซ่อนลูกศรของ select (ผู้ใช้ขอ) - Bootstrap วาดลูกศรด้วย background-image ไม่ใช่ appearance
       ของเบราว์เซอร์ จึงต้องเอา background-image ออก แล้วคืนที่ว่างขวาที่กันไว้ให้ลูกศร (2.25rem)
       กลับมาเป็นระยะปกติ กล่องจึงเล็กพอดีเลขสองหลัก */
    background-image: none;
    flex: 0 0 auto;
    width: auto;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
    text-align: center;
    /* ยังคลิกเปลี่ยนค่าได้เหมือนเดิม - ไม่มีลูกศรแล้วต้องมีสัญญาณอื่นบอกว่ากดได้ */
    cursor: pointer;
    /* ตัวเลขความกว้างเท่ากันทุกตัว เลขไม่ขยับตอนเปลี่ยนค่า (ชุดเดียวกับเลขคิวบนจอแสดงผล) */
    font-variant-numeric: tabular-nums;
}

/* ":" คั่นกลาง - เป็นตัวคั่นเชิงสายตา ไม่ใช่ช่องกรอก จึงไม่มีพื้นหลัง/ขอบซ้ายขวาให้ดูเป็นกล่องที่สาม */
body.page-admin .time-select-24h .input-group-text {
    padding-left: 0.4rem;
    padding-right: 0.4rem;
    background: transparent;
    border-left: 0;
    border-right: 0;
    font-weight: 600;
    color: var(--nc-text-muted);
}

/* แผงโหมดรอบคิวที่ "ไม่ได้ทำงานอยู่" (public/assets/app.js -> syncQueueCycleFields)
   ‼️ หรี่ "ลูกทุกตัวของแผง" ยกเว้นแถวหัวข้ออย่างเดียว (.queue-cycle-head ซึ่งมีสวิตช์อยู่ข้างใน)
      ปุ่ม "เริ่มรอบคิวใหม่" หรี่ตามแผงด้วย - ตอนเปิดอัตโนมัติมันถูก disable จริง จึงตรงกับความจริง
      ‼️ ต้องใส่ opacity ที่ "ลูก" ไม่ใช่ที่ตัวแผง - opacity ของพ่อดันกลับที่ลูกไม่ได้
         (ลูกสว่างเกินพ่อไม่ได้) ใส่ที่แผงแล้วรีเซ็ตลูกเป็น 1 จึงไม่มีทางได้ผล
      เคยหรี่ทั้งแผงด้วย .opacity-50 แล้วผู้ใช้ทักซ้ำสองรอบว่า "ดูเหมือนกดไม่ได้"
      ทั้งที่ตัวควบคุมยังใช้งานได้จริง (2026-09-10) */
body.page-admin .queue-cycle-panel-idle > *:not(.queue-cycle-head) {
    opacity: 0.55;
}

/* สวิตช์ "รอบคิวอัตโนมัติ" - ตัวตัดสินโหมดของทั้งการ์ด จึงต้องเด่นกว่าสวิตช์ทั่วไป
   (DESIGN_GUIDELINE ข้อ 2: สิ่งที่สำคัญที่สุดในหน้าต้องเด่นที่สุดด้วยขนาด/น้ำหนัก ไม่ใช่เด่นเท่ากันหมด)
   ‼️ จงใจ scope ที่ .queue-cycle-head เท่านั้น - ในระบบมี form-switch อีก 8 จุด (display-settings,
      queue-types, omc/*) ที่ใช้ค่าเริ่มต้นของ Bootstrap อยู่ ห้ามไปขยายให้เลย์เอาต์หน้าอื่นขยับ
   ‼️ ต้องล้าง padding-left/float/margin ของ .form-check ก่อน - Bootstrap จัดตำแหน่ง input ด้วย
      float + margin-left ติดลบ ถ้าเปลี่ยนเป็น flex โดยไม่ล้าง ตัวสวิตช์จะเยื้องออกนอกแถว */
body.page-admin .queue-cycle-head.form-switch {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding-left: 0;
}

body.page-admin .queue-cycle-head.form-switch .form-check-input {
    float: none;
    margin: 0;
    width: 3rem;
    height: 1.5rem;
    cursor: pointer;
    /* ตอนปิดต้องยังอ่านออกว่าเป็นตัวควบคุมที่กดได้ ไม่ใช่เส้นจาง ๆ ที่มองข้าม */
    border-color: var(--nc-text-muted);
}

body.page-admin .queue-cycle-head.form-switch .form-check-input:checked {
    border-color: var(--bs-primary);
}

body.page-admin .queue-cycle-head.form-switch .form-check-label {
    font-size: 1.05rem;
    cursor: pointer;
}

/* ---------- Phase 7 - class แทน inline style ที่เคยกระจายอยู่ในหน้า (CLAUDE.md ข้อ 7) ----------
   ‼️ ค่าคงที่ทุกค่าอยู่ที่ไฟล์นี้ที่เดียว - ที่ยังเหลือเป็น style="" ในหน้ามีเฉพาะค่าที่คำนวณตอน render
      (เปอร์เซ็นต์ของหลอดโควตา, ลำดับหน่วงของเมนู) ซึ่งเขียนเป็น CSS ล่วงหน้าไม่ได้ */

/* ไอคอนใหญ่บนหน้า error 404/500 */
.state-icon-lg {
    font-size: 3rem;
}

/* ช่องกรอกรหัสประเภทคิว - แสดงเป็นตัวพิมพ์ใหญ่ขณะพิมพ์ (ค่าจริงถูก normalize ที่ Service อีกชั้น) */
.input-uppercase {
    text-transform: uppercase;
}

/* หัวตารางรายละเอียดแบบสองคอลัมน์ (คอลัมน์ซ้าย = ชื่อฟิลด์) */
.detail-th-30 {
    width: 30%;
}

.detail-th-40 {
    width: 40%;
}

/* select ที่ต้องกว้างตามเนื้อหา ไม่ใช่เต็มความกว้างของ form-select ปกติ */
.select-auto-width {
    width: auto;
}

/* ‼️ toast ต้องอยู่เหนือ modal ของ Bootstrap (z-index 1055) ไม่งั้นข้อความแจ้งผลถูกบัง */
.toast-container-above-modal {
    z-index: 1080;
}

/* iframe สำหรับสั่งพิมพ์บัตรคิว - ต้องอยู่ใน DOM จริงแต่ไม่กินพื้นที่และไม่ถูกอ่านโดย screen reader */
.print-frame-hidden {
    position: absolute;
    width: 0;
    height: 0;
    border: 0;
    visibility: hidden;
}

/* หน้าเช็คสถานะคิวของลูกค้า - อ่านบนมือถือเป็นหลัก จึงคุมความกว้างสูงสุดไว้ */
.container-narrow-480 {
    max-width: 480px;
}

/* ภาพแนบในกล่องรายละเอียดเรื่องที่แจ้ง */
.attachment-preview-img {
    max-height: 240px;
}

/* บัตรคิวตัวอย่างบนหน้าแรก - สีของแต่ละใบเป็นงานตกแต่ง ไม่ได้มาจากข้อมูลจริง */
body.page-home .home-board-ticket-a {
    --ticket-color: #0b16b1;
}

body.page-home .home-board-ticket-b {
    --ticket-color: #af1aff;
}

body.page-home .home-board-ticket-c {
    --ticket-color: #d92d20;
}

/* คอลัมน์ขวาของคอนโซล: การ์ด "คิวกำลังเรียก" กับ "คิวรอข้าม" แบ่งความสูงคนละครึ่งตายตัว (2026-09-18)

   ‼️ flex-basis: 0 คือหัวใจ - ถ้าใช้ flex: 1 1 auto (flex-fill ของ Bootstrap) ความสูงจะยังขึ้นกับ
      เนื้อหาข้างใน การ์ดจึงขยาย/หดไปมาทุกครั้งที่จำนวนคิวเปลี่ยน ซึ่งทำให้ตาพนักงานต้องไล่หาของใหม่
      ทุก 3 วินาทีที่ poll ‼️ เจ้าของระบบสั่งให้ล็อกครึ่งต่อครึ่งไปเลย
   ‼️ min-height: 0 จำเป็นคู่กันเสมอ ไม่งั้นลิสต์ข้างในจะดันการ์ดให้สูงเกินครึ่งที่กำหนดไว้
   ‼️ ความสูงรวมของคอลัมน์ยังถูกล็อกด้วย syncDashboardColumnHeights เหมือนเดิม กฎนี้แค่แบ่งที่ว่าง
      ที่ได้มาออกเป็นสองส่วนเท่า ๆ กัน */
body.page-admin .dash-col-right > .card {
    flex: 1 1 0;
    min-height: 0;
}

/* ---------- กล่องแจ้งเรื่องถึงแอดมิน: การ์ดเลือกประเภท (ผู้ใช้ขอให้สวยขึ้น 2026-09-25) ----------
   ‼️ ใช้ทุกโซน (หลังบ้าน/หน้าสาธารณะ) จึงผูกกับตัวแปรของ Bootstrap ที่สลับตามโหมดมืดและสีร้านเองอยู่แล้ว
      ขอฟีเจอร์ใช้ --bs-link-color = สีหลักของโซนนั้น (หลังบ้าน = สีร้าน / หน้าแรก = ส้มแบรนด์ OMC)
   ‼️ ขอบบางสีเทาเท่ากันทุกใบตอนปกติ - สีประจำประเภทอยู่ที่ไอคอนอย่างเดียว ชี้/โฟกัสแล้วขอบค่อยเป็นสีนั้น */
#support-modal .modal-content {
    border: 0;
    border-radius: var(--space-4);
    overflow: hidden;
}

#support-modal .modal-header {
    padding: var(--space-4) var(--space-4) var(--space-2);
    border-bottom: 0;
}

#support-modal .modal-title {
    font-weight: 700;
}

#support-modal .modal-body {
    padding: var(--space-2) var(--space-4) var(--space-4);
}

#support-modal .support-type-lead {
    margin: 0 0 var(--space-3);
    color: var(--bs-secondary-color);
}

#support-modal .support-type-list {
    display: grid;
    gap: var(--space-2);
}

#support-modal .support-type-btn {
    --support-accent: var(--bs-secondary-color);
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    padding: var(--space-3);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--space-3);
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    text-align: left;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

#support-modal .support-type-btn-bug {
    --support-accent: var(--bs-danger);
}

#support-modal .support-type-btn-feature {
    --support-accent: var(--bs-link-color);
}

#support-modal .support-type-btn:hover,
#support-modal .support-type-btn:focus-visible {
    border-color: var(--support-accent);
    background: color-mix(in srgb, var(--support-accent) 5%, var(--bs-body-bg));
}

#support-modal .support-type-btn:focus-visible {
    outline: 2px solid var(--support-accent);
    outline-offset: 2px;
}

#support-modal .support-type-icon {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--space-3);
    background: color-mix(in srgb, var(--support-accent) 12%, var(--bs-body-bg));
    color: var(--support-accent);
    font-size: 1.25rem;
}

#support-modal .support-type-body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-width: 0;
}

#support-modal .support-type-name {
    font-weight: 600;
}

#support-modal .support-type-desc {
    font-size: 0.875rem;
    color: var(--bs-secondary-color);
}

#support-modal .support-type-arrow {
    flex: 0 0 auto;
    color: var(--bs-secondary-color);
    transition: transform 0.15s ease, color 0.15s ease;
}

#support-modal .support-type-btn:hover .support-type-arrow,
#support-modal .support-type-btn:focus-visible .support-type-arrow {
    color: var(--support-accent);
    transform: translateX(var(--space-1));
}

@media (prefers-reduced-motion: reduce) {
    #support-modal .support-type-btn,
    #support-modal .support-type-arrow {
        transition: none;
    }
}

/* ---------- มือถือจำลองขั้นผูกคิวกับ LINE (แท็บ "ลูกค้าของร้าน" หน้าแรก 2026-09-25) ----------
   ‼️ หน้าตาเลียนหน้า queue-status จริง (การ์ดขาว เลขคิวส้ม ปุ่ม LINE เขียว) + ห้องแชต LINE
   ‼️ สีเขียวของปุ่ม/หัวแชตเป็นสีแบรนด์ LINE (--line-green) ไม่ใช่สีของเรา / พื้นแชตฟ้าเทาแบบห้องแชต LINE */
body.page-home .home-phone {
    --home-phone-chat-bg: #8fa9d4;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--omc-sp-300);
}

body.page-home .home-phone-body {
    position: relative;
    width: 232px;
    aspect-ratio: 9 / 18.5;
    padding: 10px;
    border-radius: 36px;
    background: linear-gradient(160deg, #3a3a40, #18181b 40%);
    box-shadow:
        inset 0 0 0 1px rgba(255, 255, 255, 0.14),
        0 30px 50px -18px rgba(0, 0, 0, 0.7);
}

body.page-home .home-phone-notch {
    position: absolute;
    top: 18px;
    left: 50%;
    z-index: 2;
    width: 64px;
    height: 16px;
    border-radius: 999px;
    background: #0a0a0b;
    transform: translateX(-50%);
}

body.page-home .home-phone-screens {
    position: relative;
    height: 100%;
    overflow: hidden;
    border-radius: 27px;
    background: #f5f6f8;
    color: #1f2937;
}

body.page-home .home-phone-screen {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    padding: 40px 12px var(--omc-sp-200);
    opacity: 0;
    transform: translateX(14%);
    transition: opacity 0.45s var(--omc-ease), transform 0.45s var(--omc-ease);
    pointer-events: none;
}

body.page-home .home-phone[data-screen="1"] .home-phone-screen-1,
body.page-home .home-phone[data-screen="2"] .home-phone-screen-2,
body.page-home .home-phone[data-screen="3"] .home-phone-screen-3 {
    opacity: 1;
    transform: none;
}

/* หน้าจอ 1: สถานะคิว */
body.page-home .home-phone-appbar,
body.page-home .home-phone-chatbar {
    display: flex;
    align-items: center;
    gap: var(--omc-sp-75);
    font-size: 12px;
    font-weight: 700;
}

body.page-home .home-phone-logo {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: #13233f;
    color: var(--omc-brand);
    font-size: 7px;
    font-weight: 800;
}

body.page-home .home-phone-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    margin-top: 12px;
    padding: var(--omc-sp-200) var(--omc-sp-100);
    border-radius: 14px;
    background: #ffffff;
    box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
    text-align: center;
}

body.page-home .home-phone-badge {
    padding: 2px 10px;
    border-radius: 999px;
    background: #6b7280;
    color: #ffffff;
    font-size: 10px;
    font-weight: 700;
}

body.page-home .home-phone-caption {
    font-size: 10px;
    color: #6b7280;
}

body.page-home .home-phone-no {
    font-family: var(--disp-font-num, "Segoe UI", Arial, sans-serif);
    font-size: 44px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    color: var(--omc-brand);
}

body.page-home .home-phone-no-sm {
    font-size: 26px;
}

body.page-home .home-phone-info {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    margin-top: 6px;
    padding: 8px;
    border: 1px solid #a5e3ee;
    border-radius: 10px;
    background: #dff6fa;
    color: #0e5163;
    font-size: 10px;
    line-height: 1.4;
    text-align: left;
}

body.page-home .home-phone-info svg {
    flex: none;
    width: 16px;
    height: 16px;
}

body.page-home .home-phone-line-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    margin-top: 12px;
    padding: 10px;
    border-radius: 10px;
    background: var(--line-green);
    color: var(--line-on-green);
    font-size: 11px;
    font-weight: 700;
}

body.page-home .home-phone-line-hint {
    margin-top: 6px;
    font-size: 9px;
    line-height: 1.45;
    color: #6b7280;
    text-align: center;
}

body.page-home .home-phone-line-btn svg {
    width: 16px;
    height: 16px;
}

/* หน้าจอ 2: ผูกคิวสำเร็จ (วงเขียวอ่อน + เครื่องหมายถูกเข้ม แบบหน้าจริง) */
body.page-home .home-phone-done {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: #d5e8e0;
    color: #14532d;
}

body.page-home .home-phone-done svg {
    width: 26px;
    height: 26px;
}

body.page-home .home-phone-done-title {
    margin-top: 4px;
    font-size: 14px;
    font-weight: 600;
}

body.page-home .home-phone-screen-2 .home-phone-no {
    font-size: 36px;
}

body.page-home .home-phone-rows {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 4px 10px;
    width: 100%;
    margin: 8px 0 0;
    font-size: 10px;
    text-align: left;
}

body.page-home .home-phone-rows dt {
    font-weight: 400;
    color: #6b7280;
}

body.page-home .home-phone-rows dd {
    margin: 0;
}

body.page-home .home-phone-done-text {
    margin-top: 10px;
    font-size: 10px;
    line-height: 1.5;
    color: #6b7280;
    text-align: center;
}

/* หน้าจอ 3: แชต LINE */
body.page-home .home-phone-screen-3 {
    padding: 0;
    background: var(--home-phone-chat-bg);
}

body.page-home .home-phone-chatbar {
    padding: 40px 12px 10px;
    background: #ffffff;
}

body.page-home .home-phone-chat {
    display: flex;
    flex-direction: column;
    gap: var(--omc-sp-100);
    padding: 12px;
}

body.page-home .home-phone-msg {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-width: 88%;
    padding: 10px 12px;
    border-radius: 4px 16px 16px 16px;
    background: #ffffff;
    font-size: 11px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.12);
}

body.page-home .home-phone-msg-title {
    font-weight: 700;
}

body.page-home .home-phone-msg-sub {
    color: #6b7280;
}

/* การ์ด "ผูกคิวสำเร็จ" แบบข้อความ LINE จริง */
body.page-home .home-phone-msg-linked {
    max-width: 100%;
    padding: 10px 10px 8px;
    border-radius: 14px;
}

body.page-home .home-phone-msg-no {
    display: flex;
    align-items: baseline;
    gap: 4px;
    margin-top: 2px;
}

body.page-home .home-phone-msg-no small {
    color: #6b7280;
}

body.page-home .home-phone-msg-status {
    font-weight: 600;
    color: #4b5563;
}

body.page-home .home-phone-msg-btn {
    margin-top: 6px;
    padding: 6px;
    border-radius: 8px;
    background: #0f766e;
    color: #ffffff;
    font-weight: 700;
    text-align: center;
}

body.page-home .home-phone-msg-call {
    flex-direction: row;
    align-items: center;
    gap: 6px;
    font-weight: 700;
}

body.page-home .home-phone-msg-call svg {
    flex: none;
    width: 16px;
    height: 16px;
    color: var(--omc-brand-deep);
}

/* เลขขั้นใต้มือถือ */
body.page-home .home-phone-steps {
    display: flex;
    gap: var(--omc-sp-50);
    padding: var(--omc-sp-50);
    border-radius: 999px;
    background: var(--omc-surface-2);
    box-shadow: inset 0 0 0 1px var(--omc-border);
}

body.page-home .home-phone-step {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--omc-text-muted);
    font: inherit;
    font-size: var(--omc-fs-xs);
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.2s var(--omc-ease), color 0.2s var(--omc-ease);
}

body.page-home .home-phone-step span {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--omc-border);
    font-size: 10px;
}

body.page-home .home-phone-step.is-active {
    background: var(--omc-brand);
    color: var(--omc-ink);
}

body.page-home .home-phone-step.is-active span {
    background: var(--omc-ink);
    color: var(--omc-brand);
}

@media (prefers-reduced-motion: reduce) {
    body.page-home .home-phone-screen,
    body.page-home .home-phone-msg-call {
        transition: none;
    }
}

/* ---------- จำลองหน้าจอจริงในแท็บ "หน้าร้าน" (console บนโน้ตบุ๊ก) และ "เจ้าของกิจการ" (แดชบอร์ดสาขา) 2026-09-25 ----------
   ‼️ ข้อมูลตัวอย่างล้วน / ความหนาแน่นใช้สเกลไฟจราจรชุดเดียวกับแดชบอร์ดจริง (--bdash-load-* ของ page-admin) */
body.page-home .home-feat-visual {
    --home-load-1: #60a5fa;
    --home-load-2: #4ade80;
    --home-load-3: #facc15;
    --home-load-4: #fb923c;
    --home-load-5: #f87171;
    --home-mock-bg: #f5f6f8;
    --home-mock-text: #1f2937;
    --home-mock-muted: #6b7280;
    --home-mock-line: #e5e7eb;
}

/* โน้ตบุ๊ก */
body.page-home .home-laptop {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: min(100%, 480px);
}

body.page-home .home-laptop-screen {
    width: 100%;
    padding: 10px 10px 14px;
    border-radius: 14px 14px 4px 4px;
    background: linear-gradient(160deg, #3a3a40, #18181b 40%);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14);
}

body.page-home .home-laptop-base {
    width: 116%;
    height: 12px;
    border-radius: 2px 2px 14px 14px;
    background: linear-gradient(to bottom, #6b6b73, #2d2d32);
    box-shadow: 0 20px 30px -10px rgba(0, 0, 0, 0.7);
}

body.page-home .home-console {
    display: flex;
    flex-direction: column;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    border-radius: 4px;
    background: #eceef2;
    color: var(--home-mock-text);
    font-size: 7px;
    line-height: 1.3;
}

body.page-home .home-console-bar {
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 5px 8px;
    background: #ffffff;
}

body.page-home .home-console-bar .home-phone-logo {
    width: 18px;
    height: 18px;
    font-size: 5px;
}

body.page-home .home-console-brand {
    display: flex;
    flex-direction: column;
    font-weight: 700;
}

body.page-home .home-console-brand small {
    font-size: 5px;
    font-weight: 600;
    letter-spacing: 0.08em;
    color: var(--home-mock-muted);
}

body.page-home .home-console-clock {
    margin: 0 auto;
    font-size: 9px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

body.page-home .home-console-user {
    padding: 2px 6px;
    border-radius: 4px;
    background: color-mix(in srgb, var(--omc-brand) 16%, #ffffff);
    font-weight: 600;
}

body.page-home .home-console-body {
    display: grid;
    flex: 1;
    grid-template-columns: 1fr 1.6fr 1.2fr;
    gap: 6px;
    min-height: 0;
    padding: 6px;
}

body.page-home .home-console-col {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-height: 0;
}

body.page-home .home-console-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 6px;
    border-radius: 6px;
    background: #ffffff;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
}

body.page-home .home-console-col-wide,
body.page-home .home-console-col > .home-console-card:last-child {
    flex: 1;
}

body.page-home .home-console-head {
    display: flex;
    align-items: center;
    gap: 4px;
    padding-bottom: 4px;
    border-bottom: 1px solid var(--home-mock-line);
    font-weight: 700;
}

body.page-home .home-console-head em {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 11px;
    height: 11px;
    border-radius: 999px;
    background: var(--omc-brand);
    color: #ffffff;
    font-size: 6px;
    font-style: normal;
}

body.page-home .home-console-call {
    margin-left: auto;
    padding: 2px 6px;
    border-radius: 999px;
    background: var(--omc-brand);
    color: #ffffff;
    font-weight: 600;
}

body.page-home .home-console-types {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 4px;
}

body.page-home .home-console-type-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 4px 0;
    border: 1px solid var(--omc-brand);
    border-radius: 4px;
    color: var(--omc-brand);
}

body.page-home .home-console-type-btn b {
    font-size: 11px;
    line-height: 1.1;
}

body.page-home .home-console-type-btn small {
    font-size: 5px;
}

body.page-home .home-console-stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 4px;
}

body.page-home .home-console-stats > span {
    display: flex;
    flex-direction: column;
    padding: 4px;
    border: 1px solid var(--home-mock-line);
    border-radius: 4px;
}

body.page-home .home-console-stats small {
    color: var(--home-mock-muted);
    font-size: 5px;
}

body.page-home .home-console-stats b {
    font-size: 10px;
}

body.page-home .home-console-row {
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 5px 6px;
    border: 1px solid var(--home-mock-line);
    border-radius: 4px;
}

body.page-home .home-console-row b {
    font-size: 10px;
    color: var(--omc-brand-deep);
    font-variant-numeric: tabular-nums;
}

body.page-home .home-console-row small {
    margin-left: auto;
    color: var(--home-mock-muted);
}

body.page-home .home-console-check {
    width: 7px;
    height: 7px;
    border: 1px solid #c9ced6;
    border-radius: 2px;
}

body.page-home .home-console-now-item {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    padding: 6px;
    border-radius: 4px;
    background: color-mix(in srgb, var(--omc-brand) 12%, #ffffff);
}

body.page-home .home-console-now-item b {
    font-family: var(--disp-font-num, "Segoe UI", Arial, sans-serif);
    font-size: 16px;
    color: var(--omc-brand-deep);
    font-variant-numeric: tabular-nums;
}

body.page-home .home-console-now-actions {
    display: flex;
    gap: 3px;
}

body.page-home .home-console-btn {
    flex: 1;
    padding: 3px 0;
    border: 1px solid var(--home-mock-line);
    border-radius: 4px;
    text-align: center;
    font-weight: 600;
}

body.page-home .home-console-btn-main {
    border-color: var(--omc-brand);
    background: var(--omc-brand);
    color: #ffffff;
}
/* แดชบอร์ด */
body.page-home .home-dash {
    display: flex;
    flex-direction: column;
    gap: 10px;
    width: min(100%, 480px);
    padding: 14px;
    border-radius: 16px;
    background: #eef0f4;
    color: var(--home-mock-text);
    font-size: 11px;
    box-shadow: 0 30px 50px -18px rgba(0, 0, 0, 0.6);
}

body.page-home .home-dash-top {
    display: flex;
    gap: 6px;
}

body.page-home .home-dash-pill {
    padding: 4px 10px;
    border-radius: 8px;
    background: #ffffff;
    font-weight: 700;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
}

body.page-home .home-dash-pill-muted {
    margin-left: auto;
    color: var(--home-mock-muted);
}

body.page-home .home-dash-stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 6px;
}

body.page-home .home-dash-stat {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 8px 10px;
    border-radius: 10px;
    background: #ffffff;
}

body.page-home .home-dash-stat small {
    color: var(--home-mock-muted);
}

body.page-home .home-dash-stat b {
    font-size: 20px;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}

body.page-home .home-dash-stat em {
    font-style: normal;
    color: var(--home-mock-muted);
}

body.page-home .home-dash-stat em.is-up {
    color: #15803d;
    font-weight: 700;
}

body.page-home .home-dash-chart {
    padding: 10px;
    border-radius: 10px;
    background: #ffffff;
}

body.page-home .home-dash-chart-title {
    font-weight: 700;
}

body.page-home .home-dash-bars {
    display: flex;
    align-items: flex-end;
    gap: 6px;
    height: 110px;
    margin-top: 8px;
    border-bottom: 1px solid var(--home-mock-line);
}

body.page-home .home-dash-col {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    height: 100%;
    gap: 4px;
}

body.page-home .home-dash-col small {
    color: var(--home-mock-muted);
    font-size: 9px;
    transform: translateY(16px);
}

body.page-home .home-dash-bar {
    width: 100%;
    border-radius: 3px 3px 0 0;
    background: var(--home-load-1);
    transform-origin: bottom;
    animation: home-dash-grow 0.8s var(--omc-ease) both;
}

body.page-home .home-dash-col:nth-child(1) .home-dash-bar { height: 14%; }
body.page-home .home-dash-col:nth-child(2) .home-dash-bar { height: 92%; background: var(--home-load-5); }
body.page-home .home-dash-col:nth-child(3) .home-dash-bar { height: 66%; background: var(--home-load-4); }
body.page-home .home-dash-col:nth-child(4) .home-dash-bar { height: 5%; }
body.page-home .home-dash-col:nth-child(5) .home-dash-bar { height: 10%; }
body.page-home .home-dash-col:nth-child(6) .home-dash-bar { height: 20%; }
body.page-home .home-dash-col:nth-child(7) .home-dash-bar { height: 26%; background: var(--home-load-2); }
body.page-home .home-dash-col:nth-child(8) .home-dash-bar { height: 10%; }
body.page-home .home-dash-col:nth-child(9) .home-dash-bar { height: 44%; background: var(--home-load-3); }

body.page-home .home-dash-chart {
    padding-bottom: 22px;
}

@keyframes home-dash-grow {
    from {
        transform: scaleY(0);
    }
}

@media (prefers-reduced-motion: reduce) {
    body.page-home .home-dash-bar {
        animation: none;
    }
}

/* ---------- section มือถือ 3 ขั้น (2026-09-25 ผู้ใช้สั่ง: ไม่อยู่ในกล่อง ขั้นละแถวสลับซ้าย-ขวา มี animation) ----------
   ‼️ มือถืออยู่ข้างข้อความเสมอ ไม่ทับหัวข้อ / จอแคบเรียงมือถือบน-ข้อความล่าง
   ‼️ ซ่อนรอ animation เฉพาะเมื่อ JS ใส่ .is-animated แล้ว (public.js initHomePhone) */
body.page-home .home-phone-section .home-section-head {
    margin-inline: auto;
    text-align: center;
}

body.page-home .home-phone-rows-list {
    display: grid;
    gap: var(--omc-sp-700);
    margin: 0;
    padding: 0;
    list-style: none;
}

body.page-home .home-phone-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
    gap: var(--omc-sp-700);
}

body.page-home .home-phone-stage {
    position: relative;
    display: flex;
    justify-content: center;
    isolation: isolate;
}

/* แสงส้มนุ่มหลังมือถือแทนกล่องพื้นเข้ม */
body.page-home .home-phone-stage::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    z-index: -1;
    width: 380px;
    height: 380px;
    border-radius: 50%;
    background: radial-gradient(closest-side, color-mix(in srgb, var(--omc-brand) 26%, transparent), transparent);
    transform: translate(-50%, -50%);
    pointer-events: none;
}

body.page-home .home-phone-section .home-phone-body {
    width: 250px;
}

/* แถวคู่: มือถืออยู่ขวา ข้อความอยู่ซ้าย */
body.page-home .home-phone-row:nth-child(even) .home-phone-stage {
    order: 2;
}

body.page-home .home-phone-row-copy {
    max-width: 30rem;
}

body.page-home .home-phone-row:nth-child(odd) .home-phone-row-copy {
    justify-self: start;
}

body.page-home .home-phone-row:nth-child(even) .home-phone-row-copy {
    justify-self: end;
}

body.page-home .home-phone-row-no {
    display: block;
    font-family: var(--disp-font-num, "Segoe UI", Arial, sans-serif);
    font-size: var(--omc-fs-6xl);
    line-height: 1;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    color: var(--omc-brand);
}

body.page-home .home-phone-story-title {
    margin: var(--omc-sp-200) 0 0;
    font-size: var(--omc-fs-2xl);
    line-height: var(--omc-lh-2xl);
    font-weight: 700;
    color: var(--omc-text);
}

body.page-home .home-phone-story-text {
    margin: var(--omc-sp-100) 0 0;
    font-size: var(--omc-fs-lg);
    line-height: 1.7;
    color: var(--omc-text-muted);
}

/* ---------- animation ตอนเลื่อนถึง ---------- */
body.page-home .home-phone-section.is-animated .home-phone-stage,
body.page-home .home-phone-section.is-animated .home-phone-row-copy {
    opacity: 0;
    transition: opacity 0.9s var(--omc-ease), transform 0.8s var(--omc-ease);
}

/* มือถือค่อย ๆ ปรากฏ (fade) อยู่กับที่ ไม่เลื่อนเข้าจากด้านข้าง - ผู้ใช้สั่ง 2026-09-25 */
body.page-home .home-phone-section.is-animated .home-phone-row-copy {
    transform: translateY(24px);
    transition-delay: 0.15s;
}

body.page-home .home-phone-section.is-animated .home-phone-row.is-in .home-phone-stage,
body.page-home .home-phone-section.is-animated .home-phone-row.is-in .home-phone-row-copy {
    opacity: 1;
    transform: none;
}

/* ข้อความในแชต LINE เด้งขึ้นทีละข้อความหลังมือถือเข้าที่ */
body.page-home .home-phone-section.is-animated .home-phone-msg {
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 0.4s var(--omc-ease), transform 0.4s var(--omc-ease);
}

body.page-home .home-phone-section.is-animated .home-phone-msg-linked { transition-delay: 0.7s; }
body.page-home .home-phone-section.is-animated .home-phone-msg-near { transition-delay: 1.5s; }
body.page-home .home-phone-section.is-animated .home-phone-msg-call { transition-delay: 2.3s; }

body.page-home .home-phone-section.is-animated .home-phone-row.is-in .home-phone-msg {
    opacity: 1;
    transform: none;
}

@media (max-width: 767.98px) {
    body.page-home .home-phone-row {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--omc-sp-300);
        justify-items: center;
        text-align: center;
    }

    body.page-home .home-phone-row:nth-child(even) .home-phone-stage {
        order: 0;
    }

    body.page-home .home-phone-row .home-phone-row-copy {
        justify-self: center;
    }

    body.page-home .home-phone-section .home-phone-body {
        width: 220px;
    }
}
/* ---------- แท็บความสามารถ: ไม่มีกล่องพื้นเข้มครอบภาพ + ล็อก layout (ผู้ใช้สั่ง 2026-09-25) ----------
   ‼️ ทุกแท็บซ้อนอยู่ในช่อง grid เดียวกัน แท็บที่ไม่ได้เลือกแค่มองไม่เห็น (visibility) ไม่ใช่ display: none
      ความสูงของส่วนนี้จึง = แท็บที่สูงที่สุดเสมอ กดสลับแท็บแล้วเนื้อหาด้านล่างไม่ขยับขึ้นลง
   ‼️ ภาพประกอบสูงคงที่ + รายการชิดบน - เปิดคำอธิบายข้อไหนตัวภาพก็ไม่เลื่อน */
body.page-home .home-feat {
    display: grid;
}

body.page-home .home-feat-panel {
    grid-row: 2;
    grid-column: 1;
    align-items: start;
}

body.page-home .home-feat.is-ready .home-feat-panel[hidden] {
    display: grid;
    visibility: hidden;
}

body.page-home .home-feat-visual {
    /* สูงพอสำหรับมือถือจำลองของแท็บลูกค้า (สูงสุดในสามแท็บ) */
    height: 500px;
    min-height: 0;
    padding: var(--omc-sp-200);
    background: none;
    color: var(--omc-text);
    overflow: visible;
}

/* แสงส้มนุ่มหลังภาพแทนกล่อง (เหมือน section มือถือ) */
body.page-home .home-feat-visual::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    z-index: -1;
    width: 80%;
    height: 90%;
    border-radius: 50%;
    background: radial-gradient(closest-side, color-mix(in srgb, var(--omc-brand) 22%, transparent), transparent);
    transform: translate(-50%, -50%);
    pointer-events: none;
}

body.page-home .home-feat-panel {
    isolation: isolate;
}

/* ภาพเดิมของแท็บลูกค้า (มือถือโครงเส้น) ออกแบบไว้บนพื้นเข้ม - ให้ตัวมือถือมีพื้นเข้มของตัวเองแทน */
body.page-home .home-feat-phone {
    border-color: #2a2a30;
    background: var(--omc-ink);
    color: var(--omc-on-ink);
    box-shadow: 0 30px 50px -18px rgba(0, 0, 0, 0.45);
}

@media (max-width: 1024px) {
    body.page-home .home-feat-visual {
        height: 320px;
    }
}

/* ‼️ มือถือเลื่อนเข้ามาจากนอกคอลัมน์ (translateX) และแสงหลังภาพกว้างเกินคอลัมน์ - ตัดส่วนเกินแนวนอนไว้ ไม่งั้นหน้าเลื่อนซ้าย-ขวาได้ */
/* ‼️ ตัดที่ .home-page (กว้างเท่าจอ) ไม่ใช่ที่ตัว section - ตัดที่ section จะตัดแถบพื้นเต็มจอของ section ทิ้งด้วย */
body.page-home .home-page {
    overflow-x: clip;
}

/* ---------- สามชั้น: คำอธิบายในกล่อง (2026-09-25) ---------- */
body.page-home .home-tier {
    align-items: flex-start;
}

body.page-home .home-tier-text {
    display: block;
    margin-top: var(--omc-sp-100);
    font-size: var(--omc-fs-sm);
    line-height: 1.65;
    color: var(--omc-text-muted);
}
/* ---------- กล่องปิดท้าย "เริ่มจากคิวของพรุ่งนี้เช้า" โทนเดียวกับ hero / หัวหน้าราคา (ผู้ใช้สั่ง 2026-09-25) ----------
   น้ำตาลเข้ม #241a10 + ดวงแสงส้มนุ่มจากมุม แทนกรมท่าที่หลุดธีม / หัวเรื่องขาวล้วน ระยะบรรทัดพอสำหรับสระไทย */
body.page-home .home-final {
    background: color-mix(in srgb, var(--home-hero-bg) 80%, var(--omc-brand-deep));
}

body.page-home .home-final-inner {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    background: var(--home-hero-bg);
}

body.page-home .home-final-inner::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
        radial-gradient(40% 80% at 0% 0%, color-mix(in srgb, var(--omc-brand) 26%, transparent), transparent 70%),
        radial-gradient(45% 90% at 100% 100%, color-mix(in srgb, var(--omc-brand-deep) 45%, transparent), transparent 70%);
    pointer-events: none;
}

body.page-home .home-final-title {
    line-height: 1.35;
    letter-spacing: 0;
    background-image: none;
    color: var(--omc-on-ink);
}

body.page-home .home-final-lead,
body.page-home .home-final-promise {
    color: var(--home-hero-muted);
}

/* ---------- แท็บความสามารถ: สีตามธีมหน้าแรก (ผู้ใช้สั่ง 2026-09-25) ----------
   แท็บที่เลือก = น้ำตาลเข้มของ hero + ไอคอนส้ม (แทนกรมท่า) / แถบแท็บพื้นเทาอุ่นอ่อน */
body.page-home .home-feat-tabs {
    background: color-mix(in srgb, var(--omc-brand) 6%, var(--omc-surface));
    border-color: color-mix(in srgb, var(--omc-brand) 22%, var(--omc-border));
}

body.page-home .home-feat-tab[aria-selected="true"] {
    background: var(--home-hero-bg);
    color: var(--omc-on-ink);
    box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--home-hero-bg) 60%, transparent);
}

body.page-home .home-feat-tab[aria-selected="true"] svg {
    color: var(--omc-brand);
}

body.page-home .home-feat-tab:not([aria-selected="true"]):hover {
    background: color-mix(in srgb, var(--omc-brand) 10%, transparent);
    color: var(--omc-brand-text);
}

/* หัวข้อที่เปิดอยู่ในรายการ: ไอคอนพื้นส้ม */
body.page-home .home-feat-item.is-open .home-feature-icon {
    background: var(--omc-brand);
    color: var(--omc-ink);
    box-shadow: none;
}

body.page-home .home-feat-mini-phone .home-phone-body {
    width: 230px;
}

/* ---------- ปุ่มใน hero เด่นขึ้น (ผู้ใช้สั่ง 2026-09-25) ----------
   ใหญ่ขึ้นหนึ่งขั้น + ปุ่มหลักมีแสงส้มรอบตัว ยกขึ้นเล็กน้อยตอนชี้ / ปุ่มรองขอบขาวชัดขึ้น
   ตัวอักษรปุ่มหลักใช้น้ำตาลเข้มของ hero (เข้ากับธีม คอนทราสต์บนส้มแบรนด์เกิน 7) */
body.page-home .home-hero .home-btn-primary,
body.page-home .home-hero .home-btn-ghost {
    padding: var(--omc-sp-200) var(--omc-sp-400);
    border-radius: var(--omc-r-xl);
    font-size: var(--omc-fs-lg);
    line-height: var(--omc-lh-lg);
    font-weight: 700;
    transition: background-color 250ms var(--omc-ease), color 250ms var(--omc-ease), border-color 250ms var(--omc-ease), transform 250ms var(--omc-ease), box-shadow 250ms var(--omc-ease);
}

body.page-home .home-hero .home-btn-primary {
    color: var(--home-hero-bg);
    box-shadow:
        0 0 0 1px color-mix(in srgb, var(--omc-brand) 40%, transparent),
        0 10px 30px -6px color-mix(in srgb, var(--omc-brand) 65%, transparent);
}

body.page-home .home-hero .home-btn-primary:hover,
body.page-home .home-hero .home-btn-primary:focus-visible {
    color: var(--home-hero-bg);
    transform: translateY(-2px);
    box-shadow:
        0 0 0 4px color-mix(in srgb, var(--omc-brand) 25%, transparent),
        0 16px 36px -8px color-mix(in srgb, var(--omc-brand) 80%, transparent);
}

body.page-home .home-hero .home-btn-ghost {
    border: 1.5px solid color-mix(in srgb, var(--omc-on-ink) 55%, transparent);
    background: color-mix(in srgb, var(--omc-on-ink) 6%, transparent);
    color: var(--omc-on-ink);
}

body.page-home .home-hero .home-btn-ghost:hover,
body.page-home .home-hero .home-btn-ghost:focus-visible {
    border-color: var(--omc-on-ink);
    background: color-mix(in srgb, var(--omc-on-ink) 14%, transparent);
    color: var(--omc-on-ink);
    transform: translateY(-2px);
}

body.page-home .home-hero .home-actions {
    gap: var(--omc-sp-200);
}

@media (prefers-reduced-motion: reduce) {
    body.page-home .home-hero .home-btn-primary:hover,
    body.page-home .home-hero .home-btn-primary:focus-visible,
    body.page-home .home-hero .home-btn-ghost:hover,
    body.page-home .home-hero .home-btn-ghost:focus-visible {
        transform: none;
    }
}

/* ---------- ระยะห่างระหว่างหัวข้อ (ผู้ใช้ทักว่าติดกัน 2026-09-25) ----------
   เดิม 96px ทุกหัวข้อ แต่ภาพประกอบใหญ่ + แสงนุ่มรอบภาพทำให้ดูชนกับหัวข้อถัดไป
   -> 144px บนจอกว้าง / หัวข้อกับเนื้อหาห่างขึ้นอีกขั้น / จอเล็กคงระยะเดิมที่ตั้งไว้ใน media ด้านบน */
@media (min-width: 769px) {
    /* ‼️ ยกเว้น .home-section-first (หน้าราคา/ติดต่อเรา) - ใช้ margin ติดลบให้การ์ดขึ้นไปทับขอบล่างของหัวหน้า */
    body.page-home .home-section:not(.home-section-first) {
        margin-top: calc(var(--omc-sp-900) * 1.5);
    }

    body.page-home .home-section-head {
        margin-bottom: var(--omc-sp-700);
    }
}

/* กล่องสามชั้น: ชี้แล้วขอบเป็นสีแบรนด์ แบบเดียวกับการ์ด "สิ่งที่ร้านต้องเตรียม" (ผู้ใช้สั่ง 2026-09-25) */
body.page-home .home-tier {
    transition: border-color 0.2s var(--omc-ease);
}

body.page-home .home-tier:hover {
    border-color: var(--omc-brand-text);
}

/* ---------- พื้นหลังหน้าแรกไม่ขาวโล่ง (ผู้ใช้สั่ง 2026-09-25) ----------
   1) ลายจุดจาง ๆ ทั้งหน้า (เลื่อนไปตามเนื้อหา) + ดวงแสงส้มนุ่มเป็นระยะด้านข้าง
   2) หัวข้อสลับกันเป็นแถบพื้นอุ่นเต็มความกว้างจอ (ขั้นตอนมือถือ / สามชั้น / คำถาม) ให้มีจังหวะตอนเลื่อน
   ‼️ ตกแต่งล้วนอยู่หลังเนื้อหา (z-index -1 ใต้ isolation) / ความกว้างแถบใช้ 100cqw ของ body ไม่ใช่ 100vw (กัน scrollbar ล้น) */
body.page-home .home-page {
    position: relative;
    isolation: isolate;
    background-image:
        radial-gradient(color-mix(in srgb, var(--omc-text) 7%, transparent) 1px, transparent 1.4px),
        radial-gradient(38% 1200px at 100% 1800px, color-mix(in srgb, var(--omc-brand) 9%, transparent), transparent 70%),
        radial-gradient(38% 1200px at 0% 3600px, color-mix(in srgb, var(--omc-brand) 8%, transparent), transparent 70%),
        radial-gradient(38% 1200px at 100% 5600px, color-mix(in srgb, var(--omc-brand) 8%, transparent), transparent 70%);
    background-size: 24px 24px, 100% 100%, 100% 100%, 100% 100%;
    background-repeat: repeat, no-repeat, no-repeat, no-repeat;
}

body.page-home #home-phone-flow,
body.page-home #home-layers,
body.page-home #home-faq {
    position: relative;
    isolation: isolate;
    padding-block: var(--omc-sp-900);
}

body.page-home #home-phone-flow::before,
body.page-home #home-layers::before,
body.page-home #home-faq::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(50% - 50cqw);
    z-index: -1;
    width: 100cqw;
    background:
        radial-gradient(50% 60% at 50% 0%, color-mix(in srgb, var(--omc-brand) 10%, transparent), transparent 70%),
        color-mix(in srgb, var(--omc-brand) 5%, var(--omc-page));
    border-block: 1px solid color-mix(in srgb, var(--omc-brand) 14%, var(--omc-border));
    pointer-events: none;
}

/* แถบพื้นอุ่น: การ์ดขาวข้างในจะลอยเด่นขึ้น */
body.page-home #home-layers .home-tier,
body.page-home #home-faq details {
    background: var(--omc-surface);
}

@media (max-width: 768px) {
    body.page-home #home-phone-flow,
    body.page-home #home-layers,
    body.page-home #home-faq {
        padding-block: var(--omc-sp-700);
    }
}

/* ระยะจากเนื้อหาสุดท้ายถึง footer (ผู้ใช้ทักว่าห่างเกิน 2026-09-25)
   เดิม padding ล่างของหน้า 64 + margin บน footer 96 = 160px ก่อนเส้นคั่น -> เหลือแค่ padding ล่างของหน้า */
body.page-home .home-footer {
    margin-top: 0;
}

/* หน้าราคา/ติดต่อเรา: ส่วนถัดจากการ์ดแรกอยู่ชิดขึ้น (ผู้ใช้สั่ง 2026-09-25)
   ระยะ 144px ระหว่างหัวข้อตั้งไว้สำหรับหน้าแรกที่มีภาพใหญ่ - สองหน้านี้เป็นเนื้อหาต่อเนื่องชุดเดียว */
body.page-home .home-section-first ~ .home-section {
    margin-top: var(--omc-sp-500);
}
