/* ─── OMC Support — Design System ────────────────────────────────────────
   ‼️ ที่เดียวของทั้งระบบที่เก็บสี ระยะห่าง และขนาด (กฎเหล็กข้อ 8)
      ห้าม hardcode ค่าดิบในหน้า ห้ามมี <style> ในหน้า ห้ามมีไฟล์ CSS แยกต่อหน้า

   ธีม: ม่วงเป็นสีหลัก · ส้ม OMC เป็น accent เท่านั้น (ห้ามใช้ส้มเป็นปุ่มหลัก)
   ‼️ ห้ามใส่ @import จากโดเมนภายนอก — CSP ของระบบคือ style-src 'self'; font-src 'self'
      ฟอนต์ภายนอกจะถูกบล็อกและขึ้น error ใน console เสมอ จึงใช้ฟอนต์ระบบที่รองรับไทยแทน
   ──────────────────────────────────────────────────────────────────────── */

:root {
  /* ‼️ บอกเบราว์เซอร์ให้วาดแถบเลื่อน/ช่องกรอกของตัวเองตามโหมด — ถ้าไม่ตั้ง
     จะเหลือแถบเลื่อนขาวคาดอยู่กลางหน้ามืด (theme.js ตั้งซ้ำอีกชั้นให้ตรงกัน) */
  color-scheme: light;
  /* ── พื้นผิว ───────────────────────────────────────────────────────── */
  --bg:        #f4f6fa;
  --surface:   #ffffff;
  --surface2:  #f8f9fc;
  --surface3:  #f1f2f7;
  --border:    #e7e9f0;
  --hairline:  #eff1f6;
  --text:      #16233c;
  --muted:     #4d5b73;
  /* ‼️ ค่าไม่ได้เลือกจาก "บนการ์ดขาว" อย่างเดียว — token นี้ถูกวางบน surface2/surface3
     และพื้นม่วงอ่อนด้วย ‼️ ต้องผ่าน 4.5 บนพื้นที่เข้มที่สุดที่มันไปอยู่จริง */
  --faint:     #616c7a;
  --on-solid:  #ffffff;

  /* ── แบรนด์: ม่วงหลัก ─────────────────────────────────────────────── */
  --primary:       #6c3ce9;
  --primary-hover: #5b2fd1;
  --primary-press: #4c26b3;
  --primary-soft:  #f1ecfe;
  --primary-soft2: #e6dcfd;
  --primary-text:  #5a2fc9;

  /* ── แบรนด์: ส้ม OMC (accent เท่านั้น) ────────────────────────────── */
  --accent:       #f59e0b;
  --accent-soft:  #fef4e6;
  --accent-text:  #b45309;

  /* ‼️ สีของตราสัญลักษณ์ OMC — ‼️ ค่าวัดจาก **ไฟล์โลโก้จริงของบริษัท**
        (#faa61a คือ 66.76% ของพิกเซลทั้งภาพ) ไม่ใช่ค่าที่เลือกเอง
        ‼️ เป็น "สีของแบรนด์" ไม่ใช่สีของธีม ห้ามเปลี่ยนตามธีมและห้ามเอาไปใช้กับอย่างอื่น
        ‼️ ชุดเดิมเป็นไล่สี 3 ขั้น (#fb923c → #f97316 → #c2410c) พอร์ตจาก favicon ของ
           ระบบเก่า ‼️ โลโก้จริงเป็นสีทึบสีเดียว ไม่มีไล่สีและไม่มีแสงสะท้อน จึงยุบเหลือค่าเดียว */
  --omc-logo:      #faa61a;
  --omc-logo-ink:  #ffffff;


  /**
   * ‼️‼️ สีของหน้าเข้าสู่ระบบ — ‼️ เป็น "สีของหน้านั้น" ไม่ใช่สีของธีม
   *
   * ‼️ หน้าเข้าสู่ระบบทำตามภาพ target UI ที่เจ้าของงานกำหนด ซึ่งเป็นงานพื้นสว่างล้วน
   *    ‼️ จึงตรึงเป็นชุดเดียวทั้งโหมดสว่างและมืด แนวเดียวกับ --omc-logo-* ด้านบน
   *    ‼️ ห้ามเอา token กลุ่มนี้ไปใช้กับหน้าอื่น และห้ามให้หน้าอื่นมาพึ่งพา
   */
  --lg-orange:       #f7761f;
  --lg-orange-deep:  #e2620d;
  --lg-orange-soft:  #fef3ea;
  --lg-navy:         #14274a;
  --lg-navy-soft:    #33456a;
  --lg-line:         #e8ebf1;
  --lg-surface:      #ffffff;
  --lg-field:        #f8f9fc;
  --lg-field-hover:  #f1f2f7;
  --lg-on-solid:     #ffffff;
  --lg-faint:        #616c7a;

  /* แสงครีมหลังโลโก้ฝั่งซ้าย */
  --lg-glow-a:       #fdece0;
  --lg-glow-b:       #fdf4ec;

  /* ‼️ แถบภาพออฟฟิศฝั่งขวา — ไม่มีไฟล์รูปจริง จึงประกอบจาก gradient ล้วน */
  --lg-photo-wood:   #cfa87e;
  --lg-photo-wood-0: rgba(207, 168, 126, 0);
  --lg-photo-light:  #eef3f7;
  --lg-photo-light-0: rgba(238, 243, 247, 0);
  --lg-photo-1:      #93a7bb;
  --lg-photo-2:      #7d94ac;
  --lg-photo-3:      #9db0c2;
  --lg-photo-4:      #c9b295;
  --lg-white-0:      rgba(255, 255, 255, 0);
  --lg-white-96:     rgba(255, 255, 255, 0.96);

  /* เงาและวงแหวนโฟกัส */
  --lg-card-shadow:  0 26px 60px rgba(20, 39, 74, 0.14), 0 3px 10px rgba(20, 39, 74, 0.05);
  --lg-btn-shadow:   0 8px 18px rgba(247, 118, 31, 0.28);
  --lg-focus-ring:   0 0 0 3px rgba(247, 118, 31, 0.16);
  --lg-pillar-shadow: 0 1px 10px rgba(20, 39, 74, 0.42);

  /* กล่องไอคอนจุดขาย 4 ช่อง — ‼️ สีตกแต่ง ห้ามใช้สื่อความหมายสถานะ */
  --lg-ic-orange-bg: #fdece0;  --lg-ic-orange-fg: #d9600c;
  --lg-ic-violet-bg: #eee9fd;  --lg-ic-violet-fg: #6c3ce9;
  --lg-ic-blue-bg:   #e5eefc;  --lg-ic-blue-fg:   #2f6fd0;
  --lg-ic-green-bg:  #e4f5ec;  --lg-ic-green-fg:  #10875a;

  /* ── สถานะ ────────────────────────────────────────────────────────── */
  --success:      #0f9d63;
  --success-soft: #e8f7f0;
  --success-text: #0a7c4e;
  --warning:      #d97706;
  --warning-soft: #fef4e6;
  --warning-text: #b45309;
  --danger:       #d43b30;
  --danger-soft:  #fdeeed;
  --danger-text:  #c0392e;
  --danger-hover: #bf332a;
  --info:         #3b7de8;
  --info-soft:    #ebf2fe;
  --info-text:    #2c5fbd;
  --neutral-soft: #f1f2f7;
  --neutral-text: #616b80;

  /* ── ระยะห่าง — ใช้ token เท่านั้น ห้ามใส่ px ดิบในหน้า ───────────── */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;

  /* ── มุมโค้ง ──────────────────────────────────────────────────────── */
  --radius-xs: 6px;
  --radius-sm: 8px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
  --radius-pill: 999px;

  /* ── เงา ──────────────────────────────────────────────────────────── */
  --shadow-xs: 0 1px 2px rgba(16, 24, 40, .04);
  --shadow-sm: 0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .04);
  --shadow-md: 0 2px 4px rgba(16, 24, 40, .04), 0 8px 20px rgba(16, 24, 40, .07);
  --shadow-lg: 0 4px 8px rgba(16, 24, 40, .05), 0 20px 48px rgba(16, 24, 40, .12);
  --ring:      0 0 0 3px rgba(108, 60, 233, .22);
  --ring-danger: 0 0 0 3px rgba(212, 59, 48, .24);

  /* ── ขนาดโครง ─────────────────────────────────────────────────────── */
  --sidebar-w:      272px;
  --sidebar-w-mini: 76px;
  --topbar-h:       72px;
  --input-h:        42px;
  --btn-h:          42px;
  --btn-h-sm:       34px;
  --btn-h-lg:       48px;
  --icon:           20px;
  --content-max:    1440px;

  /* ── ตัวอักษร ─────────────────────────────────────────────────────── */
  /* ‼️ ฟอนต์ระบบล้วน — CSP บล็อกฟอนต์ภายนอก · เรียงให้ไทยอ่านชัดทุก OS */
  /* ‼️ 'OMC Thai' ต้องอยู่หน้าสุด — มันจองเฉพาะช่วงอักษรไทย (ดู @font-face ท้ายไฟล์)
     ‼️ อักษรละตินไม่เข้าเงื่อนไขนั้น จึงไหลไปใช้ Inter/Segoe ตามเดิมทุกประการ */
  --font: 'OMC Thai', 'Inter', 'Segoe UI Variable Text', 'Segoe UI', system-ui,
          -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'Cascadia Mono', 'Consolas', 'IBM Plex Mono', ui-monospace, monospace;

  /* ‼️ ไม่มีขนาดไหนต่ำกว่า 13px และข้อความใช้งานจริงไม่ต่ำกว่า 14px
     ‼️ เดิม xs=12 sm=13 ถูกใช้รวมกัน 56 จุด ซึ่งเป็นข้อความส่วนใหญ่ของทั้งระบบ */
  --fs-xs:  13px;
  --fs-sm:  14px;
  --fs-md:  15px;
  --fs-lg:  16px;
  --fs-xl:  20px;
  --fs-2xl: 26px;
  --fs-3xl: 32px;

  --fw-normal: 400;
  --fw-medium: 500;
  --fw-semi:   600;
  --fw-bold:   700;

  /* ‼️ ไทยมีสระบนและสระล่างซ้อนกันได้ถึงสองชั้น — ระยะบรรทัดของละตินทำให้เบียดกัน */
  --lh-tight: 1.35;
  --lh-base:  1.7;

  --z-sticky:  20;
  --z-drawer:  60;
  --z-dropdown: 70;
  --z-modal:   80;
  --z-toast:   90;

  --transition: 140ms ease;
}

/* ═══════════════════════════════════════════════════════════════════════
   ‼️ โหมดสว่าง / มืด — token-first
   ═══════════════════════════════════════════════════════════════════════

   ‼️‼️ กติกาที่ห้ามละเมิด:
      1. ทุกสีที่หน้าใช้ต้องมาจาก token ‼️ ห้ามเขียนค่าดิบใน selector ใดๆ
         (กฎเหล็กข้อ 8) — ค่าดิบจะไม่เปลี่ยนตามโหมด แล้วกลายเป็นจุดขาววาบกลางหน้ามืด
      2. ‼️ ค่าสีของโหมดมืดอยู่ใน `--d-*` **ที่เดียว** ข้างล่างนี้
         สองบล็อกที่เปิดใช้งาน (ตามอุปกรณ์ / ผู้ใช้เลือกเอง) แค่ "ชี้" ไปที่ชุดเดียวกัน
         ‼️ ที่มักพลาดคือก็อปค่าสีไปวางสองรอบ แล้ววันหนึ่งแก้ที่เดียว — ผลคือคนที่
            เลือกโหมดมืดเองเห็นคนละสีกับคนที่ตั้งตามอุปกรณ์ ซึ่งหาสาเหตุยากมาก
         ‼️ ThemeContractTest คุมไว้ว่าสองบล็อกต้องกำหนดชื่อ token ชุดเดียวกันเป๊ะ

   ‼️ "ตามอุปกรณ์" = ไม่มี attribute `data-theme` บน <html> เลย
      จึงต้องเขียนเงื่อนไขเป็น :root:not([data-theme="light"]) ‼️ ไม่ใช่ :root เปล่า
      ไม่งั้นคนที่ตั้งใจเลือก "สว่าง" บนเครื่องที่ตั้งมืด จะโดนโหมดมืดทับ
   ─────────────────────────────────────────────────────────────────────── */

:root {
  /* ── จานสีโหมดมืด — ‼️ ที่เดียวของทั้งระบบ ────────────────────────────
     ‼️ ไม่ใช่การกลับขาว-ดำ: พื้นมืดอมน้ำเงินม่วงอ่อนๆ เพื่อให้ม่วงของแบรนด์ยัง
        อ่านเป็นสีเดียวกับโหมดสว่าง ‼️ พื้นเทากลางล้วนทำให้ม่วงดูอมชมพู
     ‼️ ม่วงในโหมดมืดต้อง **สว่างกว่า** โหมดสว่าง — #6c3ce9 บนพื้นมืดได้ contrast
        ราว 3:1 ซึ่งตกมาตรฐานสำหรับตัวอักษร */
  --d-bg:        #0f1117;
  --d-surface:   #171a23;
  --d-surface2:  #1d2029;
  --d-surface3:  #242833;
  --d-border:    #2a2f3d;
  --d-hairline:  #21252f;
  --d-text:      #e8eaf0;
  --d-muted:     #a9b3c6;
  --d-faint:     #8b95a8;
  --d-on-solid:  #ffffff;

  --d-primary:       #7c4ded;
  --d-primary-hover: #8355ef;
  --d-primary-press: #6d3ae4;
  --d-primary-soft:  #241d3d;
  --d-primary-soft2: #2f2551;
  --d-primary-text:  #b39cfa;

  --d-accent:      #f5a623;
  --d-accent-soft: #2b2113;
  --d-accent-text: #f8c165;

  --d-success:      #22b37c;
  --d-success-soft: #102620;
  --d-success-text: #4ade9f;
  --d-warning:      #e2932a;
  --d-warning-soft: #2b2113;
  --d-warning-text: #f7bd63;
  --d-danger:       #cf3a2d;
  --d-danger-soft:  #2d1715;
  --d-danger-text:  #ff8f85;
  --d-danger-hover: #b93125;
  --d-info:         #5b95f0;
  --d-info-soft:    #14203a;
  --d-info-text:    #8ab4f8;
  --d-neutral-soft: #242833;
  --d-neutral-text: #9aa3b6;

  --d-border-strong: #3a4152;

  /* ‼️ เงาบนพื้นมืดแทบไม่เห็น — ต้องเข้มขึ้นและพึ่งเส้นขอบมากขึ้น */
  --d-shadow-xs: 0 1px 2px rgba(0, 0, 0, .40);
  --d-shadow-sm: 0 1px 2px rgba(0, 0, 0, .44), 0 1px 3px rgba(0, 0, 0, .36);
  --d-shadow-md: 0 2px 4px rgba(0, 0, 0, .40), 0 8px 20px rgba(0, 0, 0, .46);
  --d-shadow-lg: 0 4px 8px rgba(0, 0, 0, .44), 0 20px 48px rgba(0, 0, 0, .58);
  --d-shadow-primary: 0 1px 2px rgba(124, 77, 237, .40);
  --d-ring:        0 0 0 3px rgba(124, 77, 237, .48);
  --d-ring-danger: 0 0 0 3px rgba(207, 58, 45, .44);
  --d-overlay:   rgba(0, 0, 0, .66);
  --d-hover-ink: rgba(232, 234, 240, .10);
  --d-login-gradient: linear-gradient(135deg, #2b1a63 0%, #1b1140 55%, #120b2b 100%);

  /* ── token ที่เพิ่มเพื่อเลิกใช้ค่าดิบ ────────────────────────────────
     ‼️ หกจุดนี้เคย hardcode อยู่ใน selector จึงไม่เปลี่ยนตามโหมด */
  --border-strong:  #d6d9e4;
  --shadow-primary: 0 1px 2px rgba(108, 60, 233, .24);
  --overlay:        rgba(26, 29, 41, .45);
  --hover-ink:      rgba(26, 29, 41, .07);
  --login-gradient: linear-gradient(135deg, #6c3ce9 0%, #4c26b3 55%, #33177d 100%);

  /* ‼️‼️ แผ่นรองโลโก้ — **ขาวเสมอทั้งสองโหมด** โดยตั้งใจ
     ‼️ โลโก้ลูกค้าเป็นไฟล์ที่ผู้ใช้อัปโหลดเอง ส่วนใหญ่เป็น PNG พื้นโปร่งที่วาดด้วย
        หมึกสีเข้ม ‼️ วางบนพื้นมืดแล้วหายไปทั้งอัน กลายเป็นช่องว่างที่ดูเหมือนระบบพัง
     ‼️ จึงให้โลโก้นั่งบนแผ่นขาวเสมอ — เป็นคำตอบเดียวที่ไม่ต้องขอให้ลูกค้าอัปโหลดใหม่
        และไม่ต้องเดาว่าไฟล์ไหนสว่างหรือมืด ‼️ ห้ามผูกค่านี้กับโหมด */
  --logo-plate:        #ffffff;
  --logo-plate-border: #e7e9f0;
}

/* ── ตามอุปกรณ์ (ค่าเริ่มต้น — ไม่มี data-theme บน <html>) ─────────────── */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: var(--d-bg);
    --surface: var(--d-surface);
    --surface2: var(--d-surface2);
    --surface3: var(--d-surface3);
    --border: var(--d-border);
    --hairline: var(--d-hairline);
    --text: var(--d-text);
    --muted: var(--d-muted);
    --faint: var(--d-faint);
    --on-solid: var(--d-on-solid);
    --primary: var(--d-primary);
    --primary-hover: var(--d-primary-hover);
    --primary-press: var(--d-primary-press);
    --primary-soft: var(--d-primary-soft);
    --primary-soft2: var(--d-primary-soft2);
    --primary-text: var(--d-primary-text);
    --accent: var(--d-accent);
    --accent-soft: var(--d-accent-soft);
    --accent-text: var(--d-accent-text);
    --success: var(--d-success);
    --success-soft: var(--d-success-soft);
    --success-text: var(--d-success-text);
    --warning: var(--d-warning);
    --warning-soft: var(--d-warning-soft);
    --warning-text: var(--d-warning-text);
    --danger: var(--d-danger);
    --danger-soft: var(--d-danger-soft);
    --danger-text: var(--d-danger-text);
    --danger-hover: var(--d-danger-hover);
    --info: var(--d-info);
    --info-soft: var(--d-info-soft);
    --info-text: var(--d-info-text);
    --neutral-soft: var(--d-neutral-soft);
    --neutral-text: var(--d-neutral-text);
    --border-strong: var(--d-border-strong);
    --shadow-xs: var(--d-shadow-xs);
    --shadow-sm: var(--d-shadow-sm);
    --shadow-md: var(--d-shadow-md);
    --shadow-lg: var(--d-shadow-lg);
    --shadow-primary: var(--d-shadow-primary);
    --ring: var(--d-ring);
    --ring-danger: var(--d-ring-danger);
    --overlay: var(--d-overlay);
    --hover-ink: var(--d-hover-ink);
    --login-gradient: var(--d-login-gradient);
  }
}

/* ── ผู้ใช้เลือก "มืด" เอง — ‼️ ต้องชนะ prefers-color-scheme ทุกกรณี ──── */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: var(--d-bg);
  --surface: var(--d-surface);
  --surface2: var(--d-surface2);
  --surface3: var(--d-surface3);
  --border: var(--d-border);
  --hairline: var(--d-hairline);
  --text: var(--d-text);
  --muted: var(--d-muted);
  --faint: var(--d-faint);
  --on-solid: var(--d-on-solid);
  --primary: var(--d-primary);
  --primary-hover: var(--d-primary-hover);
  --primary-press: var(--d-primary-press);
  --primary-soft: var(--d-primary-soft);
  --primary-soft2: var(--d-primary-soft2);
  --primary-text: var(--d-primary-text);
  --accent: var(--d-accent);
  --accent-soft: var(--d-accent-soft);
  --accent-text: var(--d-accent-text);
  --success: var(--d-success);
  --success-soft: var(--d-success-soft);
  --success-text: var(--d-success-text);
  --warning: var(--d-warning);
  --warning-soft: var(--d-warning-soft);
  --warning-text: var(--d-warning-text);
  --danger: var(--d-danger);
  --danger-soft: var(--d-danger-soft);
  --danger-text: var(--d-danger-text);
  --danger-hover: var(--d-danger-hover);
  --info: var(--d-info);
  --info-soft: var(--d-info-soft);
  --info-text: var(--d-info-text);
  --neutral-soft: var(--d-neutral-soft);
  --neutral-text: var(--d-neutral-text);
  --border-strong: var(--d-border-strong);
  --shadow-xs: var(--d-shadow-xs);
  --shadow-sm: var(--d-shadow-sm);
  --shadow-md: var(--d-shadow-md);
  --shadow-lg: var(--d-shadow-lg);
  --shadow-primary: var(--d-shadow-primary);
  --ring: var(--d-ring);
  --ring-danger: var(--d-ring-danger);
  --overlay: var(--d-overlay);
  --hover-ink: var(--d-hover-ink);
  --login-gradient: var(--d-login-gradient);
}

/* ─── RESET ──────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { -webkit-text-size-adjust: 100%; }

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-md);
  line-height: var(--lh-base);
  -webkit-font-smoothing: antialiased;
  /* ‼️ กัน horizontal overflow ระดับหน้า — ตารางกว้างต้องเลื่อนในกล่องของตัวเอง */
  overflow-x: hidden;
}

a { color: var(--primary-text); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3, h4 { line-height: var(--lh-tight); font-weight: var(--fw-semi); }
h1 { font-size: var(--fs-2xl); letter-spacing: -.01em; }
h2 { font-size: var(--fs-xl); }
h3 { font-size: var(--fs-lg); }

small, .text-muted { color: var(--muted); font-size: var(--fs-sm); }
.text-faint  { color: var(--faint); }
.text-danger { color: var(--danger-text); }
/* ‼️ เหลือง = พิสูจน์แล้วว่าเป็นข้อจำกัดของต้นทาง · เขียว = ผ่านจริง
   ทั้งสองสีมาจาก token ห้าม hardcode ค่าดิบ (กฎเหล็กข้อ 8) */
.text-warning { color: var(--warning-text); }
.text-success { color: var(--success-text); }
.text-mono   { font-family: var(--font-mono); }

img, svg { max-width: 100%; }
svg { flex: none; }

/* ‼️ focus ต้องมองเห็นเสมอ (ข้อกำหนด UX) — ห้ามใช้ outline: none โดยไม่มีตัวแทน */
:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}

/* ‼️ ทุกกล่องที่ "ห่อ .sr-only" ต้องเป็น containing block ของตัวเอง
      .sr-only เป็น position:absolute — ถ้าไม่มีพ่อที่ positioned มันจะไปอ้างอิง
      viewport แทน แล้วหลุดออกนอกกล่องเลื่อนของตาราง ทำให้ทั้ง "หน้า" เลื่อนแนวนอนได้
      (เจอจริงตอนตรวจด้วยเบราว์เซอร์: html.scrollWidth = 708 บนจอ 390) */
.btn,
.page-link,
.dropdown-trigger,
.flash-close { position: relative; }

/* ─── โครงหน้า ───────────────────────────────────────────────────────── */
.layout { display: flex; min-height: 100vh; }

/* ═══ SIDEBAR ════════════════════════════════════════════════════════ */
.sidebar {
  width: var(--sidebar-w);
  flex: 0 0 var(--sidebar-w);
  background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  z-index: var(--z-drawer);
}

/* ── ตราสินค้า ───────────────────────────────────────────────────────── */
.sidebar-brand {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-4);
  border-bottom: 1px solid var(--hairline);
  min-height: var(--topbar-h);
  color: var(--text);
}

.sidebar-brand:hover { text-decoration: none; }

/* ‼️ ‼️ ห้ามใส่ border-radius ตรงนี้ — ‼️ ตราวาดมุมโค้งของตัวเองใน SVG (rx) ตามโลโก้จริง
      ‼️ ของเดิมเป็น `border-radius: var(--radius-pill)` ไว้บีบตราวงกลมชุดเก่า
         ‼️ เจอจริง: พอเปลี่ยนเป็นตราสี่เหลี่ยมแล้ว บรรทัดนี้ยังคลิปเป็นวงกลมทับ SVG อยู่
            ทำให้มุมที่ตั้งใจไว้หายไปหมดโดยที่ดูจากไฟล์ SVG อย่างเดียวไม่มีทางรู้
      ‼️ รูปทรงของตราต้องมาจากไฟล์ตราที่เดียว ‼️ CSS ห้ามมากำหนดทรงซ้อน */
.brand-mark { width: 38px; height: 38px; flex: none; }

.brand-text { min-width: 0; display: flex; flex-direction: column; }
.brand-name { font-size: var(--fs-lg); font-weight: var(--fw-bold); letter-spacing: -.01em; line-height: 1.2; }
.brand-sub  { font-size: var(--fs-xs); color: var(--faint); line-height: 1.3; }

/* ── รายการเมนู ──────────────────────────────────────────────────────── */
.sidebar-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--sp-3) var(--sp-3) var(--sp-5);
}

.nav-group + .nav-group { margin-top: var(--sp-4); }

.nav-group-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  border: none;
  background: none;
  color: var(--faint);
  font-family: var(--font);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  letter-spacing: .02em;
  text-align: left;
  cursor: pointer;
  border-radius: var(--radius-xs);
}

.nav-group-label:hover { color: var(--muted); background: var(--surface2); }
.nav-group-label .chevron { transition: transform var(--transition); flex: none; }
.nav-group[data-collapsed="true"] .nav-group-label .chevron { transform: rotate(-90deg); }
.nav-group[data-collapsed="true"] .nav-list { display: none; }

.nav-list { display: flex; flex-direction: column; gap: 2px; }

.nav-link {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
  color: var(--muted);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  min-height: 40px;
  transition: background var(--transition), color var(--transition);
}

.nav-link:hover { background: var(--surface2); color: var(--text); text-decoration: none; }
.nav-link .nav-icon { flex: none; width: var(--icon); height: var(--icon); color: var(--faint); }
.nav-link:hover .nav-icon { color: var(--muted); }

/* ‼️ ข้อความไทยยาวต้องไม่ล้นออกนอกแถบ — ตัดเป็นสองบรรทัดแทนการดันความกว้าง */
.nav-text {
  min-width: 0;
  overflow-wrap: anywhere;
  line-height: 1.35;
}

.nav-link.is-active {
  background: var(--primary-soft);
  color: var(--primary-text);
  font-weight: var(--fw-semi);
}

.nav-link.is-active .nav-icon { color: var(--primary); }

/* แถบ accent สีส้มด้านซ้ายของเมนูที่เลือกอยู่ */
.nav-link.is-active::before {
  content: '';
  position: absolute;
  left: calc(var(--sp-3) * -1);
  inset-block: 8px;
  width: 3px;
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
  background: var(--accent);
}

.nav-badge {
  margin-left: auto;
  flex: none;
  background: var(--danger-soft);
  color: var(--danger-text);
  border-radius: var(--radius-pill);
  padding: 0 var(--sp-2);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  line-height: 18px;
}

.sidebar-foot {
  padding: var(--sp-3) var(--sp-4) var(--sp-4);
  border-top: 1px solid var(--hairline);
  font-size: var(--fs-xs);
  color: var(--faint);
}

.sidebar-foot strong { display: block; color: var(--muted); font-weight: var(--fw-semi); font-size: var(--fs-sm); }

/* ── โหมดย่อ (desktop) ───────────────────────────────────────────────── */
.layout.is-mini .sidebar { width: var(--sidebar-w-mini); flex-basis: var(--sidebar-w-mini); }
.layout.is-mini .brand-text,
.layout.is-mini .nav-group-label span,
.layout.is-mini .nav-text,
.layout.is-mini .nav-badge,
.layout.is-mini .sidebar-foot { display: none; }
.layout.is-mini .sidebar-brand { justify-content: center; padding-inline: 0; }

/* ‼️ ตอนย่อ ชื่อกลุ่มถูกซ่อน เหลือลูกศรลอยอยู่เดี่ยวๆ ซึ่งดูเหมือนของหลุด
      เปลี่ยนหัวข้อกลุ่มเป็นเส้นคั่นบางๆ แทน (ยังกดพับ/ขยายได้เหมือนเดิม) */
.layout.is-mini .nav-group-label {
  justify-content: center;
  padding: 0;
  height: 1px;
  margin: var(--sp-3) var(--sp-3);
  background: var(--hairline);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.layout.is-mini .nav-group-label .chevron { display: none; }
.layout.is-mini .nav-group-label:hover { background: var(--primary-soft2); }
.layout.is-mini .nav-link { justify-content: center; padding-inline: 0; }
.layout.is-mini .nav-link.is-active::before { left: 0; }

/* tooltip ตอนย่อ — ใช้ ::after ไม่ต้องพึ่ง JS */
.layout.is-mini .nav-link[data-label]:hover::after,
.layout.is-mini .nav-link[data-label]:focus-visible::after {
  content: attr(data-label);
  position: absolute;
  left: calc(100% + var(--sp-2));
  top: 50%;
  transform: translateY(-50%);
  background: var(--text);
  color: var(--on-solid);
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-xs);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  white-space: nowrap;
  box-shadow: var(--shadow-md);
  z-index: var(--z-dropdown);
  pointer-events: none;
}

/* ── flyout ของกลุ่มเมนูตอนย่อ (desktop) ─────────────────────────────────
   ‼️ ปัญหาเดิม: โหมดย่อมีแค่ tooltip (::after · pointer-events: none) ซึ่งถูก
      .sidebar-scroll { overflow-y: auto } ตัดจนเหลือแถบดำ — ดูเหมือน "เมนูโผล่แล้วหาย"
   ‼️ ทางแก้: กลุ่มที่มีชื่อกลายเป็นปุ่มไอคอนหนึ่งปุ่ม แล้ว .nav-list เดิมกลายเป็น flyout
      แบบ position: fixed (หลุดจาก overflow ของ .sidebar-scroll ได้) ‼️ ตำแหน่งตั้งโดย JS
      ผ่าน CSSOM ไม่ใช่ style attribute (CSP style-src 'self')
   ‼️ สะพาน hover: ::before โปร่งใสกว้างเท่าช่องว่างระหว่างปุ่มกับ flyout — ไม่บังเนื้อหาหลัก
   ‼️ โหมดปกติและลิ้นชักบนจอเล็กไม่แตะเลย — ทุก selector ครอบด้วย .layout.is-mini + min-width */
.nav-flyout-trigger,
.nav-flyout-title { display: none; }

@media (min-width: 1025px) {
  .layout.is-mini .nav-group.has-flyout .nav-group-label { display: none; }

  .layout.is-mini .nav-group.has-flyout .nav-flyout-trigger {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 40px;
    padding: var(--sp-2) 0;
    border: none;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--muted);
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
  }

  .layout.is-mini .nav-flyout-trigger .nav-icon { width: var(--icon); height: var(--icon); color: var(--faint); }
  .layout.is-mini .nav-flyout-trigger:hover,
  .layout.is-mini .nav-group.is-flyout-open .nav-flyout-trigger { background: var(--surface2); color: var(--text); }
  .layout.is-mini .nav-flyout-trigger:hover .nav-icon,
  .layout.is-mini .nav-group.is-flyout-open .nav-flyout-trigger .nav-icon { color: var(--muted); }
  .layout.is-mini .nav-flyout-trigger:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }

  /* กลุ่มที่มีเมนูที่เปิดอยู่ — ไอคอนกลุ่มบอกด้วย */
  .layout.is-mini .nav-group.has-flyout.has-active .nav-flyout-trigger { background: var(--primary-soft); color: var(--primary-text); }
  .layout.is-mini .nav-group.has-flyout.has-active .nav-flyout-trigger .nav-icon { color: var(--primary); }

  /* ‼️ ซ่อนรายการในแถบ (ไม่ว่ากลุ่มจะพับหรือไม่) — โผล่เป็น flyout เท่านั้น */
  .layout.is-mini .nav-group.has-flyout .nav-list { display: none; }

  .layout.is-mini .nav-group.has-flyout.is-flyout-open .nav-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    position: fixed;
    /* left/top ตั้งโดย JS (CSSOM) ตามตำแหน่งปุ่มจริง และดันขึ้นเองเมื่อใกล้ขอบล่าง */
    z-index: var(--z-dropdown);
    min-width: 224px;
    max-width: min(320px, calc(100vw - var(--sidebar-w-mini) - var(--sp-5)));
    /* ‼️ ห้าม overflow: auto ที่นี่ — มันจะตัด ::before (สะพาน hover) ที่อยู่นอกกล่องทิ้ง
       วัดจริง: pointer ที่ x=80 ในช่องว่างตกไปโดน MAIN แล้วเมนูปิดตอน 200ms พอดี */
    padding: var(--sp-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow-lg);
  }

  /* สะพาน hover — ปิดช่องว่างระหว่างไอคอนกับ flyout ให้ pointer เดินผ่านได้โดยไม่หลุด */
  .layout.is-mini .nav-group.has-flyout.is-flyout-open .nav-list::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    right: 100%;
    width: var(--sp-3);
  }

  .layout.is-mini .nav-group.has-flyout.is-flyout-open .nav-flyout-title {
    display: block;
    padding: var(--sp-1) var(--sp-3) var(--sp-2);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semi);
    color: var(--faint);
    letter-spacing: .02em;
  }

  /* ใน flyout รายการกลับมาเป็นไอคอน + ข้อความเหมือนโหมดปกติ และไม่มี tooltip ซ้อน */
  .layout.is-mini .nav-group.has-flyout.is-flyout-open .nav-link { justify-content: flex-start; padding-inline: var(--sp-3); }
  .layout.is-mini .nav-group.has-flyout.is-flyout-open .nav-text,
  .layout.is-mini .nav-group.has-flyout.is-flyout-open .nav-badge { display: revert; }
  .layout.is-mini .nav-group.has-flyout.is-flyout-open .nav-link[data-label]:hover::after,
  .layout.is-mini .nav-group.has-flyout.is-flyout-open .nav-link[data-label]:focus-visible::after { content: none; }
  .layout.is-mini .nav-group.has-flyout.is-flyout-open .nav-link.is-active::before { left: 0; }

  /* ‼️ ลิงก์เดี่ยวนอกกลุ่ม (ภาพรวม) — tooltip เดิมถูก overflow ตัด จึงให้ JS วาดป้ายลอยแทน */
  .layout.is-mini .nav-link[data-label]:hover::after,
  .layout.is-mini .nav-link[data-label]:focus-visible::after { content: none; }
}

/* ป้ายลอยของลิงก์เดี่ยวตอนย่อ — element เดียวใช้ร่วมกัน วางไว้ท้าย body โดย JS */
.nav-tip {
  position: fixed;
  z-index: var(--z-dropdown);
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-xs);
  background: var(--text);
  color: var(--on-solid);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  white-space: nowrap;
  box-shadow: var(--shadow-md);
  pointer-events: none;
}

/* ═══ ส่วนเนื้อหา ════════════════════════════════════════════════════ */
.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/* ═══ HEADER ═════════════════════════════════════════════════════════ */
.topbar {
  min-height: var(--topbar-h);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-2) var(--sp-5);
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
}

.topbar-left  { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.topbar-tools { display: flex; align-items: center; gap: var(--sp-2); flex: none; }

/* ── breadcrumb ──────────────────────────────────────────────────────── */
.breadcrumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-2);
  font-size: var(--fs-md);
  min-width: 0;
}

.breadcrumb li { list-style: none; display: inline-flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.breadcrumb a { color: var(--muted); font-weight: var(--fw-medium); }
.breadcrumb a:hover { color: var(--text); text-decoration: none; }
.breadcrumb .is-current { color: var(--text); font-weight: var(--fw-semi); }
.breadcrumb .sep { color: var(--faint); flex: none; }

/* ── ปุ่มไอคอนบนหัวข้อ ──────────────────────────────────────────────── */
.icon-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  flex: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--muted);
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}

.icon-btn:hover { background: var(--surface2); color: var(--text); text-decoration: none; }
.icon-btn:active { background: var(--surface3); }
.icon-btn[aria-expanded="true"] { background: var(--primary-soft); color: var(--primary-text); }

.icon-btn .dot {
  position: absolute;
  top: 8px;
  right: 9px;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--primary);
  border: 2px solid var(--surface);
}

/* ── ตัวเลือกภาษา / เมนูผู้ใช้ ───────────────────────────────────────── */
.dropdown { position: relative; }

.dropdown-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 40px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  cursor: pointer;
  max-width: 220px;
  transition: border-color var(--transition), background var(--transition);
}

.dropdown-trigger:hover { border-color: var(--primary-soft2); background: var(--surface2); text-decoration: none; }
.dropdown-trigger[aria-expanded="true"] { border-color: var(--primary); box-shadow: var(--ring); }
.dropdown-trigger .chevron { color: var(--faint); flex: none; }
.dropdown-trigger .trigger-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.avatar {
  width: 30px; height: 30px; flex: none;
  border-radius: var(--radius-pill);
  background: var(--primary-soft);
  color: var(--primary-text);
  display: inline-grid; place-items: center;
  font-size: var(--fs-xs); font-weight: var(--fw-bold);
}

.dropdown-menu {
  position: absolute;
  top: calc(100% + var(--sp-2));
  right: 0;
  min-width: 220px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: var(--sp-2);
  z-index: var(--z-dropdown);
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* ‼️ เมนูต้องไม่ถูกตัดบนจอเตี้ย */
  max-height: min(70vh, 420px);
  overflow-y: auto;
}

.dropdown-menu[hidden] { display: none; }
.dropdown-head { padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--hairline); margin-bottom: var(--sp-1); }
.dropdown-head strong { display: block; font-size: var(--fs-md); }
.dropdown-head span { color: var(--faint); font-size: var(--fs-xs); }

.dropdown-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  border: none;
  border-radius: var(--radius-xs);
  background: none;
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-md);
  text-align: left;
  cursor: pointer;
}

.dropdown-item:hover { background: var(--surface2); text-decoration: none; }
.dropdown-item.is-active { background: var(--primary-soft); color: var(--primary-text); font-weight: var(--fw-semi); }
.dropdown-item.is-danger { color: var(--danger-text); }
.dropdown-item.is-danger:hover { background: var(--danger-soft); }
.dropdown-item svg { color: var(--faint); flex: none; }
.dropdown-item.is-active svg { color: var(--primary); }
.dropdown-form { display: block; }

/* ── ช่องค้นหาบนหัวข้อ ──────────────────────────────────────────────── */
.topbar-search { position: relative; display: none; }
.topbar-search.is-open { display: block; }

.topbar-search input {
  width: min(320px, 42vw);
  height: 40px;
  padding: 0 var(--sp-3) 0 var(--sp-6);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface2);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-md);
}

.topbar-search input:focus { outline: none; border-color: var(--primary); background: var(--surface); box-shadow: var(--ring); }
.topbar-search svg { position: absolute; left: var(--sp-3); top: 50%; transform: translateY(-50%); color: var(--faint); pointer-events: none; }

/* ── ปุ่มเปิดเมนูบนมือถือ ───────────────────────────────────────────── */
.sidebar-toggle { display: none; }
.sidebar-mini { display: inline-flex; }
.scrim { display: none; }

/* ═══ CONTENT ════════════════════════════════════════════════════════ */
.content {
  flex: 1;
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
  /* ‼️ เว้นท้ายหน้าให้พ้นปุ่มแจ้งปัญหาที่ลอยอยู่มุมขวาล่าง
        ไม่งั้นปุ่มจะทับแถวสุดท้ายของตารางจนกดไม่ได้ (เจอจริงตอนถ่ายหน้าจอ) */
  padding: var(--sp-6) var(--sp-5) calc(var(--sp-7) + var(--btn-h-lg));
  min-width: 0;
}

/* ── หัวหน้า ─────────────────────────────────────────────────────────── */
.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
  flex-wrap: wrap;
}

.page-head-text { min-width: 0; }
.page-head h1 { font-size: var(--fs-3xl); font-weight: var(--fw-bold); }
.page-head .page-sub { color: var(--muted); font-size: var(--fs-md); margin-top: var(--sp-1); }
.page-actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }

/* ── การ์ด ───────────────────────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-xs);
  padding: var(--sp-5);
}

.card + .card { margin-top: var(--sp-4); }
.card-title { font-size: var(--fs-lg); font-weight: var(--fw-semi); margin-bottom: var(--sp-3); }
.card-flush { padding: 0; overflow: hidden; }
.card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap;
  padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--hairline);
  /* ‼️ ไม่มีบรรทัดนี้ ลูกที่กว้างเกินจะดันกล่องแทนที่จะถูกบีบ (flex min-width ค่าเริ่มต้นคือ auto) */
  min-width: 0;
}

/**
 * ‼️‼️ ป้ายสถานะบนหัวการ์ด **ต้องตัดบรรทัดได้** ต่างจากป้ายในตาราง
 *
 * ‼️ วัดจริงบนจอ 390: หน้า ระบบ → การย้ายข้อมูล (แท็บโครงสร้างฐาน) กว้างถึง 673px
 *    ‼️ ต้นเหตุคือป้ายใบเดียวกว้าง 628px — เพราะ `.badge` เป็น `white-space: nowrap`
 *       แต่หัวการ์ดบางใบเอาป้ายไปใส่ **ทั้งประโยค** เช่นเหตุผลที่ฐานกู้ทดสอบยังไม่พร้อม
 *    ‼️ ป้ายที่ตัดบรรทัดไม่ได้ + ประโยคยาว = ดันการ์ด ดันเนื้อหา ดันทั้งหน้าให้เลื่อนแนวนอน
 *
 * ‼️ แก้เฉพาะป้ายบนหัวการ์ด ‼️ ห้ามแก้ `.badge` ทั้งชุด — ป้ายในตารางและในแถวรายการ
 *    ต้องอยู่บรรทัดเดียวเสมอ ไม่งั้นความสูงของแต่ละแถวจะกระโดดไม่เท่ากัน
 */
.card-head .badge {
  white-space: normal;
  overflow-wrap: anywhere;
  max-width: 100%;
  text-align: start;
}

.grid  { display: grid; gap: var(--sp-4); }
/* ‼️ align-items: start — ไม่งั้นการ์ดที่มีเนื้อหาน้อยจะถูกยืดสูงเท่าการ์ดข้างๆ
      กลายเป็นพื้นที่ว่างก้อนใหญ่ (เห็นชัดที่หน้าแรกช่อง "งานที่ฉันรับผิดชอบ") */
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); align-items: start; }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.stack { display: flex; flex-direction: column; gap: var(--sp-3); }
.row { display: flex; gap: var(--sp-3); flex-wrap: wrap; align-items: flex-end; }
.row-center { justify-content: center; margin-top: var(--sp-4); }
.row-end { justify-content: flex-end; }

/**
 * ‼️‼️ ช่องตัวเลข/ค่าสรุป — ต้องยอมตัดคำ ไม่ใช่ดันกล่อง
 *
 * ‼️ วัดจริงบนจอ 390: หน้า ระบบ → การย้ายข้อมูล กว้าง 425px เพราะช่องนี้ช่องเดียว
 *    ‼️ ต้นเหตุคือค่าที่ใส่ไม่ได้เป็น "ตัวเลข" เสมอไป — หลายที่ใส่ชื่อฐานข้อมูล
 *       (`omcsupport_legacy_snapshot`) · fingerprint · checksum ซึ่งเป็นสตริงยาว
 *       ที่ไม่มีช่องว่างให้เบราว์เซอร์ตัดบรรทัด
 *    ‼️ พอตัดไม่ได้ กล่องก็กว้างตามคำ แล้วดัน grid ดันการ์ด ดันทั้งหน้าให้เลื่อนแนวนอน
 * ‼️ `min-width: 0` จำเป็นด้วย — ลูกของ flex/grid มีค่าเริ่มต้นเป็น auto ซึ่งแปลว่า
 *    "ห้ามเล็กกว่าเนื้อหา" ‼️ ลำพัง overflow-wrap อย่างเดียวจึงยังไม่พอ
 */
.stat { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.stat-value {
  font-size: var(--fs-2xl); font-weight: var(--fw-bold); color: var(--text);
  min-width: 0; overflow-wrap: anywhere;
}
.stat-label { color: var(--muted); font-size: var(--fs-sm); min-width: 0; overflow-wrap: anywhere; }

/* ── การ์ดสรุปตัวเลข (summary card) ─────────────────────────────────── */
.summary-grid {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  margin-bottom: var(--sp-5);
}

.summary-card {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-xs);
  color: var(--text);
  min-width: 0;
  transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}

a.summary-card:hover {
  text-decoration: none;
  border-color: var(--primary-soft2);
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.summary-icon {
  width: 52px; height: 52px; flex: none;
  border-radius: var(--radius);
  display: grid; place-items: center;
  background: var(--primary-soft);
  color: var(--primary);
}

.summary-icon.is-warning { background: var(--warning-soft); color: var(--warning); }
.summary-icon.is-danger  { background: var(--danger-soft);  color: var(--danger); }
.summary-icon.is-success { background: var(--success-soft); color: var(--success); }
.summary-icon.is-info    { background: var(--info-soft);    color: var(--info); }

/* ‼️ label/value เป็น <span> — ต้องบังคับเป็น block ไม่งั้นตัวเลขไปต่อท้ายข้อความ
      บนบรรทัดเดียวกัน ("เอกสารทั้งหมด 6") แทนที่จะซ้อนกันตามแบบ */
.summary-body { min-width: 0; flex: 1; display: flex; flex-direction: column; }
.summary-label { display: block; color: var(--muted); font-size: var(--fs-md); line-height: 1.35; overflow-wrap: anywhere; }
.summary-value { display: block; font-size: var(--fs-3xl); font-weight: var(--fw-bold); line-height: 1.15; margin-top: var(--sp-1); }
.summary-value.is-warning { color: var(--warning-text); }
.summary-value.is-danger  { color: var(--danger-text); }
.summary-value.is-success { color: var(--success-text); }
.summary-value.is-primary { color: var(--text); }
.summary-hint { display: block; color: var(--faint); font-size: var(--fs-xs); margin-top: 2px; }
.summary-arrow { color: var(--faint); flex: none; }
a.summary-card:hover .summary-arrow { color: var(--primary); }

/* ── แถบตัวกรอง ─────────────────────────────────────────────────────── */
.toolbar {
  display: flex;
  gap: var(--sp-3);
  flex-wrap: wrap;
  align-items: flex-end;
  padding: var(--sp-4) var(--sp-5);
}

.toolbar .field { margin-bottom: 0; flex: 1 1 200px; min-width: 0; }
.toolbar .field-search { flex: 2 1 260px; }
.toolbar-actions { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; }

/* ช่องค้นหาที่มีไอคอนแว่นขยายด้านใน */
.input-icon { position: relative; display: block; }
.input-icon > svg { position: absolute; left: var(--sp-3); top: 50%; transform: translateY(-50%); color: var(--faint); pointer-events: none; }
.input-icon > .form-control { padding-left: var(--sp-7); }

/* ─── ฟอร์ม ──────────────────────────────────────────────────────────── */
.field { display: flex; flex-direction: column; gap: var(--sp-1); margin-bottom: var(--sp-4); min-width: 0; }
.field label { font-weight: var(--fw-medium); font-size: var(--fs-sm); color: var(--text); }
.field-hint { color: var(--faint); font-size: var(--fs-xs); }
.field-required { color: var(--danger); }

.form-control,
select.form-control,
textarea.form-control {
  width: 100%;
  min-height: var(--input-h);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-md);
  transition: border-color var(--transition), box-shadow var(--transition);
}

select.form-control {
  /* ‼️ ลูกศรวาดด้วย background image (data URI) — CSP อนุญาต img-src data: */
  appearance: none;
  padding-right: var(--sp-6);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2398a2b3' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-3) center;
}

.form-control:hover { border-color: var(--border-strong); }
.form-control:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring); }
.form-control:disabled { background: var(--surface3); color: var(--faint); cursor: not-allowed; }
.form-control::placeholder { color: var(--faint); }
.form-control.is-invalid { border-color: var(--danger); }
.form-control.is-invalid:focus { box-shadow: var(--ring-danger); }
textarea.form-control { min-height: 120px; resize: vertical; line-height: var(--lh-base); }

/* ช่องเลือกไฟล์ — ปุ่มของเบราว์เซอร์หน้าตาไม่เข้าชุดกับปุ่มอื่นเลย จึงจัดให้เหมือนกัน */
input[type="file"] {
  width: 100%;
  padding: var(--sp-2);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface2);
  color: var(--muted);
  font-family: var(--font);
  font-size: var(--fs-sm);
  cursor: pointer;
}

input[type="file"]:hover { border-color: var(--primary); background: var(--primary-soft); }

input[type="file"]::file-selector-button {
  margin-right: var(--sp-3);
  height: var(--btn-h-sm);
  padding: 0 var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semi);
  cursor: pointer;
}

input[type="file"]::file-selector-button:hover { border-color: var(--primary); color: var(--primary-text); }
.field-error { color: var(--danger-text); font-size: var(--fs-xs); font-weight: var(--fw-medium); }

/* ─── ปุ่ม ───────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  height: var(--btn-h);
  padding: 0 var(--sp-4);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-family: var(--font);
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  background: var(--surface);
  border-color: var(--border);
  color: var(--text);
  transition: background var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition);
}

.btn:hover { text-decoration: none; background: var(--surface2); }
.btn:active { background: var(--surface3); }
.btn:disabled, .btn.is-disabled { opacity: .55; cursor: not-allowed; pointer-events: none; }
.btn svg { flex: none; }

.btn-primary { background: var(--primary); border-color: var(--primary); color: var(--on-solid); box-shadow: var(--shadow-primary); }
.btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
.btn-primary:active { background: var(--primary-press); border-color: var(--primary-press); }

.btn-outline { background: transparent; border-color: var(--border); color: var(--text); }
.btn-outline:hover { border-color: var(--primary); color: var(--primary-text); background: var(--primary-soft); }

.btn-ghost { background: transparent; border-color: transparent; color: var(--primary-text); }
.btn-ghost:hover { background: var(--primary-soft); }

.btn-danger { background: var(--danger); border-color: var(--danger); color: var(--on-solid); }
.btn-danger:hover { background: var(--danger-hover); border-color: var(--danger-hover); }
.btn-danger:focus-visible { outline-color: var(--danger); }

.btn-danger-ghost { background: transparent; border-color: transparent; color: var(--danger-text); }
.btn-danger-ghost:hover { background: var(--danger-soft); }

/* ปุ่มที่กำลังส่งฟอร์ม — บอกว่าระบบรับเรื่องแล้วและกันกดซ้ำ */
.btn.is-loading { opacity: .75; cursor: progress; }

.btn.is-loading::before {
  content: '';
  width: 14px;
  height: 14px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: var(--radius-pill);
  animation: btn-spin .6s linear infinite;
}

@keyframes btn-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .btn.is-loading::before { animation: none; }
}

.btn-block { width: 100%; }
.btn-sm { height: var(--btn-h-sm); padding: 0 var(--sp-3); font-size: var(--fs-sm); }
.btn-lg { height: var(--btn-h-lg); padding: 0 var(--sp-5); font-size: var(--fs-lg); }
.btn-icon { width: var(--btn-h); padding: 0; }
.btn-icon.btn-sm { width: var(--btn-h-sm); }

/* ─── ตาราง ──────────────────────────────────────────────────────────── */
.table-wrap {
  /* ‼️ ตารางกว้างเลื่อนในกล่องของตัวเองเท่านั้น ห้ามดันให้ทั้งหน้าเลื่อน */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
}

.card-flush > .table-wrap { border: none; border-radius: 0; box-shadow: none; }

table { width: 100%; border-collapse: collapse; font-size: var(--fs-md); }

thead th {
  background: var(--surface2);
  text-align: left;
  font-size: var(--fs-sm);
  color: var(--muted);
  font-weight: var(--fw-semi);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

tbody td {
  padding: var(--sp-4);
  border-bottom: 1px solid var(--hairline);
  vertical-align: middle;
}

tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--surface2); }
tbody td a { color: var(--text); font-weight: var(--fw-medium); }
tbody td a:hover { color: var(--primary-text); }

.cell-strong { font-weight: var(--fw-semi); }
.cell-sub { color: var(--faint); font-size: var(--fs-xs); margin-top: 2px; }
.cell-actions { width: 1%; white-space: nowrap; text-align: right; }

/* ปุ่มจัดการท้ายแถว — ‼️ ห้าม wrap ไม่งั้นปุ่มไปเรียงลงมาเป็นแนวตั้งและแถวสูงผิดรูป */
.table-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-1);
  flex-wrap: nowrap;
}

.table-actions form { display: inline-flex; }
.table-empty { padding: var(--sp-6); text-align: center; color: var(--muted); }

/* ── ท้ายตาราง: จำนวน + หน้า ─────────────────────────────────────────── */
.table-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-5);
  border-top: 1px solid var(--hairline);
  color: var(--muted);
  font-size: var(--fs-sm);
}

/* ── การแบ่งหน้า ─────────────────────────────────────────────────────── */
.pagination { display: flex; align-items: center; gap: var(--sp-1); flex-wrap: wrap; }

.page-link {
  display: inline-grid;
  place-items: center;
  min-width: var(--btn-h-sm);
  height: var(--btn-h-sm);
  padding: 0 var(--sp-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  background: var(--surface);
  color: var(--muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semi);
}

.page-link:hover { border-color: var(--primary); color: var(--primary-text); text-decoration: none; }
.page-link.is-current { background: var(--primary); border-color: var(--primary); color: var(--on-solid); }
.page-link.is-disabled { opacity: .45; pointer-events: none; }
.page-gap { color: var(--faint); padding: 0 var(--sp-1); }

/* ─── ป้ายสถานะ ──────────────────────────────────────────────────────── */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  background: var(--neutral-soft);
  color: var(--neutral-text);
  white-space: nowrap;
  line-height: 1.5;
}

/* กลุ่มความหมายเดียวกันใช้สีเดียวกันทั้งระบบ */
.badge-success, .badge-done, .badge-resolved, .badge-approved,
.badge-deployed, .badge-active, .badge-low
  { background: var(--success-soft); color: var(--success-text); }

.badge-warning, .badge-pending, .badge-in_progress, .badge-testing,
.badge-medium, .badge-expiring
  { background: var(--warning-soft); color: var(--warning-text); }

.badge-danger, .badge-rejected, .badge-failed, .badge-urgent,
.badge-high, .badge-critical, .badge-rolled_back, .badge-expired
  { background: var(--danger-soft); color: var(--danger-text); }

.badge-info, .badge-new, .badge-processing, .badge-draft
  { background: var(--info-soft); color: var(--info-text); }

.badge-primary, .badge-acknowledged, .badge-planning
  { background: var(--primary-soft); color: var(--primary-text); }

.badge-muted, .badge-waiting, .badge-archived, .badge-inactive, .badge-cancelled
  { background: var(--neutral-soft); color: var(--neutral-text); }

.badge-accent { background: var(--accent-soft); color: var(--accent-text); }

/* ─── กล่องแจ้งเตือน / toast ─────────────────────────────────────────── */
.flash-stack {
  position: fixed;
  top: var(--sp-5);
  right: var(--sp-5);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  max-width: min(380px, calc(100vw - var(--sp-6)));
}

.alert {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  border-left: 3px solid var(--muted);
  background: var(--surface);
  box-shadow: var(--shadow-md);
  font-size: var(--fs-md);
  color: var(--text);
}

.alert > svg { flex: none; margin-top: 2px; }
.alert > span { flex: 1; min-width: 0; overflow-wrap: anywhere; }

.flash-close {
  margin-left: auto;
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border: none;
  border-radius: var(--radius-xs);
  background: none;
  color: currentColor;
  opacity: .6;
  cursor: pointer;
}

.flash-close:hover { opacity: 1; background: var(--hover-ink); }

.alert-success { border-left-color: var(--success); background: var(--success-soft); border-color: var(--success-soft); }
.alert-error   { border-left-color: var(--danger);  background: var(--danger-soft);  border-color: var(--danger-soft); }
.alert-warning { border-left-color: var(--warning); background: var(--warning-soft); border-color: var(--warning-soft); }
.alert-info    { border-left-color: var(--info);    background: var(--info-soft);    border-color: var(--info-soft); }

/* ─── สถานะว่าง ──────────────────────────────────────────────────────── */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-7) var(--sp-5);
  text-align: center;
}

.empty-icon {
  width: 64px; height: 64px;
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  background: var(--surface3);
  color: var(--faint);
}

.empty-title { font-size: var(--fs-lg); font-weight: var(--fw-semi); }
.empty-text { color: var(--muted); max-width: 46ch; }

/* ─── โครงร่างระหว่างโหลด (skeleton) ─────────────────────────────────── */
.skeleton {
  border-radius: var(--radius-xs);
  background: linear-gradient(90deg, var(--surface3) 25%, var(--hairline) 37%, var(--surface3) 63%);
  background-size: 400% 100%;
  animation: skeleton-shimmer 1.4s ease infinite;
  min-height: 14px;
}

.skeleton-row { display: flex; gap: var(--sp-4); padding: var(--sp-4); border-bottom: 1px solid var(--hairline); }
.skeleton-row > .skeleton { flex: 1; height: 16px; }

@keyframes skeleton-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }
  * { transition-duration: .01ms !important; }
}

/* ─── หน้าเข้าสู่ระบบ ────────────────────────────────────────────────── */
.auth-shell {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--sp-5);
  background: var(--login-gradient);
}

.auth-card {
  width: 100%;
  max-width: 420px;
  background: var(--lg-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--sp-6);
}

.auth-brand { font-size: var(--fs-xl); font-weight: var(--fw-bold); color: var(--text); }
.auth-sub { color: var(--muted); font-size: var(--fs-sm); margin-bottom: var(--sp-5); }
.auth-logo { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-4); }

/* ─── หน้าเข้าสู่ระบบ ────────────────────────────────────────────────────
   ‼️ ทำตาม "ภาพ target UI" ที่เจ้าของงานกำหนด ‼️ ไม่ใช่การออกแบบเอง
      สองฝั่ง: จุดขายซ้าย · การ์ดเข้าสู่ระบบขวา · ภาพประดับกินขอบขวา

   ‼️ ธีมของหน้านี้เป็น **ส้ม OMC + กรมท่า** ตามภาพ ‼️ ไม่ใช่ม่วงของทั้งระบบ
      ‼️ จึงประกาศ token เฉพาะหน้านี้ไว้ที่ .login ‼️ ห้ามไปแก้ --primary ของส่วนกลาง
         เพราะนั่นจะเปลี่ยนสีทุกหน้าในระบบพร้อมกัน ซึ่งไม่ใช่สิ่งที่ขอ

   ‼️ ฝั่งซ้ายเป็นของประดับล้วน — บนมือถือถูกสั่งให้อยู่หลังการ์ดด้วย order
      ‼️ และหน้านี้ต้องไม่มีการเลื่อนแนวนอนทุกขนาดจอ
   ──────────────────────────────────────────────────────────────────────── */

.login {
  /* ‼️ สีอยู่ในบล็อก token ด้านบนแล้ว (กฎเหล็กข้อ 8) ‼️ ที่นี่เหลือแค่ค่าโครงหน้า */
  --lg-card-w:      clamp(340px, 31vw, 500px);
  --lg-photo-w:     19%;

  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--lg-surface);
  /* ‼️ กันเลื่อนแนวนอนที่ระดับหน้า — ของประดับทุกชิ้นอยู่ในกรอบนี้ */
  overflow-x: hidden;
}

/* ── ฉากหลัง ───────────────────────────────────────────────────────── */
.login-scene { position: absolute; inset: 0; pointer-events: none; }

/* แสงครีมนวลหลังโลโก้ฝั่งซ้าย */
.login-scene-glow {
  position: absolute;
  left: -8%;
  top: -14%;
  width: 62%;
  height: 78%;
  border-radius: 50%;
  background: radial-gradient(closest-side, var(--lg-glow-a) 0%, var(--lg-glow-b) 45%, var(--lg-white-0) 100%);
}

/* ‼️ แถบภาพออฟฟิศฝั่งขวา — ไม่มีไฟล์รูปจริงในโปรเจคและ CSP ห้ามโหลดรูปจากภายนอก
   ‼️ จึงประกอบจาก gradient ล้วน ให้โทนใกล้ภาพ reference (ฟ้าอมเทา + ไม้ + แสงหน้าต่าง)
      ‼️ ห้ามเปลี่ยนไปฝังภาพทั้งใบเป็นพื้นหลังของหน้า — UI ต้องเป็น HTML/CSS จริง */
.login-scene-photo {
  position: absolute;
  right: 0;
  top: 0;
  width: var(--lg-photo-w);
  height: 100%;
  background:
    linear-gradient(115deg, var(--lg-white-96) 0%, var(--lg-white-0) 26%),
    radial-gradient(120% 60% at 78% 84%, var(--lg-photo-wood) 0%, var(--lg-photo-wood-0) 58%),
    radial-gradient(90% 45% at 96% 22%, var(--lg-photo-light) 0%, var(--lg-photo-light-0) 62%),
    linear-gradient(160deg, var(--lg-photo-1) 0%, var(--lg-photo-2) 34%, var(--lg-photo-3) 62%, var(--lg-photo-4) 100%);
}

/* คำโปรยแนวตั้งบนภาพ */
.login-pillars {
  position: absolute;
  right: clamp(var(--sp-4), 2.4vw, var(--sp-7));
  top: clamp(var(--sp-6), 9vh, 108px);
  display: grid;
  gap: 2px;
  text-align: right;
  font-weight: var(--fw-bold);
  font-size: clamp(15px, 1.35vw, 21px);
  line-height: 1.28;
  letter-spacing: 0.04em;
  color: var(--lg-on-solid);
  text-shadow: var(--lg-pillar-shadow);
}

/* ขีดส้มใต้คำโปรย */
.login-pillars::after {
  content: "";
  justify-self: end;
  width: 58px;
  height: 3px;
  margin-top: var(--sp-3);
  border-radius: var(--radius-pill);
  background: var(--lg-orange);
}

/* ── โครงสองฝั่ง ───────────────────────────────────────────────────── */
.login-body {
  position: relative;
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--lg-card-w);
  align-items: center;
  gap: clamp(var(--sp-5), 3vw, 64px);
  width: 100%;
  max-width: 1560px;
  margin: 0 auto;
  padding: clamp(var(--sp-5), 4vh, 64px) clamp(var(--sp-5), 4vw, 72px)
           clamp(var(--sp-5), 4vh, 64px) clamp(var(--sp-5), 6.5vw, 118px);
  /* ‼️ เว้นที่ให้แถบภาพขวาเสมอ ไม่งั้นการ์ดจะทับคำโปรย */
  padding-right: calc(var(--lg-photo-w) + clamp(var(--sp-5), 2vw, var(--sp-7)));
}

/* ── ฝั่งซ้าย: จุดขาย ──────────────────────────────────────────────── */
.login-pitch { min-width: 0; }

.login-logo {
  width: clamp(190px, 17vw, 262px);
  height: auto;
  display: block;
  margin-bottom: clamp(var(--sp-4), 2.4vh, var(--sp-6));
}

.login-title {
  margin: 0;
  font-size: clamp(38px, 4.6vw, 68px);
  line-height: 1.04;
  font-weight: var(--fw-bold);
  letter-spacing: -0.015em;
}

.login-title-a { color: var(--lg-navy); }
.login-title-b { color: var(--lg-orange); }

.login-lead {
  margin: var(--sp-3) 0 0;
  font-size: clamp(17px, 1.55vw, 25px);
  font-weight: var(--fw-semi);
  color: var(--lg-navy);
}

.login-tagline {
  /* ‼️ กว้างพอให้ตัดสองบรรทัดเหมือนภาพ */
  max-width: 27ch;
  margin: var(--sp-4) 0 0;
  font-size: clamp(14px, 1.1vw, 17px);
  line-height: 1.65;
  color: var(--lg-navy-soft);
}

/* จุดขาย 4 ช่อง — 2×2 */
.login-features {
  list-style: none;
  margin: clamp(var(--sp-5), 3.4vh, var(--sp-7)) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(var(--sp-4), 2vw, var(--sp-6)) clamp(var(--sp-4), 2.6vw, var(--sp-7));
  max-width: 640px;
}

.login-feature { display: flex; gap: var(--sp-4); align-items: flex-start; min-width: 0; }

.login-feature-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: 14px;
}

/* ‼️ สีของกล่องไอคอนมาจากภาพ reference — เป็นสีตกแต่ง ไม่ใช่สีสถานะ
   ‼️ ห้ามเอาไปใช้สื่อความหมายว่าสำเร็จ/ผิดพลาดที่อื่น */
.login-feature-icon.is-orange { background: var(--lg-ic-orange-bg); color: var(--lg-ic-orange-fg); }
.login-feature-icon.is-violet { background: var(--lg-ic-violet-bg); color: var(--lg-ic-violet-fg); }
.login-feature-icon.is-blue   { background: var(--lg-ic-blue-bg);   color: var(--lg-ic-blue-fg); }
.login-feature-icon.is-green  { background: var(--lg-ic-green-bg);  color: var(--lg-ic-green-fg); }

.login-feature-body { display: grid; gap: 3px; min-width: 0; }
.login-feature-body strong { font-size: clamp(14px, 1.05vw, 16px); color: var(--lg-navy); }

.login-feature-body span {
  font-size: clamp(12.5px, 0.92vw, 14px);
  line-height: 1.55;
  color: var(--lg-navy-soft);
}

/* "Your Success is Our Mission" + ขีดส้มโค้ง */
.login-mission {
  position: relative;
  display: inline-block;
  margin: clamp(var(--sp-6), 4vh, 64px) 0 0 clamp(0px, 4.5vw, 84px);
  white-space: nowrap;
  padding-bottom: var(--sp-3);
  font-family: "Segoe Script", "Bradley Hand", "Comic Sans MS", cursive;
  font-size: clamp(19px, 1.85vw, 28px);
  font-weight: var(--fw-semi);
  font-style: italic;
  color: var(--lg-navy);
}

.login-mission::after {
  content: "";
  position: absolute;
  left: 6%;
  right: -6%;
  bottom: 0;
  height: 12px;
  border-bottom: 3px solid var(--lg-orange);
  border-right: 3px solid var(--lg-orange);
  border-radius: 0 0 60% 0;
  transform: rotate(-1.5deg);
}

/* ── ฝั่งขวา: การ์ดเข้าสู่ระบบ ─────────────────────────────────────── */
.login-panel { min-width: 0; display: flex; justify-content: center; }

.login-card {
  width: 100%;
  background: var(--lg-surface);
  border-radius: 26px;
  box-shadow: var(--lg-card-shadow);
  padding: clamp(var(--sp-5), 2.4vw, var(--sp-7));
  text-align: center;
}

.login-card-logo { width: clamp(132px, 11vw, 172px); height: auto; margin: 0 auto; display: block; }

.login-rule { border: 0; border-top: 1px solid var(--lg-line); margin: var(--sp-5) 0; }

.login-heading {
  margin: 0;
  font-size: clamp(24px, 2.1vw, 31px);
  font-weight: var(--fw-bold);
  color: var(--lg-navy);
}

.login-brand {
  margin: 2px 0 0;
  font-size: clamp(15px, 1.25vw, 19px);
  font-weight: var(--fw-bold);
  color: var(--lg-orange);
}

.login-sub {
  margin: var(--sp-2) 0 0;
  font-size: clamp(12.5px, 0.95vw, 14px);
  color: var(--lg-navy-soft);
}

.login-form { margin-top: clamp(var(--sp-5), 2.2vh, var(--sp-6)); text-align: left; }
.login-form .field + .field { margin-top: var(--sp-4); }

.login-label {
  display: block;
  margin-bottom: var(--sp-2);
  font-size: 14px;
  font-weight: var(--fw-semi);
  color: var(--lg-navy);
}

/* ช่องกรอกที่มีไอคอนนำหน้า */
.login-input { position: relative; display: block; }

.login-input-icon {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  color: var(--lg-navy-soft);
  pointer-events: none;
}

/* ‼️ เขียนทับเฉพาะช่องกรอกที่อยู่ในหน้านี้ — .form-control ของส่วนกลางไม่ถูกแตะ */
.login-input .form-control {
  height: 50px;
  padding-left: 44px;
  border-radius: 12px;
  border-color: var(--lg-line);
  background: var(--lg-field);
  font-size: 15px;
}

.login-input .form-control:focus {
  border-color: var(--lg-orange);
  box-shadow: var(--lg-focus-ring);
  background: var(--lg-surface);
}

.login-eye {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 10px;
  background: none;
  color: var(--lg-navy-soft);
  cursor: pointer;
}

.login-eye:hover { background: var(--lg-field-hover); color: var(--lg-navy); }
.login-eye:focus-visible { outline: 2px solid var(--lg-orange); outline-offset: 1px; }

/* ปุ่มเข้าสู่ระบบ — ‼️ ส้มเต็มความกว้างตามภาพ */
.login-submit {
  width: 100%;
  height: 52px;
  margin-top: clamp(var(--sp-5), 2.4vh, var(--sp-6));
  gap: var(--sp-2);
  border: 0;
  border-radius: 12px;
  background: var(--lg-orange);
  color: var(--lg-on-solid);
  font-size: 16px;
  font-weight: var(--fw-bold);
  box-shadow: var(--lg-btn-shadow);
}

.login-submit:hover { background: var(--lg-orange-deep); }
.login-submit:focus-visible { outline: 2px solid var(--lg-orange-deep); outline-offset: 2px; }

/* เส้นคั่น "หรือเปลี่ยนภาษา" */
.login-divider {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: clamp(var(--sp-5), 2.4vh, var(--sp-6)) 0 var(--sp-4);
  font-size: 13px;
  color: var(--lg-faint);
}

.login-divider::before,
.login-divider::after { content: ""; flex: 1; height: 1px; background: var(--lg-line); }

.login-langs { display: flex; justify-content: center; gap: var(--sp-3); }

.login-lang {
  min-width: 62px;
  padding: 9px 0;
  border: 1px solid var(--lg-line);
  border-radius: 10px;
  font-weight: var(--fw-semi);
  font-size: 14px;
  color: var(--lg-navy-soft);
  text-decoration: none;
  text-align: center;
}

.login-lang:hover { border-color: var(--lg-orange); color: var(--lg-orange); }

/* ภาษาที่ใช้อยู่ — ‼️ ต้องต่างจากตัวอื่นด้วยสีและกรอบ ไม่ใช่แค่สีจาง */
.login-lang.is-active {
  border-color: var(--lg-orange);
  background: var(--lg-orange-soft);
  color: var(--lg-orange-deep);
}

.login-help {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  margin: clamp(var(--sp-5), 2.4vh, var(--sp-6)) 0 0;
  font-size: 13.5px;
  color: var(--lg-navy-soft);
}

.login-help > svg { color: var(--lg-navy-soft); }

.login-help-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-weight: var(--fw-semi);
  color: var(--lg-orange);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.login-help-link:hover { color: var(--lg-orange-deep); }

/* ── แถบท้ายหน้า ───────────────────────────────────────────────────── */
.login-foot {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4) clamp(var(--sp-5), 5vw, 96px);
  border-top: 1px solid var(--lg-line);
  background: var(--lg-surface);
  font-size: 12.5px;
  color: var(--lg-navy-soft);
  /* ‼️ เว้นที่ให้ปุ่มแจ้งปัญหาที่ลอยมุมขวาล่างของทุกหน้า ไม่ให้ทับสโลแกน */
  padding-right: clamp(150px, 14vw, 240px);
}

.login-foot-dots { display: inline-flex; gap: 5px; }

.login-foot-dots i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--lg-orange);
  opacity: 0.55;
}

.login-foot-copy { min-width: 0; }
.login-foot-slogan { margin-left: auto; white-space: nowrap; }

/* ── จอกลาง: ยุบภาพขวา แล้วให้จุดขายกับการ์ดอยู่คนละฝั่งเต็มความกว้าง ── */
@media (max-width: 1180px) {
  .login { --lg-photo-w: 0%; }
  .login-scene-photo { display: none; }
  .login-pillars { display: none; }
  .login-body { padding-right: clamp(var(--sp-5), 5vw, 96px); }
}

/* ── แท็บเล็ต / มือถือ ─────────────────────────────────────────────── */
@media (max-width: 900px) {
  .login-body {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-6);
    padding: var(--sp-6) var(--sp-5);
    align-items: start;
  }

  /* ‼️‼️ การ์ดต้องมาก่อนจุดขายบนจอแคบ — คนเปิดหน้านี้มาเพื่อล็อกอิน
     ‼️ ไม่ใช่มาอ่านโฆษณาแล้วต้องเลื่อนหาช่องกรอก */
  .login-panel { order: 1; }
  .login-pitch { order: 2; text-align: center; }

  .login-card { max-width: 452px; margin: 0 auto; }
  .login-logo { margin-left: auto; margin-right: auto; }
  .login-tagline { margin-left: auto; margin-right: auto; }
  .login-features { margin-left: auto; margin-right: auto; text-align: left; }
  .login-mission { margin-left: 0; }

  .login-foot { flex-wrap: wrap; justify-content: center; text-align: center; }
  .login-foot-slogan { margin-left: 0; }
}

@media (max-width: 560px) {
  .login-body { padding: var(--sp-5) var(--sp-4); }
  .login-card { padding: var(--sp-5) var(--sp-4); border-radius: 20px; }
  /* ‼️ 2 ช่องบนจอ 360px ทำให้ข้อความบีบจนอ่านไม่ออก — เหลือคอลัมน์เดียว */
  .login-features { grid-template-columns: minmax(0, 1fr); }
  .login-scene-glow { width: 120%; height: 42%; }
}

/* ─── ปุ่มแจ้งปัญหา (ลอยทุกหน้า) ─────────────────────────────────────── */
.support-fab {
  position: fixed;
  right: var(--sp-5);
  bottom: var(--sp-5);
  z-index: var(--z-sticky);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-lg);
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-solid);
  padding: 0 var(--sp-5);
  height: var(--btn-h-lg);
}

.support-fab:hover { background: var(--primary-hover); border-color: var(--primary-hover); }

/* ─── Modal / Drawer / Confirm ───────────────────────────────────────── */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: var(--overlay);
  display: grid;
  place-items: center;
  z-index: var(--z-modal);
  padding: var(--sp-4);
}

.modal-backdrop[hidden] { display: none; }

.modal {
  width: 100%;
  max-width: 520px;
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  /* ‼️ กล่องต้องไม่ถูกตัดบนจอเตี้ย — ให้ตัวมันเองเลื่อน */
  max-height: min(90vh, 720px);
  display: flex;
  flex-direction: column;
}

.modal-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--hairline); flex: none;
}

.modal-body { padding: var(--sp-5); overflow-y: auto; flex: 1; min-height: 0; }
.modal-foot {
  padding: var(--sp-4) var(--sp-5); border-top: 1px solid var(--hairline);
  display: flex; justify-content: flex-end; gap: var(--sp-2); flex: none; flex-wrap: wrap;
}

.modal-sm { max-width: 420px; }
.modal-icon { width: 48px; height: 48px; border-radius: var(--radius-pill); display: grid; place-items: center; background: var(--danger-soft); color: var(--danger); margin-bottom: var(--sp-3); }

.type-choices { display: grid; gap: var(--sp-2); }

.type-choice {
  display: flex; flex-direction: column; gap: var(--sp-1); text-align: left;
  padding: var(--sp-3) var(--sp-4); border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--surface); cursor: pointer;
  font-family: var(--font); color: var(--text);
}

.type-choice:hover { border-color: var(--primary); background: var(--primary-soft); text-decoration: none; }
.type-choice strong { font-size: var(--fs-md); }
.type-choice span { color: var(--muted); font-size: var(--fs-xs); }

/* ─── ตารางสิทธิ์ ────────────────────────────────────────────────────── */
.perm-cell { display: inline-block; margin: 0 var(--sp-1) var(--sp-1) 0; }

.perm-toggle {
  display: inline-block;
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  background: var(--surface2);
  color: var(--faint);
  font-family: var(--font);
  font-size: var(--fs-xs);
  cursor: pointer;
  white-space: nowrap;
}

.perm-toggle:hover { border-color: var(--primary); color: var(--primary-text); }
.perm-toggle.is-on { background: var(--primary); border-color: var(--primary); color: var(--on-solid); font-weight: var(--fw-semi); }

/* ─── คู่มือการใช้งาน ────────────────────────────────────────────────── */
.manual-group { margin-bottom: var(--sp-5); }
.manual-group h3 { margin-bottom: var(--sp-3); }
.manual-list { display: grid; gap: var(--sp-2); grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }

.manual-item {
  display: block; padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text); font-weight: var(--fw-medium);
}

.manual-item:hover { border-color: var(--primary); background: var(--primary-soft); color: var(--primary-text); text-decoration: none; }
.manual-body { line-height: 1.8; }
.manual-body h2 { margin: var(--sp-5) 0 var(--sp-2); font-size: var(--fs-lg); }
.manual-body ol, .manual-body ul { margin: var(--sp-2) 0 var(--sp-2) var(--sp-5); }
.manual-body figure { margin: var(--sp-4) 0; }
.manual-body img { max-width: 100%; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.manual-body figcaption { color: var(--muted); font-size: var(--fs-sm); margin-top: var(--sp-2); }

/* ─── หน้าแจ้งข้อผิดพลาด ─────────────────────────────────────────────── */
.error-shell { min-height: 60vh; display: grid; place-items: center; text-align: center; padding: var(--sp-6); }
.error-code { font-size: var(--fs-3xl); font-weight: var(--fw-bold); color: var(--primary-text); }

.error-ref {
  font-family: var(--font-mono); background: var(--surface2);
  border: 1px solid var(--border); border-radius: var(--radius-xs);
  padding: var(--sp-2) var(--sp-3); display: inline-block; margin: var(--sp-3) 0;
}

/* ─── กล่องคำสั่งที่ให้ก็อปไปวาง (คำสั่ง cron ของเครื่องนั้น) ───────────── */
/* ‼️ ต้องเลื่อนแนวนอนได้ ไม่ตัดบรรทัด — path ที่ถูกตัดกลางคือคำสั่งที่ก็อปไปแล้วใช้ไม่ได้ */
.code-block {
  font-family: var(--font-mono); font-size: var(--fs-sm);
  background: var(--surface2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-xs);
  padding: var(--sp-3); margin: 0;
  overflow-x: auto; white-space: pre; -webkit-overflow-scrolling: touch;
}
.code-block code { font: inherit; color: inherit; background: none; }

/* ─── ตาราง key/value ────────────────────────────────────────────────── */
.kv { display: grid; grid-template-columns: minmax(140px, 220px) 1fr; gap: var(--sp-3) var(--sp-4); }
.kv dt { color: var(--muted); font-size: var(--fs-sm); }
.kv dd { word-break: break-word; }

/* ─── แท็บ / เธรด / ไฟล์แนบ / คะแนน ─────────────────────────────────── */
.tabs { display: flex; gap: var(--sp-1); flex-wrap: wrap; margin-bottom: var(--sp-4); border-bottom: 1px solid var(--border); }

.tab {
  padding: var(--sp-3) var(--sp-4);
  border: none; border-bottom: 2px solid transparent;
  color: var(--muted); font-size: var(--fs-md); font-weight: var(--fw-semi);
  margin-bottom: -1px;
}

.tab:hover { text-decoration: none; color: var(--text); }
.tab.is-active { color: var(--primary-text); border-bottom-color: var(--primary); }
.tab-count { font-size: var(--fs-xs); color: var(--faint); margin-left: var(--sp-1); }

.thread { display: flex; flex-direction: column; gap: var(--sp-4); }
.thread-item { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--sp-4); background: var(--surface); }
.thread-item.is-internal { background: var(--accent-soft); border-left: 3px solid var(--accent); }
.thread-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-2); flex-wrap: wrap; }
.thread-author { font-weight: var(--fw-semi); font-size: var(--fs-md); }
.thread-time { color: var(--faint); font-size: var(--fs-xs); }
.thread-message { white-space: pre-wrap; line-height: 1.7; overflow-wrap: anywhere; }
.thread-empty { color: var(--muted); padding: var(--sp-4) 0; }

/* ลำดับเหตุการณ์ของงาน (Phase 20) — ‼️ ไม่ใช้ .thread-message เพราะคลาสนั้น
   ตั้ง white-space: pre-wrap ไว้สำหรับข้อความล้วน ทำให้ป้ายสถานะเรียงเพี้ยน */
.timeline-change { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.thread-actions { display: flex; gap: var(--sp-2); margin-top: var(--sp-3); flex-wrap: wrap; align-items: flex-end; }

.file-list { display: grid; gap: var(--sp-2); }

.file-item {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-3); border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); flex-wrap: wrap;
}

.file-meta { color: var(--faint); font-size: var(--fs-xs); }

.chips { display: flex; gap: var(--sp-2); flex-wrap: wrap; align-items: center; }

.chip {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-3); border-radius: var(--radius-pill);
  background: var(--primary-soft); color: var(--primary-text);
  font-size: var(--fs-xs); font-weight: var(--fw-semi);
}

.chip form { display: inline; }
.chip button { border: none; background: none; color: inherit; cursor: pointer; font-size: var(--fs-xs); padding: 0; }

.rating { display: flex; gap: var(--sp-1); align-items: center; }
.rating-value { color: var(--accent-text); font-weight: var(--fw-bold); }

/* ─── หน้าสำหรับพิมพ์ ────────────────────────────────────────────────── */
.print-sheet { max-width: 800px; margin: 0 auto; padding: var(--sp-6); color: var(--text); font-family: var(--font); }
.print-head { border-bottom: 2px solid var(--primary); padding-bottom: var(--sp-3); margin-bottom: var(--sp-4); }
.print-actions { margin-bottom: var(--sp-4); }

@media print {
  .print-actions, .support-fab, .flash-stack, .sidebar, .topbar { display: none; }
  .print-sheet { padding: 0; max-width: none; }
  .thread-item, .card { break-inside: avoid; }
  .content { padding: 0; }
}

/* ─── โปรเจค ─────────────────────────────────────────────────────────── */
.project-grid { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }

.project-card {
  display: flex; flex-direction: column; gap: var(--sp-3);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-xs); padding: var(--sp-4);
  color: var(--text);
  transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}

.project-card:hover { border-color: var(--primary-soft2); box-shadow: var(--shadow-md); transform: translateY(-1px); text-decoration: none; }
.project-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-3); }
.project-name { font-weight: var(--fw-semi); color: var(--text); }
.project-code { font-size: var(--fs-xs); color: var(--faint); font-family: var(--font-mono); }
.project-client { font-size: var(--fs-sm); color: var(--muted); }
.project-meta { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; font-size: var(--fs-sm); color: var(--muted); }
.project-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); font-size: var(--fs-xs); color: var(--muted); }
.is-overdue { color: var(--danger-text); font-weight: var(--fw-semi); }

.progress { height: 8px; border-radius: var(--radius-pill); background: var(--surface3); overflow: hidden; }
.progress-bar { height: 100%; width: 0; background: var(--primary); border-radius: var(--radius-pill); transition: width var(--transition); }

/* ‼️ ความกว้างมาจากข้อมูล แต่ CSP ห้าม inline style — จึงทำเป็นขั้นละ 5%
      partials/progress.php ปัดค่าเข้าขั้นเหล่านี้ก่อนเสมอ */
.progress-bar[data-progress="0"] { width: 0%; }
.progress-bar[data-progress="5"] { width: 5%; }
.progress-bar[data-progress="10"] { width: 10%; }
.progress-bar[data-progress="15"] { width: 15%; }
.progress-bar[data-progress="20"] { width: 20%; }
.progress-bar[data-progress="25"] { width: 25%; }
.progress-bar[data-progress="30"] { width: 30%; }
.progress-bar[data-progress="35"] { width: 35%; }
.progress-bar[data-progress="40"] { width: 40%; }
.progress-bar[data-progress="45"] { width: 45%; }
.progress-bar[data-progress="50"] { width: 50%; }
.progress-bar[data-progress="55"] { width: 55%; }
.progress-bar[data-progress="60"] { width: 60%; }
.progress-bar[data-progress="65"] { width: 65%; }
.progress-bar[data-progress="70"] { width: 70%; }
.progress-bar[data-progress="75"] { width: 75%; }
.progress-bar[data-progress="80"] { width: 80%; }
.progress-bar[data-progress="85"] { width: 85%; }
.progress-bar[data-progress="90"] { width: 90%; }
.progress-bar[data-progress="95"] { width: 95%; }
.progress-bar[data-progress="100"] { width: 100%; }
.progress-label { font-size: var(--fs-xs); color: var(--muted); }

/* ─── โลโก้บริษัท ────────────────────────────────────────────────────── */
.logo-box { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; }
.logo-img { width: 96px; height: 96px; object-fit: contain; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface2); padding: var(--sp-2); }
.logo-empty { width: 96px; height: 96px; display: grid; place-items: center; border: 1px dashed var(--border); border-radius: var(--radius-sm); color: var(--faint); font-size: var(--fs-xs); text-align: center; }

/* ─── ทะเบียน สคบ. ───────────────────────────────────────────────────── */
.ocpb-steps { display: grid; gap: var(--sp-4); }
.ocpb-step { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--sp-4); background: var(--surface); }
.ocpb-step-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.ocpb-step-head h3 { font-size: var(--fs-md); font-weight: var(--fw-semi); }
.ocpb-step-grid { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.ocpb-step-grid .field { margin-bottom: 0; }
.ocpb-step.is-done { border-left: 3px solid var(--success); }
.ocpb-step.is-empty { background: var(--surface2); }

/* ─── รายการติ๊กเลือก ────────────────────────────────────────────────── */
.check-list {
  display: grid; gap: var(--sp-2); grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  max-height: 260px; overflow-y: auto; border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: var(--sp-3); background: var(--surface2);
}

/* ‼️‼️ แต่ละข้อต้อง "ตัดบรรทัด" ได้ ไม่ใช่ดันการ์ดให้กว้างออกไป
   ‼️ วัดจริงบนจอ 390: หน้า ระบบ → การย้ายข้อมูล กว้างถึง 673px เพราะการ์ดนี้
      ‼️ ต้นเหตุคือรายละเอียดบางข้อเป็นสตริงยาวที่ไม่มีช่องว่างให้ตัด เช่น
         checksum ของไฟล์ schema และ `ตาราง.คอลัมน์: อาการ` ของผลตรวจ encoding
      ‼️ flex ที่ไม่ยอม wrap + คำที่ตัดไม่ได้ = กล่องขยายจนดันทั้งหน้าให้เลื่อนแนวนอน
   ‼️ align-items ต้องเป็น flex-start ไม่ใช่ center — พอตัดบรรทัดแล้ว center จะทำให้
      ป้ายสถานะลอยอยู่กลางย่อหน้าแทนที่จะอยู่บรรทัดแรกคู่กับข้อความ */
.check-item {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: var(--sp-2);
  font-size: var(--fs-md);
  min-width: 0;
}

/* ‼️ ป้ายสถานะห้ามถูกบีบจนตัวหนังสือซ้อนกัน — มันสั้นอยู่แล้ว */
.check-item .badge { flex: none; }

/* ‼️ คำที่ไม่มีช่องว่าง (checksum · ชื่อตาราง.คอลัมน์) ต้องยอมตัดกลางคำ
      ‼️ ไม่งั้นมันจะดันการ์ดออกไปเรื่อยๆ ไม่ว่ากล่องจะแคบแค่ไหน */
.check-item .text-mono { min-width: 0; overflow-wrap: anywhere; }

.check-item input { width: 16px; height: 16px; accent-color: var(--primary); }

/* ─── รายงานสรุปการให้บริการ ─────────────────────────────────────────── */
.kpi-row { display: flex; flex-wrap: wrap; gap: var(--sp-3); }

.kpi-box {
  flex: 1 1 140px; text-align: center; padding: var(--sp-4) var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface2); font-size: var(--fs-xs); color: var(--muted);
}

.kpi-value { display: block; font-size: var(--fs-xl); font-weight: var(--fw-bold); color: var(--text); }
.report-figure { margin: 0; max-width: 320px; }
.report-image { max-width: 100%; border: 1px solid var(--border); border-radius: var(--radius-sm); }

/* ─── ประวัติการใช้งาน · changelog ───────────────────────────────────── */
.log-detail { max-width: 420px; word-break: break-word; font-size: var(--fs-xs); color: var(--muted); }

/* ═══ RESPONSIVE ═════════════════════════════════════════════════════ */

/* ── Tablet ยาว: ย่อระยะ ─────────────────────────────────────────────── */
@media (max-width: 1200px) {
  .content { padding: var(--sp-5) var(--sp-4) var(--sp-6); }
}

/* ── Tablet / Mobile: sidebar กลายเป็นลิ้นชัก ────────────────────────── */
@media (max-width: 1024px) {
  .sidebar-toggle { display: inline-flex; }
  .sidebar-mini { display: none; }   /* โหมดย่อไม่มีความหมายบนลิ้นชัก */

  .sidebar {
    position: fixed;
    inset-block: 0;
    left: 0;
    transform: translateX(-100%);
    transition: transform 180ms ease;
    box-shadow: var(--shadow-lg);
    width: min(var(--sidebar-w), 86vw);
    flex-basis: auto;
  }

  .layout.is-open .sidebar { transform: translateX(0); }

  .layout.is-open .scrim {
    display: block;
    position: fixed;
    inset: 0;
    background: var(--overlay);
    z-index: calc(var(--z-drawer) - 1);
  }

  /* โหมดย่อไม่มีความหมายบนลิ้นชัก */
  .layout.is-mini .sidebar { width: min(var(--sidebar-w), 86vw); }
  .layout.is-mini .brand-text,
  .layout.is-mini .nav-group-label span,
  .layout.is-mini .nav-text,
  .layout.is-mini .nav-badge,
  .layout.is-mini .sidebar-foot { display: revert; }
  .layout.is-mini .sidebar-brand,
  .layout.is-mini .nav-group-label,
  .layout.is-mini .nav-link { justify-content: flex-start; padding-inline: var(--sp-3); }
  .layout.is-mini .sidebar-brand { padding-inline: var(--sp-4); }
  .layout.is-mini .nav-link[data-label]:hover::after { content: none; }

  .topbar { padding-inline: var(--sp-4); }
  .page-head h1 { font-size: var(--fs-2xl); }

  /* ‼️ บังคับ 2 คอลัมน์บนแท็บเล็ต — auto-fit จะได้ 3 ใบแล้วเหลือใบที่ 4 ห้อยอยู่แถวล่าง
        ซึ่งดูเหมือนจัดวางพลาดมากกว่าตั้งใจ */
  .summary-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ── Mobile ─────────────────────────────────────────────────────────── */
@media (max-width: 767px) {
  .content { padding: var(--sp-4) var(--sp-3) var(--sp-6); }
  .topbar { padding-inline: var(--sp-3); gap: var(--sp-2); }

  /* ‼️ หัวข้อบนมือถือ: ตัดของที่ไม่จำเป็นออก ไม่บีบทุกอย่างไว้บรรทัดเดียว
        เหลือเฉพาะชื่อหน้าปัจจุบัน และต้องตัดด้วย ellipsis ไม่ใช่ไปทับปุ่มกระดิ่ง */
  .topbar-left { flex: 1; min-width: 0; overflow: hidden; }
  .topbar .breadcrumb { flex-wrap: nowrap; min-width: 0; }
  .topbar .breadcrumb li:not(:last-child) { display: none; }
  .topbar .breadcrumb .sep { display: none; }
  .topbar .breadcrumb li:last-child { min-width: 0; }

  .topbar .breadcrumb .is-current {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .topbar-search.is-open { position: absolute; inset-inline: var(--sp-3); top: calc(100% + var(--sp-2)); background: var(--surface); border-radius: var(--radius-sm); box-shadow: var(--shadow-lg); z-index: var(--z-dropdown); }
  .topbar-search input { width: 100%; }
  .lang-switch .trigger-label { display: none; }

  /* ‼️‼️ แถบเครื่องมือบนหัวต้องไม่ดันให้ทั้งหน้าเลื่อนแนวนอน
     ‼️ วัดจริงบนจอ 390: เดิม scrollWidth = 408 (ล้นอยู่แล้ว 18px จากชื่อผู้ใช้เต็มๆ)
        และพอเพิ่มตัวสลับโหมดเข้าไปกลายเป็น 482 ‼️ ทั้งสองอย่างต้องแก้ที่นี่
     ‼️ วิธีแก้คือ "ยุบเหลือไอคอน" ไม่ใช่ซ่อนปุ่มทิ้ง — ปุ่มที่หายไปบนมือถือคือ
        ฟีเจอร์ที่ผู้ใช้มือถือไม่มีวันได้ใช้เลย
     ‼️ ไอคอนยังกดได้เต็มพื้นที่ 42px ตาม --btn-h จึงไม่เสียความสะดวกในการแตะ */
  .topbar-tools { min-width: 0; }

  /* ชื่อผู้ใช้และตัวอักษรภาษาซ่อนบนมือถือ — รูปวงกลมและไอคอนบอกได้อยู่แล้วว่าคืออะไร */
  .dropdown-trigger .trigger-label { display: none; }

  /* ‼️ ลูกศรของตัวเลือกภาษา/โหมด ไม่ได้บอกอะไรที่ไอคอนไม่ได้บอก — ตัดทิ้งเพื่อคืนที่ว่าง
     ‼️ ของเมนูผู้ใช้เก็บไว้ เพราะรูปวงกลมอย่างเดียวไม่สื่อว่ากดแล้วมีเมนู */
  .lang-switch .chevron,
  .theme-switch .chevron { display: none; }

  .lang-switch .dropdown-trigger,
  .theme-switch .dropdown-trigger { padding-inline: var(--sp-2); }

  .page-head { gap: var(--sp-3); }
  .page-head h1 { font-size: var(--fs-xl); }
  .page-actions { width: 100%; }
  .page-actions .btn { flex: 1 1 auto; }

  .summary-grid { grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
  .summary-card { padding: var(--sp-3); gap: var(--sp-3); flex-direction: column; align-items: flex-start; }
  .summary-icon { width: 40px; height: 40px; }
  .summary-value { font-size: var(--fs-2xl); }
  .summary-arrow { display: none; }

  .toolbar { padding: var(--sp-3); }
  .toolbar .field, .toolbar .field-search { flex: 1 1 100%; }
  .toolbar-actions { width: 100%; }
  .toolbar-actions .btn { flex: 1 1 auto; }

  .card { padding: var(--sp-4); }
  .card-head, .table-foot { padding-inline: var(--sp-4); }
  .kv { grid-template-columns: 1fr; gap: var(--sp-1); }
  .kv dd { margin-bottom: var(--sp-2); }

  .flash-stack { top: auto; bottom: var(--sp-4); inset-inline: var(--sp-3); max-width: none; }
  .support-fab { right: var(--sp-3); bottom: var(--sp-3); height: var(--btn-h); padding: 0 var(--sp-4); font-size: var(--fs-sm); }

  .modal { max-height: min(92vh, 100%); }
  .modal-foot .btn { flex: 1 1 auto; }
}

/* ‼️ ตารางบนมือถือ: ไม่บีบคอลัมน์จนอ่านไม่ออก แต่ให้ "เลื่อนแนวนอนแบบควบคุมได้"
      อยู่ในกล่อง .table-wrap เท่านั้น — ทั้งหน้ายังต้องไม่เลื่อนแนวนอน
      ถ้าไม่ตั้ง min-width ตารางจะย่อคอลัมน์ชื่อจนข้อความไทยหักบรรทัดละ 2-3 คำ */
@media (max-width: 767px) {
  .table-wrap table { min-width: 720px; }
  .table-wrap { scrollbar-width: thin; }
  tbody td, thead th { padding: var(--sp-3); }
}

/* คอลัมน์ที่เป็น "ชื่อของสิ่งนั้น" ต้องกว้างพอให้ภาษาไทยไม่หักบรรทัดละ 2 คำ
   ‼️ ใช้กับคอลัมน์ชื่อ/หัวข้อเท่านั้น ไม่ใช่ทุกคอลัมน์ */
.col-wide { min-width: 240px; }

/* คอลัมน์รหัส — ‼️ ห้ามตัดกลางรหัส ("T-35503" ต้องไม่กลายเป็น "T-" / "35503" คนละบรรทัด) */
.col-code { white-space: nowrap; font-family: var(--font-mono); }
.col-code a { font-family: var(--font-mono); }

/* ‼️ .field มี margin-bottom ไว้สำหรับฟอร์มแนวตั้ง แต่พอไปอยู่ในแถวตัวกรอง
      จะดันให้ปุ่มกับช่องกรอกไม่อยู่ระดับเดียวกัน */
.row > .field { margin-bottom: 0; }

@media (max-width: 480px) {
  /* การ์ดสรุปยังคง 2 คอลัมน์ — อ่านได้สบายและไม่ต้องเลื่อนยาว */
  .summary-grid { gap: var(--sp-2); }
  .summary-card { padding: var(--sp-3); }
  .summary-value { font-size: var(--fs-xl); }
}

/* ─── ตัวเลือกโหมดสว่าง/มืด ───────────────────────────────────────────── */

/* ‼️ ไอคอนบนปุ่มบอก "ตอนนี้อยู่โหมดไหน" ไม่ใช่ "กดแล้วจะไปโหมดไหน"
   ‼️ สลับด้วย CSS ล้วน ไม่ใช่ JS — โหมด "ตามอุปกรณ์" จึงเปลี่ยนไอคอนตามเครื่อง
      ได้ทันทีที่ผู้ใช้สลับธีมของ OS ‼️ โดยไม่ต้องโหลดหน้าใหม่และไม่ต้องมี listener */
.theme-icon-dark  { display: none; }
.theme-icon-light { display: block; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-icon-light { display: none; }
  :root:not([data-theme="light"]) .theme-icon-dark  { display: block; }
}

:root[data-theme="dark"] .theme-icon-light { display: none; }
:root[data-theme="dark"] .theme-icon-dark  { display: block; }

/* ‼️ เลือก "สว่าง" เองต้องชนะการตั้งค่าของเครื่องเสมอ */
:root[data-theme="light"] .theme-icon-light { display: block; }
:root[data-theme="light"] .theme-icon-dark  { display: none; }

.theme-switch .dropdown-menu { min-width: 190px; }

/* ‼️ รายการที่กำลังใช้อยู่ต้องดูออกโดยไม่ต้องพึ่งสีอย่างเดียว — มีจุดนำหน้าด้วย
      (ผู้ใช้ที่แยกสีไม่ได้ต้องใช้งานได้เท่ากัน) */
.theme-switch .dropdown-item.is-active {
  color: var(--primary-text);
  font-weight: var(--fw-semi);
}

.theme-switch .dropdown-item.is-active::after {
  content: '';
  width: 6px;
  height: 6px;
  margin-inline-start: auto;
  border-radius: var(--radius-pill);
  background: var(--primary);
}

/* ─── โลโก้ลูกค้าในโหมดมืด ───────────────────────────────────────────────
   ‼️‼️ โลโก้เป็นไฟล์ที่ลูกค้าอัปโหลดเอง ส่วนใหญ่เป็น PNG พื้นโปร่งหมึกเข้ม
      ‼️ วางบนพื้นมืดแล้วหายไปทั้งอัน กลายเป็นกล่องว่างที่ดูเหมือนรูปโหลดไม่ขึ้น
   ‼️ จึงบังคับให้นั่งบนแผ่นขาวเสมอทั้งสองโหมด — ไม่ต้องขอให้ลูกค้าอัปโหลดใหม่
      และไม่ต้องเดาว่าไฟล์ไหนสว่างหรือมืด ‼️ ห้ามผูกสีแผ่นนี้กับโหมด */
.logo-img {
  background: var(--logo-plate);
  border-color: var(--logo-plate-border);
}

/* ═══════════════════════════════════════════════════════════════════════
   Component กลางของงานระบบ (Migration · Cron · ติดตั้ง/อัปเดต)
   ═══════════════════════════════════════════════════════════════════════
   ‼️ ทุกสีมาจาก token ‼️ ห้ามค่าดิบ (กฎเหล็กข้อ 8 · ThemeContractTest คุมอยู่)
   ‼️ ห้ามพึ่ง JavaScript — ทั้งชุดนี้ทำงานได้ด้วย HTML+CSS ล้วน
      accordion ใช้ <details> ของเบราว์เซอร์ เพราะ CSP ห้ามสคริปต์ฝังในหน้า
   ─────────────────────────────────────────────────────────────────────── */

/* ── แถบลำดับขั้น ──────────────────────────────────────────────────────
   ‼️ บอก "ตอนนี้อยู่ขั้นไหน" ไม่ใช่ปุ่มกด — ขั้นที่ยังล็อกกดข้ามไม่ได้เหมือนเดิม
   ‼️ สถานะทุกขั้นมาจาก blockers() ของ Service ‼️ หน้าจอไม่คิดเอง */
.stepper {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: var(--sp-2);
  padding: var(--sp-5);
  list-style: none;
}

.step { position: relative; display: flex; flex-direction: column; align-items: center; gap: var(--sp-2); text-align: center; min-width: 0; }

/* เส้นเชื่อมระหว่างขั้น — วาดจากขั้นถัดไปย้อนไปหาขั้นก่อน จึงไม่มีเส้นโผล่หลังขั้นสุดท้าย */
.step + .step::before {
  content: '';
  position: absolute;
  inset-inline-end: 50%;
  top: 17px;
  width: 100%;
  height: 2px;
  background: var(--border);
}

/* ‼️ เส้นที่ "เดินผ่านมาแล้ว" เป็นสีเขียว — บอกระยะทางที่ทำไปแล้วโดยไม่ต้องอ่านตัวหนังสือ */
.step.is-done + .step::before,
.step.is-done + .step.is-current::before { background: var(--success); }

.step-dot {
  position: relative;
  z-index: 1;
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  border: 2px solid var(--border);
  background: var(--surface);
  color: var(--faint);
  font-weight: var(--fw-semi);
  font-size: var(--fs-sm);
  flex: none;
}

.step-name  { font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--muted); overflow-wrap: anywhere; }
.step-state { font-size: var(--fs-xs); color: var(--faint); overflow-wrap: anywhere; }

.step.is-done .step-dot   { border-color: var(--success); background: var(--success-soft); color: var(--success-text); }
.step.is-done .step-name  { color: var(--text); }
.step.is-done .step-state { color: var(--success-text); }

.step.is-current .step-dot {
  border-color: var(--primary); background: var(--primary-soft); color: var(--primary-text);
  box-shadow: var(--ring);
}
.step.is-current .step-name  { color: var(--text); font-weight: var(--fw-semi); }
.step.is-current .step-state { color: var(--primary-text); font-weight: var(--fw-medium); }

.step.is-blocked .step-dot   { border-color: var(--danger); background: var(--danger-soft); color: var(--danger-text); }
.step.is-blocked .step-name  { color: var(--text); }
.step.is-blocked .step-state { color: var(--danger-text); font-weight: var(--fw-medium); }

/* ── สิ่งที่ต้องแก้ก่อนไปต่อ ────────────────────────────────────────────
   ‼️ แดงมาก่อนส้มเสมอ — ลำดับถูกจัดฝั่ง PHP ไม่ใช่ด้วย CSS
   ‼️ ทุกข้อต้องบอก "สาเหตุ" และ "ทำอะไรต่อ" ‼️ ห้ามมีแค่คำว่าไม่ผ่าน */
.blocker-list { display: grid; gap: var(--sp-3); list-style: none; }

.blocker {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-4);
  border: 1px solid var(--border);
  border-inline-start: 3px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface2);
  min-width: 0;
}

.blocker-icon { flex: none; margin-top: 2px; }
.blocker-body { min-width: 0; display: grid; gap: var(--sp-1); }
.blocker-title { font-weight: var(--fw-semi); overflow-wrap: anywhere; }
.blocker-fix   { color: var(--muted); font-size: var(--fs-sm); overflow-wrap: anywhere; }

.blocker.is-stop { border-color: var(--danger); border-inline-start-color: var(--danger); background: var(--danger-soft); }
.blocker.is-stop .blocker-icon, .blocker.is-stop .blocker-title { color: var(--danger-text); }

.blocker.is-warn { border-color: var(--warning); border-inline-start-color: var(--warning); background: var(--warning-soft); }
.blocker.is-warn .blocker-icon, .blocker.is-warn .blocker-title { color: var(--warning-text); }

.blocker.is-clear { border-color: var(--success); border-inline-start-color: var(--success); background: var(--success-soft); }
.blocker.is-clear .blocker-icon, .blocker.is-clear .blocker-title { color: var(--success-text); }

/* รายละเอียดผลตรวจตัวอักษร — ‼️ ต้องบอกอาการ ไม่ใช่แค่ "ไม่ผ่าน" */
.blocker-facts { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); margin-top: var(--sp-1); }
.blocker-fact  { font-size: var(--fs-sm); color: var(--muted); }
.blocker-fact strong { color: var(--text); font-variant-numeric: tabular-nums; }

.symptom-list { display: grid; gap: var(--sp-1); margin-top: var(--sp-2); list-style: none; }
.symptom {
  display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: baseline;
  font-size: var(--fs-sm); min-width: 0;
}
.symptom-where { font-family: var(--font-mono); color: var(--text); overflow-wrap: anywhere; }
.symptom-what  { color: var(--danger-text); font-weight: var(--fw-medium); }

/* ── การ์ดสั่งงาน ──────────────────────────────────────────────────────
   ‼️ เรียงตามลำดับงานจริง · ปุ่มหลักคือ "ขั้นถัดไป" เท่านั้น
   ‼️ งานที่ยังติดเงื่อนไข **ยังกดได้** แล้วเซิร์ฟเวอร์ตอบว่าติดอะไร
      ‼️ ห้ามเปลี่ยนเป็น disabled — commit 6ef6b65 แก้ออกมาแล้วเพราะผู้ใช้บนเครื่องจริง
         สั่งงานตรวจซ้ำวนไม่รู้จบ และ MigrationBlockerUxTest คุมไว้ */
.action-list { display: grid; gap: var(--sp-3); list-style: none; }

.action {
  display: flex; gap: var(--sp-4); align-items: flex-start; justify-content: space-between;
  flex-wrap: wrap;
  padding: var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  min-width: 0;
}

.action.is-next { border-color: var(--primary); background: var(--primary-soft); }

.action-body { min-width: 0; display: grid; gap: var(--sp-1); flex: 1 1 260px; }
.action-name { font-weight: var(--fw-semi); overflow-wrap: anywhere; }
.action-why  { color: var(--muted); font-size: var(--fs-sm); overflow-wrap: anywhere; }
.action-blocked { color: var(--warning-text); font-size: var(--fs-sm); overflow-wrap: anywhere; }
/**
 * ‼️‼️ ต้องยอมหดได้ ‼️ ห้ามใช้ `flex: none`
 *
 * ‼️ บั๊กจริงที่เจอตอน smoke test: หน้านี้ล้น 3px **เฉพาะภาษาอังกฤษบนจอ 390px**
 *    ‼️ ป้ายปุ่มอังกฤษยาวกว่าไทยมาก ("Migrate new records only" เทียบกับป้ายไทยที่สั้นกว่า)
 *       พอกล่องหดไม่ได้และปุ่มไม่ตัดบรรทัด มันจึงดันทั้งหน้าให้เลื่อนแนวนอน
 * ‼️ การกวาดรอบก่อนมองไม่เห็น เพราะทดสอบแต่ภาษาไทยซึ่งเป็นค่าเริ่มต้น
 *    ‼️ ระบบสองภาษาต้องตรวจทั้งสองภาษาเสมอ — ความยาวข้อความคือส่วนหนึ่งของเลย์เอาต์
 */
.action-side {
  flex: 0 1 auto;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  align-items: stretch;
  min-width: 0;
}

/* ‼️ ปุ่มในการ์ดสั่งงานต้องตัดบรรทัดได้เมื่อป้ายยาวเกินกล่อง — ความสูงจึงยืดตามเนื้อหา
      ‼️ จำกัดไว้เฉพาะที่นี่ ห้ามแก้ `.btn` ทั้งชุด เพราะปุ่มในแถบเครื่องมือและในตาราง
         ต้องสูงเท่ากันทุกใบ ไม่งั้นแถวจะกระโดดไม่เท่ากัน */
.action-side .btn {
  height: auto;
  min-height: var(--btn-h);
  padding-block: var(--sp-2);
  white-space: normal;
  text-align: center;
  overflow-wrap: anywhere;
  line-height: var(--lh-tight);
}

/* ── โซนอันตราย ───────────────────────────────────────────────────────
   ‼️ แยกออกมาให้เห็นชัดว่าข้ามเส้นแล้ว ‼️ ด่านทุกข้อเท่าเดิม:
      วลียืนยัน + รหัสผ่าน + CSRF + submit token + สิทธิ์ที่ route */
.danger-zone {
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  min-width: 0;
}

.danger-zone > .danger-head {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  background: var(--danger-soft);
  border-bottom: 1px solid var(--danger);
  min-width: 0;
}

.danger-title { color: var(--danger-text); font-weight: var(--fw-semi); font-size: var(--fs-lg); overflow-wrap: anywhere; }
.danger-body  { padding: var(--sp-5); display: grid; gap: var(--sp-4); }
.danger-note  { color: var(--muted); font-size: var(--fs-sm); }

/* ── รายละเอียดทางเทคนิค (พับไว้เป็นค่าเริ่มต้น) ────────────────────────
   ‼️ ใช้ <details> ของเบราว์เซอร์ ไม่มี JavaScript — เปิดได้แม้สคริปต์โหลดไม่สำเร็จ
   ‼️ ห้ามมี horizontal scroll ในการ์ด: ใช้ grid ที่ยุบเองตามความกว้าง */
.tech { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); overflow: hidden; }
.tech + .tech { margin-top: var(--sp-2); }

.tech > summary {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4);
  cursor: pointer;
  font-weight: var(--fw-medium);
  list-style: none;
  min-width: 0;
}

.tech > summary::-webkit-details-marker { display: none; }
.tech > summary:hover { background: var(--surface2); }
.tech > summary:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }

.tech-caret { margin-inline-start: auto; color: var(--faint); transition: transform var(--transition); flex: none; }
.tech[open] > summary .tech-caret { transform: rotate(180deg); }
.tech[open] > summary { border-bottom: 1px solid var(--hairline); }

.tech-body { padding: var(--sp-4); display: grid; gap: var(--sp-4); }

/* ‼️ 3 คอลัมน์บนจอกว้าง · 2 บนแท็บเล็ต · 1 บนมือถือ — auto-fit ทำให้ไม่ต้องเขียน
   media query และไม่มีทางเกิดแถวที่กว้างเกินกล่อง */
.tech-grid { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

.tech-item { display: grid; gap: var(--sp-1); min-width: 0; padding: var(--sp-3); background: var(--surface2); border-radius: var(--radius-xs); }
.tech-key  { font-size: var(--fs-xs); color: var(--muted); overflow-wrap: anywhere; }
.tech-val  { font-family: var(--font-mono); font-size: var(--fs-sm); color: var(--text); overflow-wrap: anywhere; }

/* ── แถบงานที่กำลังทำ (ลอยติดขอบล่าง) ──────────────────────────────────
   ‼️ ต้องไม่บังปุ่มหรือเมนู — ลอยล่างและกันที่ให้เนื้อหาด้วย padding ของ .content
   ‼️ ตัวเลขทุกตัวมาจากแถวจริงใน migration_runs ‼️ ห้ามเดินแถบเอง */
.run-bar {
  position: sticky;
  bottom: var(--sp-3);
  z-index: var(--z-sticky);
  display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap;
  margin-top: var(--sp-5);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--primary);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  min-width: 0;
}

.run-bar-main { flex: 1 1 220px; min-width: 0; display: grid; gap: var(--sp-1); }
.run-bar-name { font-weight: var(--fw-semi); overflow-wrap: anywhere; }
.run-bar-meta { font-size: var(--fs-sm); color: var(--muted); font-variant-numeric: tabular-nums; }
.run-bar .progress { flex: 1 1 160px; min-width: 120px; }

/* ‼️‼️ ใบที่ค้างคิวโดยไม่มีตัวเดินงาน — ‼️ ต้องอ่านไม่เหมือนงานที่กำลังเดินเด็ดขาด
   ‼️ แถบสีเดียวกับตอนทำงานปกติคือสิ่งที่ทำให้ผู้ดูแลรอใบ #6 ข้ามวันบนเครื่องทดสอบ
      ‼️ จึงเปลี่ยนทั้งขอบ พื้น และตัวอักษรพร้อมกัน ไม่ใช่แค่เปลี่ยนคำ
   ‼️ ใช้ตัวแปรสีของธีม จึงถูกต้องทั้งโหมดสว่างและมืดโดยไม่ต้องเขียนซ้ำ (กฎเหล็กข้อ 8) */
.run-bar.is-stalled { border-color: var(--danger); background: var(--danger-soft); }
.run-bar.is-stalled .run-bar-meta { color: var(--danger); }

/* ── ตารางจำนวนข้อมูลรายโมดูล ─────────────────────────────────────────
   ‼️ ตัวเลขชิดขวาและใช้ตัวเลขความกว้างเท่ากัน เพื่อให้เทียบด้วยสายตาได้
   ‼️ "คงเหลือ" เป็นสีแดงเฉพาะเมื่อ **เริ่มย้ายแล้วแต่ยังไม่ครบ** เท่านั้น
      ‼️ ทำให้ทุกค่าต้นทางแดงตั้งแต่ยังไม่เริ่ม คือการเตือนที่ไม่มีความหมาย
         แล้วคนจะเลิกสนใจสีแดงทั้งหน้า */
.num { text-align: end; font-variant-numeric: tabular-nums; font-family: var(--font-mono); }
.mod-progress { display: flex; align-items: center; gap: var(--sp-2); min-width: 120px; }
.mod-progress .progress { flex: 1 1 auto; }
.mod-percent { font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; flex: none; }

/* ── สถานะผิดพลาดกลาง ────────────────────────────────────────────────
   ‼️ ต้องบอกสาเหตุและทางแก้ ‼️ ห้ามมีแค่ "เกิดข้อผิดพลาด" */
.error-state {
  display: grid; gap: var(--sp-2); justify-items: center; text-align: center;
  padding: var(--sp-6) var(--sp-4);
  border: 1px dashed var(--danger);
  border-radius: var(--radius-sm);
  background: var(--danger-soft);
}
.error-state .error-title { font-weight: var(--fw-semi); color: var(--danger-text); }
.error-state .error-text  { color: var(--muted); font-size: var(--fs-sm); max-width: 60ch; }

/* ── ภาพรวมด้านบน ────────────────────────────────────────────────────
   ‼️ ต้นทาง → ปลายทาง อ่านเป็นทิศทางเดียว ไม่ใช่สองกล่องที่ไม่รู้อันไหนก่อน */
.overview { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); align-items: start; }
.flow { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; min-width: 0; }
.flow-node { display: grid; gap: var(--sp-1); min-width: 0; }
.flow-arrow { color: var(--faint); flex: none; }

@media (max-width: 900px) {
  /* ‼️ แถบลำดับขั้นเปลี่ยนเป็นแนวตั้งบนจอแคบ — บีบ 5 ขั้นให้อยู่แนวนอนบนมือถือ
     ทำให้ตัวหนังสือเล็กจนอ่านไม่ออก ซึ่งเป็นสิ่งที่หน้านี้ถูกบ่นมาตั้งแต่ต้น */
  .stepper { grid-auto-flow: row; grid-auto-columns: auto; gap: var(--sp-4); padding: var(--sp-4); }
  .step { flex-direction: row; align-items: flex-start; text-align: start; gap: var(--sp-3); }
  .step-body { display: grid; gap: var(--sp-1); min-width: 0; }

  .step + .step::before {
    inset-inline-end: auto;
    inset-inline-start: 17px;
    top: -18px;
    width: 2px;
    height: 18px;
  }
}

/**
 * ‼️ ค่าที่เป็น "ข้อความ" ไม่ใช่ "ตัวเลข"
 *
 * ‼️ `.stat-value` ถูกออกแบบมาสำหรับตัวเลขสรุป — ตัวใหญ่ 26px หนา สีม่วง
 *    ‼️ พอเอาไปใส่ชื่อฐานข้อมูล / ลายนิ้วมือ / ข้อความสถานะ มันกลายเป็นพาดหัวสีม่วง
 *       ที่ตะโกนใส่ผู้ใช้ทั้งที่ไม่ใช่ตัวเลขสำคัญ ‼️ และชื่อยาวๆ ตัดบรรทัดเป็นสองแถวใหญ่
 *    ‼️ เห็นจริงจากภาพหน้าจอ: `omcsupport_legacy_snapshot` กินพื้นที่มากกว่าตัวเลข
 *       จำนวน ticket ที่เป็นข้อมูลสำคัญกว่าหลายเท่า
 */
.stat-value.is-text {
  font-size: var(--fs-lg);
  font-weight: var(--fw-semi);
  color: var(--text);
}

/* ═══════════════════════════════════════════════════════════════════════
   ‼️‼️ ฟอนต์ไทย — แยกช่วงอักษรออกมาโดยตรง
   ═══════════════════════════════════════════════════════════════════════

   ‼️ ปัญหาจริงที่วัดได้: `--font` ขึ้นต้นด้วย 'Inter' ซึ่ง **ไม่มีอักษรไทย**
      ไทยจึงตกไปที่ตัวถัดไปคือ 'Segoe UI' ‼️ ซึ่งมีอักษรไทยแต่เส้นบางและสระเบียด
      ‼️ ส่วน 'Noto Sans Thai' ที่ตั้งใจให้ใช้ ถูกวางไว้ลำดับที่ 7 — **หลัง** Segoe UI
         จึงไม่มีวันถูกเรียกใช้บน Windows เลยแม้แต่ครั้งเดียว

   ‼️ การสลับลำดับเฉยๆ แก้ไม่ได้: ถ้าเอาฟอนต์ไทยขึ้นก่อน Segoe UI มันจะไปกินอักษร
      ละตินด้วย (ฟอนต์ไทยเกือบทุกตัวมีละตินในตัว) แล้วหน้าตาภาษาอังกฤษจะเปลี่ยนไปทั้งระบบ

   ‼️ ทางที่ถูกคือประกาศ @font-face ที่ **จองเฉพาะช่วงรหัสอักษรไทย** (U+0E00–U+0E7F)
      แล้ววางไว้หน้าสุด ‼️ อักษรละตินจะไม่เข้าเงื่อนไขนี้เลย จึงไหลไปใช้ Inter/Segoe ตามเดิม

   ‼️ ใช้ local() ล้วน — ไม่ดาวน์โหลดไฟล์ใดๆ ‼️ จึงไม่ขัด CSP (font-src 'self')
      และไม่ต้องเพิ่มไฟล์ฟอนต์เข้า repo
      ‼️ ผลพลอยได้ที่สำคัญ: ไม่มีการโหลดผ่านเครือข่าย จึง **ไม่มี FOUT**
         ตัวอักษรไม่กระโดดหรือเปลี่ยนหน้าตากลางคันตอนหน้าโหลด

   ‼️‼️ กรณีแย่ที่สุด — เครื่องที่ **ไม่มีฟอนต์ในรายการเลยสักตัว**
      ‼️ ต้องไม่ทำให้หน้าแตกและต้องไม่ขึ้นสี่เหลี่ยมเปล่า
      ตามสเปก CSS ถ้า src ของ @font-face ใช้ไม่ได้ทั้งหมด family นั้นถือว่า
      "ไม่มีอยู่" สำหรับช่วงรหัสนี้ แล้วเบราว์เซอร์จะไหลไปใช้ family ถัดไปใน --font ตามปกติ
      ‼️ ซึ่งคือพฤติกรรมเดิมก่อนงานนี้ (Segoe UI / system-ui) — แย่ลงกว่าเดิมไม่ได้
      ‼️ พิสูจน์ด้วยเบราว์เซอร์จริงแล้วโดยแทน src ด้วยชื่อฟอนต์ที่ไม่มีอยู่
   ‼️ เรียงจากดีสุดไปสำรองสุด: Noto Sans Thai (Linux/Android/เครื่องที่ลงเอง)
      → IBM Plex Sans Thai → Leelawadee UI (มากับ Windows 8 ขึ้นไป) → Thonburi (macOS/iOS)
      → Tahoma (มีทุกเครื่อง Windows เป็นตาข่ายกันตก)
   ─────────────────────────────────────────────────────────────────────── */

@font-face {
  font-family: 'OMC Thai';
  font-style: normal;
  font-weight: 100 900;
  src: local('Noto Sans Thai'), local('NotoSansThai-Regular'), local('Noto Sans Thai UI'),
       local('IBM Plex Sans Thai'),
       /* macOS / iOS — Sukhumvit Set คือฟอนต์ไทยของระบบ Apple · Thonburi คือรุ่นเก่า */
       local('Sukhumvit Set'), local('SukhumvitSet-Text'), local('Thonburi'),
       /* Windows 8 ขึ้นไป */
       local('Leelawadee UI'), local('LeelawadeeUI'),
       /* ตาข่ายกันตกชั้นสุดท้าย — มีทุกเครื่อง Windows และเครื่องไทยส่วนใหญ่ */
       local('Sarabun'), local('Tahoma');
  unicode-range: U+0E00-0E7F;
}

/* ‼️ ตัวหนาของไทยต้องชี้ไปที่ไฟล์ตัวหนาจริง ไม่ใช่ให้เบราว์เซอร์ปลอมความหนาเอง
      ‼️ ความหนาปลอม (synthetic bold) ทำให้สระและวรรณยุกต์ของไทยเลอะติดกัน */
@font-face {
  font-family: 'OMC Thai';
  font-style: normal;
  font-weight: 600 900;
  src: local('Noto Sans Thai SemiBold'), local('NotoSansThai-SemiBold'),
       local('Noto Sans Thai Bold'), local('NotoSansThai-Bold'),
       local('IBM Plex Sans Thai SemiBold'),
       local('Sukhumvit Set Bold'), local('SukhumvitSet-Bold'), local('Thonburi Bold'),
       local('Leelawadee UI Bold'), local('Sarabun Bold'), local('Tahoma Bold');
  unicode-range: U+0E00-0E7F;
}

/* ═══════════════════════════════════════════════════════════════════════
   Work list — หน้ารายการงานที่ต้องสแกนเร็วและจัดการหลายรายการ
   ═══════════════════════════════════════════════════════════════════════
   ‼️ ทุกสีมาจาก token ‼️ ห้ามค่าดิบ (ThemeContractTest คุมอยู่)
   ─────────────────────────────────────────────────────────────────────── */

/* ── แถบตัวกรองที่เกาะอยู่บนสุด ────────────────────────────────────────
   ‼️ ผู้ใช้กรองแล้วเลื่อนลงไปดูผล พอจะแก้ตัวกรองต้องเลื่อนกลับขึ้นไปทั้งหน้า
      ‼️ ยิ่งรายการยาว ยิ่งเสียเวลา — 15,686 งานคือเลื่อนกันจนลืมว่ากรองอะไรไว้
   ‼️ ใช้ sticky ไม่ใช่ fixed — fixed จะทับเนื้อหาบนจอเตี้ย */
.filterbar {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-xs);
  margin-bottom: var(--sp-4);
}

.filterbar-body { padding: var(--sp-4); display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: flex-end; }

/* ‼️ แถบสรุปผลต้องอยู่ในสายตาเสมอ — "กรองแล้วเหลือกี่รายการ" คือคำตอบแรกที่คนมองหา */
.filterbar-foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--hairline);
  background: var(--surface2);
  border-radius: 0 0 var(--radius) var(--radius);
  min-width: 0;
}

.result-count { font-size: var(--fs-sm); color: var(--muted); font-variant-numeric: tabular-nums; }
.result-count strong { color: var(--text); }

/* ── ชิปกรองด่วน ───────────────────────────────────────────────────────
   ‼️ ตัวกรองที่ใช้บ่อยที่สุดต้องกดได้ในครั้งเดียว ไม่ใช่เปิด dropdown แล้วเลื่อนหา */
.quick-filters { display: flex; flex-wrap: wrap; gap: var(--sp-2); padding: 0 var(--sp-4) var(--sp-4); }

.quick-filter {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  text-decoration: none;
  min-height: 32px;
}

.quick-filter:hover { border-color: var(--primary); color: var(--primary-text); background: var(--primary-soft); text-decoration: none; }
.quick-filter.is-active { border-color: var(--primary); background: var(--primary-soft); color: var(--primary-text); }
.quick-filter .count { color: var(--faint); font-variant-numeric: tabular-nums; }
.quick-filter.is-active .count { color: var(--primary-text); }

/* ── หัวตารางที่กดเรียงได้ ─────────────────────────────────────────────
   ‼️ ต้องเห็นชัดว่ากดได้และตอนนี้เรียงด้วยอะไร ‼️ ลูกศรอย่างเดียวไม่พอสำหรับคนที่แยกสีไม่ได้ */
th .sort-link {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  color: inherit; text-decoration: none;
  padding: var(--sp-1) var(--sp-2);
  margin: calc(var(--sp-1) * -1) calc(var(--sp-2) * -1);
  border-radius: var(--radius-xs);
  white-space: nowrap;
}

th .sort-link:hover { background: var(--surface3); color: var(--primary-text); text-decoration: none; }
th .sort-link:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
th .sort-caret { opacity: .35; flex: none; }
th .sort-link.is-sorted { color: var(--primary-text); }
th .sort-link.is-sorted .sort-caret { opacity: 1; }

/* ── แถวที่กดได้ ──────────────────────────────────────────────────────
   ‼️ ทั้งแถวเป็นเป้าคลิกที่ใหญ่ขึ้นมาก — แต่ทำด้วยลิงก์ในเซลล์ ไม่ใช่ onclick ที่แถว
      ‼️ CSP ห้าม inline handler และแถวที่กดได้ด้วย JS จะกด Ctrl+คลิกเปิดแท็บใหม่ไม่ได้ */
tbody tr:hover { background: var(--surface2); }
tbody tr:focus-within { background: var(--primary-soft); }

/* ── สถานะของรายการ ────────────────────────────────────────────────── */
.list-state {
  display: grid; gap: var(--sp-2); justify-items: center; text-align: center;
  padding: var(--sp-7) var(--sp-4);
}

.list-state .state-icon { color: var(--faint); }
.list-state .state-title { font-weight: var(--fw-semi); font-size: var(--fs-lg); }
.list-state .state-text { color: var(--muted); max-width: 52ch; }
.list-state .state-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; justify-content: center; margin-top: var(--sp-2); }

/* ── จำนวนต่อหน้า ─────────────────────────────────────────────────── */
.per-page { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); color: var(--muted); }
.per-page .form-control { height: var(--btn-h-sm); padding-block: 0; width: auto; }

/* ═══ มือถือ: ตารางกลายเป็นการ์ดรายแถว ═════════════════════════════════
   ‼️‼️ ป้ายของแต่ละค่ามาจาก `data-label` ที่ PHP ใส่ผ่าน __() เท่านั้น
      ‼️ ห้ามเขียนข้อความลงใน content: "..." ของ CSS เด็ดขาด — CSS แปลภาษาไม่ได้
         ผู้ใช้ภาษาอังกฤษจะเห็นป้ายไทยค้างอยู่ (กฎเหล็กข้อ 12)
   ‼️ ยัง `<table>` เหมือนเดิมและใส่ role ไว้ครบ — การเปลี่ยน display ทำให้เบราว์เซอร์
      ทิ้ง role โดยปริยายของตารางไป ‼️ โปรแกรมอ่านหน้าจอจะอ่านเป็นข้อความกองเดียว
   ─────────────────────────────────────────────────────────────────────── */
@media (max-width: 767px) {
  /* ‼️ กล่องต้องเลิกเลื่อนแนวนอน ไม่งั้นการ์ดจะยังกว้างเกินจอ */
  .table-cards { overflow-x: visible; }

  /**
   * ‼️‼️ ต้องล้าง min-width ของตารางทิ้ง
   *
   * ‼️ `.table-wrap table` ถูกตั้ง `min-width: 720px` ไว้บนมือถือโดยตั้งใจ
   *    เพื่อกันไม่ให้คอลัมน์ชื่อถูกย่อจนข้อความไทยหักบรรทัดละ 2-3 คำ
   *    ‼️ เหตุผลนั้นใช้กับตารางที่ **ยังเป็นตาราง** เท่านั้น
   * ‼️ ตารางที่ยุบเป็นการ์ดแล้วไม่มีคอลัมน์ให้ย่ออีก แต่ min-width ยังบังคับให้กว้าง 720px
   *    ‼️ วัดจริง: หน้ายังล้นเป็น 733px และผู้ใช้ยังต้องเลื่อนซ้ายขวาอยู่ดี
   *       ซึ่งคือสิ่งเดียวกับที่การยุบเป็นการ์ดตั้งใจจะแก้
   */
  .table-cards table { min-width: 0; }

  .table-cards table,
  .table-cards tbody,
  .table-cards tr,
  .table-cards td { display: block; width: 100%; }

  /* หัวตารางไม่ต้องแสดง แต่ต้องยังอยู่ให้โปรแกรมอ่านหน้าจออ่านได้ */
  .table-cards thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }

  .table-cards tr {
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    padding: var(--sp-3);
    margin-bottom: var(--sp-3);
    box-shadow: var(--shadow-xs);
  }

  .table-cards tr:hover { background: var(--surface); }
  .table-cards tr:focus-within { border-color: var(--primary); box-shadow: var(--ring); }

  /**
   * ‼️‼️ เซลล์เป็น `display: block` ‼️ **ห้ามใช้ flex**
   *
   * ‼️ ค่าในเซลล์เป็น **ข้อความล้วน** ไม่ใช่ element จึงกลายเป็น anonymous flex item
   *    ที่ CSS เลือกไม่ได้ และบังคับ `min-width: 0` ให้มันไม่ได้
   *    ‼️ ผลคือข้อความไทยยาวๆ ล้นออกนอกกล่องโดย **ไม่ขยายกล่อง**
   *       วัดจริงด้วยข้อความไทย 140 ตัวไม่มีเว้นวรรค: การ์ดยังกว้าง 364px
   *       แต่ทั้งหน้าเลื่อนได้ถึง 1,416px
   *       ‼️ ตัวไล่หา element ที่กว้างเกินจอหาไม่เจอเลย เพราะไม่มี element ไหนกว้างจริง
   *       ‼️ `flex-wrap: wrap` ช่วยได้แค่บางส่วน (1,416 → 1,276) ยังไม่หายขาด
   *
   * ‼️ block layout ไม่มีปัญหานี้เลย เพราะข้อความไหลตามปกติและตัดคำได้เต็มความกว้าง
   *    ‼️ แลกกับป้ายไปอยู่บรรทัดบนของค่าแทนที่จะอยู่ซ้าย — อ่านง่ายกว่าบนจอแคบอยู่แล้ว
   *       และสำคัญกว่าคือ **ไม่มีทางล้น** ไม่ว่าเนื้อหาจะยาวแค่ไหน
   */
  .table-cards td {
    display: block;
    padding: var(--sp-2) 0;
    border: 0;
    border-bottom: 1px solid var(--hairline);
    min-width: 0;
    max-width: 100%;
    overflow-wrap: anywhere;
    word-break: break-word;

    /**
     * ‼️‼️ ต้องล้าง `white-space: nowrap` ทิ้ง — **นี่คือตัวการตัวจริง**
     *
     * ‼️ `.col-code` และ `.cell-actions` ตั้ง nowrap ไว้ ซึ่งถูกต้องสำหรับตารางที่ยังเป็นตาราง
     *    (รหัสงานและปุ่มจัดการไม่ควรหักบรรทัด)
     * ‼️ แต่ `white-space: nowrap` **ทับ `overflow-wrap` ทั้งหมด** — ตั้ง anywhere
     *    หรือ break-word ไว้แค่ไหนก็ไม่มีผล เพราะเบราว์เซอร์ห้ามขึ้นบรรทัดใหม่ไปแล้ว
     *    ‼️ วัดจริง: td กว้าง 338px แต่เนื้อหายาว 1,250px ล้นออกไปเฉยๆ
     *       และไม่มี element ไหน "กว้างเกินจอ" ให้ไล่หาเจอเลย
     * ‼️ เสียเวลาไล่ผิดทางไปหลายรอบเพราะดู overflow-wrap อย่างเดียว
     *    ‼️ บทเรียน: nowrap มาก่อน overflow-wrap เสมอ ต้องล้างมันก่อน
     */
    white-space: normal;
  }

  .table-cards td:last-child { border-bottom: 0; padding-bottom: 0; }

  /* ‼️ ป้ายมาจาก data-label ที่ PHP ใส่ไว้ — เปลี่ยนภาษาแล้วป้ายเปลี่ยนตาม */
  .table-cards td[data-label]::before {
    content: attr(data-label);
    display: block;
    color: var(--muted);
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    overflow-wrap: anywhere;
  }

  /* ‼️ ลูกทุกตัวต้องหดได้และตัดคำได้ ไม่งั้นลิงก์ยาวๆ จะดันกล่องเหมือนกัน */
  .table-cards td > * { min-width: 0; max-width: 100%; overflow-wrap: anywhere; }

  /**
   * ‼️ ช่องหลักของแถว (รหัส + เรื่อง) ต้องเต็มความกว้างและอ่านก่อนเสมอ
   *    ‼️ บีบเป็นคู่ป้าย-ค่าเหมือนช่องอื่นจะทำให้หัวเรื่องเหลือครึ่งจอและอ่านไม่ออก
   */
  .table-cards td[data-primary] {
    text-align: start;
    padding-top: 0;
    font-size: var(--fs-lg);
  }

  .table-cards td[data-primary]::before { display: none; }

  /* ‼️ ตารางที่ยัง "ต้องเป็นตาราง" จริงๆ (เช่น matrix สิทธิ์) ไม่ควรถูกยุบ
        จึงต้องติดคลาส .table-cards เอง ไม่ใช่ยุบทุกตารางในระบบโดยอัตโนมัติ */

  .filterbar-body { padding: var(--sp-3); }
  .filterbar-body .field { flex: 1 1 100%; }
  .quick-filters { padding: 0 var(--sp-3) var(--sp-3); }
  .filterbar-foot { padding: var(--sp-3); }
}

/* ── กล่องตัวกรองที่พับได้ ─────────────────────────────────────────────
   ‼️ บนมือถือ ตัวกรอง 11 ช่องกินเต็มจอแรก ผู้ใช้ต้องเลื่อนผ่านทั้งหมดกว่าจะเห็นงานใบแรก
   ‼️ ช่องค้นหาอยู่นอกกล่องพับเสมอ — เป็นสิ่งที่คนใช้บ่อยที่สุด ซ่อนไว้จะต้องกดสองครั้งทุกครั้ง */
.filterbar-lead { border-bottom: 1px solid var(--hairline); }

.filter-panel > summary {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  cursor: pointer;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--muted);
  list-style: none;
  min-height: 40px;
}

.filter-panel > summary::-webkit-details-marker { display: none; }
.filter-panel > summary:hover { color: var(--primary-text); background: var(--surface2); }
.filter-panel > summary:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.filter-panel[open] > summary { border-bottom: 1px solid var(--hairline); color: var(--text); }
.filter-panel[open] > summary .tech-caret { transform: rotate(180deg); }

/* ═══════════════════════════════════════════════════════════════════════
   หน้ารายละเอียด (archetype A/C) — `.kv` เป็น primitive ที่ 19 หน้าใช้ร่วมกัน
   ═══════════════════════════════════════════════════════════════════════
   ‼️ แก้ที่นี่ที่เดียวครอบทุกหน้ารายละเอียด ‼️ ห้ามไปเขียนซ้ำในแต่ละหน้า
   ─────────────────────────────────────────────────────────────────────── */

/**
 * ‼️ หน้ารายละเอียดของระบบนี้ยาวมาก (ticket มี 12 คู่ · task มีมากกว่านั้น)
 *    ‼️ คู่ป้าย-ค่าที่ไหลติดกันเป็นพืดทำให้สายตาหาบรรทัดที่ต้องการไม่เจอ
 *       ต้องกวาดอ่านทีละบรรทัดตั้งแต่ต้น
 * ‼️ เส้นคั่นบางๆ ให้สายตาเกาะได้ทีละแถว — ไม่ได้เพิ่มความสูงมากแต่สแกนง่ายขึ้นชัด
 */
.kv dt,
.kv dd { padding-block: var(--sp-2); border-top: 1px solid var(--hairline); min-width: 0; }

/* ‼️ แถวแรกไม่ต้องมีเส้น — มันจะดูเหมือนเส้นใต้หัวข้อการ์ดที่อยู่เหนือขึ้นไป */
.kv dt:first-of-type,
.kv dt:first-of-type + dd { border-top: 0; padding-top: 0; }

/* ป้ายต้องอ่านออกโดยไม่แย่งความเด่นจากค่า */
.kv dt { font-weight: var(--fw-medium); }

/* ‼️ ค่ายาวๆ (ลิงก์ · ชื่อไฟล์ · path) ต้องตัดคำได้ ไม่ดันการ์ด */
.kv dd { overflow-wrap: anywhere; }

/* ── แถบข้อมูลย่อของหน้ารายละเอียด ────────────────────────────────────
   ‼️ ตอบ "ใบนี้คืออะไร ของใคร สถานะไหน" ได้ในบรรทัดเดียวโดยไม่ต้องอ่าน kv ทั้งชุด */
.detail-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4);
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--hairline);
  background: var(--surface2);
  min-width: 0;
}

.detail-meta > * { min-width: 0; }
.detail-meta .meta-label { color: var(--muted); font-size: var(--fs-sm); }

/* ── ฟอร์ม (archetype D) ──────────────────────────────────────────────
   ‼️ ฟอร์มของระบบนี้ยาว (ฟอร์ม ticket มี 14 ช่อง) — ต้องแบ่งกลุ่มให้เห็นว่าเหลืออีกเท่าไร */
.form-section { border-top: 1px solid var(--hairline); padding-top: var(--sp-5); margin-top: var(--sp-5); }
.form-section:first-child { border-top: 0; padding-top: 0; margin-top: 0; }
.form-section-title { font-weight: var(--fw-semi); margin-bottom: var(--sp-1); }
.form-section-hint { color: var(--muted); font-size: var(--fs-sm); margin-bottom: var(--sp-4); }

/* ‼️ ข้อความช่วยกรอกต้องอ่านออก — เดิมใช้ --faint ซึ่งจางที่สุดในระบบ
      ‼️ คำอธิบายวิธีกรอกที่อ่านไม่ออกคือคำอธิบายที่ไม่มีอยู่ */
.field-hint { color: var(--muted); }

/* ‼️ ข้อผิดพลาดต้องอยู่ติดช่องที่ผิด ไม่ใช่กองรวมบนสุดของฟอร์ม
      ‼️ ฟอร์มยาวๆ ที่รวมข้อผิดพลาดไว้ข้างบน ทำให้ต้องเลื่อนหาเองว่าช่องไหนผิด */
.field-error { color: var(--danger-text); font-size: var(--fs-sm); font-weight: var(--fw-medium); }
.field-error:empty { display: none; }

/**
 * ‼️ การ์ดสรุปที่ไม่มีอะไรต้องทำ (ค่าเป็นศูนย์)
 *
 * ‼️ ทำให้จางลง **ไม่ใช่ซ่อน** — ซ่อนแล้วผู้ใช้จะไม่รู้ว่าระบบตรวจเรื่องนี้อยู่
 *    และจะไม่รู้ว่า "ศูนย์" คือข่าวดี
 * ‼️ ยังกดได้ ยัง hover ได้ และพอ hover จะกลับมาเข้มเต็มที่
 *    ‼️ ห้ามลดจนอ่านไม่ออก — ตัวอักษรยังต้องผ่าน contrast
 */
.summary-card.is-quiet { background: var(--surface2); border-color: var(--hairline); box-shadow: none; }
.summary-card.is-quiet .summary-icon { background: var(--surface3); color: var(--faint); }
.summary-card.is-quiet .summary-value { color: var(--muted); }
.summary-card.is-quiet:hover { background: var(--surface); border-color: var(--border); }

/* ═══════════════════════════════════════════════════════════════════════
   Migration Command Center — โครงสองคอลัมน์ + component ร่วมของหน้าระบบ
   ═══════════════════════════════════════════════════════════════════════
   ‼️ ทุกสีมาจาก token ‼️ ห้ามค่าดิบ (ThemeContractTest คุมอยู่)
   ‼️ ทุกอย่างทำงานได้โดยไม่มี JS: accordion = <details> · ตัวกรอง = GET form
   ‼️ ความหนาแน่น 14px compact — ระยะห่างในการ์ดใช้ --sp-3/--sp-4 ไม่ใช่ --sp-5
   ─────────────────────────────────────────────────────────────────────── */

/* ── โครงสองคอลัมน์ — ซ้าย = สิ่งที่ต้องอ่าน/ทำ · ขวา sticky = ขั้นตอนถัดไป ── */
.cmd-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: var(--sp-4);
  align-items: start;
  margin-top: var(--sp-4);
  min-width: 0;
}

.cmd-main,
.cmd-aside { display: grid; gap: var(--sp-4); min-width: 0; }

/* ‼️ sticky เฉพาะจอกว้าง — ถัดจากขอบบนเล็กน้อย ไม่ทับ topbar */
.cmd-aside { position: sticky; top: var(--sp-4); }

/* ‼️ การ์ดใน grid ห่างกันด้วย gap แล้ว — ตัด margin ของ .card + .card ทิ้ง ไม่งั้นได้ระยะซ้อน */
.cmd-grid .card + .card,
.cmd-grid .card + details,
.cmd-grid details + .card { margin-top: 0; }

/* ‼️ อะไรที่อยู่ในคอลัมน์ต้องหดได้ ไม่งั้นตารางกว้างจะดันคอลัมน์ขวาตกจอ */
.cmd-main > *,
.cmd-aside > * { min-width: 0; }

/* ‼️ การ์ดของหน้านี้แน่นกว่าการ์ดทั่วไปเล็กน้อย */
.cmd-grid .card-title { font-size: var(--fs-md); margin-bottom: 0; }
.cmd-grid .card-head { margin-bottom: var(--sp-3); align-items: center; }
.cmd-grid .card-head .field-hint { font-size: var(--fs-xs); }

.page-head-meta { margin-top: var(--sp-2); text-align: end; flex: 0 1 auto; }

@media (max-width: 1024px) {
  .cmd-grid { grid-template-columns: minmax(0, 1fr); }
  .cmd-aside { position: static; }
  .page-head-meta { text-align: start; }
}

/* ── แถบสรุปสถานะด้านบน ───────────────────────────────────────────────── */
.ops-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
  min-width: 0;
}

.ops-item {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
  color: var(--text);
  min-width: 0;
}

a.ops-item:hover { border-color: var(--primary-soft2); box-shadow: var(--shadow-md); text-decoration: none; }
a.ops-item:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.ops-icon {
  flex: none;
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border-radius: var(--radius-sm);
  background: var(--surface2);
  color: var(--faint);
}

.ops-body { display: grid; gap: 2px; min-width: 0; flex: 1 1 auto; }
.ops-label { font-size: var(--fs-xs); color: var(--muted); overflow-wrap: anywhere; }
.ops-value { font-size: var(--fs-lg); font-weight: var(--fw-semi); line-height: var(--lh-tight); overflow-wrap: anywhere; }
.ops-hint  { font-size: var(--fs-xs); color: var(--muted); overflow-wrap: anywhere; }

.ops-item.is-success .ops-value { color: var(--success-text); }
.ops-item.is-success .ops-icon  { background: var(--success-soft); color: var(--success-text); }
.ops-item.is-warning .ops-value { color: var(--warning-text); }
.ops-item.is-warning .ops-icon  { background: var(--warning-soft); color: var(--warning-text); }
.ops-item.is-danger  .ops-value { color: var(--danger-text); }
.ops-item.is-danger  .ops-icon  { background: var(--danger-soft); color: var(--danger-text); }
.ops-item.is-info    .ops-value { color: var(--info-text); }
.ops-item.is-info    .ops-icon  { background: var(--info-soft); color: var(--info-text); }
.ops-item.is-primary .ops-icon  { background: var(--primary-soft); color: var(--primary-text); }

/* ‼️ ช่องที่ถือปุ่มจริง (ฟอร์มจาก migration-job) — ปุ่มกินเต็มช่อง */
.ops-item.is-action { border-style: dashed; align-items: center; }
.ops-item.is-action .migration-job { display: grid; gap: var(--sp-2); }
.ops-item.is-action .migration-job .btn { width: 100%; }
.ops-item.is-action .field-hint { font-size: var(--fs-xs); }

/* ── KPI ในการ์ด (4 โมดูล) ───────────────────────────────────────────── */
.kpi-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }
.kpi-grid-inline { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

.kpi {
  display: grid; gap: 2px; min-width: 0;
  padding: var(--sp-3);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: var(--surface2);
}

.kpi-label { font-size: var(--fs-xs); color: var(--muted); overflow-wrap: anywhere; }
.kpi-value { font-size: var(--fs-xl); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; line-height: var(--lh-tight); }
.kpi-hint  { font-size: var(--fs-xs); color: var(--faint); overflow-wrap: anywhere; }

/* ── การ์ดขั้นตอนถัดไป ──────────────────────────────────────────────── */
.next-action { border-color: var(--primary-soft2); }
.next-action.is-done { border-color: var(--success); }

.next-box {
  display: grid; gap: var(--sp-2);
  padding: var(--sp-4);
  border: 1px solid var(--primary-soft2);
  border-radius: var(--radius-sm);
  background: var(--primary-soft);
  min-width: 0;
}

.next-action.is-done .next-box { border-color: var(--success); background: var(--success-soft); }

.next-title { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); font-weight: var(--fw-semi); font-size: var(--fs-md); overflow-wrap: anywhere; }
.next-pending { font-size: var(--fs-sm); font-weight: var(--fw-medium); }
.next-desc { font-size: var(--fs-sm); color: var(--muted); overflow-wrap: anywhere; }

/* ‼️ ฟอร์มปุ่มมาจาก migration-job ทั้งก้อน (รวมช่องวลียืนยัน/รหัสผ่านถ้าเงื่อนไขครบ) */
.next-cta { display: grid; gap: var(--sp-2); }
.next-cta .migration-job { display: grid; gap: var(--sp-2); }
.next-cta .btn { width: 100%; height: auto; min-height: var(--btn-h); white-space: normal; padding-block: var(--sp-2); line-height: var(--lh-tight); }
.next-cta .field-hint { font-size: var(--fs-xs); }

.next-note {
  display: flex; gap: var(--sp-2); align-items: flex-start;
  margin-top: var(--sp-3);
  font-size: var(--fs-xs); color: var(--muted);
}
.next-note svg { flex: none; margin-top: 2px; color: var(--success-text); }

/* ── รายการตรวจ (checklist) + สรุปจำนวน ────────────────────────────── */
.checklist { display: grid; list-style: none; min-width: 0; }

.check-row {
  display: flex; gap: var(--sp-3); align-items: center;
  padding: var(--sp-2) 0;
  border-top: 1px solid var(--hairline);
  min-width: 0;
}
.check-row:first-child { border-top: 0; padding-top: 0; }

.check-mark {
  flex: none;
  width: 22px; height: 22px;
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  background: var(--neutral-soft);
  color: var(--neutral-text);
}

.check-row.is-ok   .check-mark { background: var(--success-soft); color: var(--success-text); }
.check-row.is-warn .check-mark { background: var(--warning-soft); color: var(--warning-text); }
.check-row.is-fail .check-mark { background: var(--danger-soft);  color: var(--danger-text); }

.check-body { display: grid; gap: 1px; min-width: 0; flex: 1 1 auto; }
.check-title { font-size: var(--fs-sm); font-weight: var(--fw-medium); overflow-wrap: anywhere; }
.check-detail { font-size: var(--fs-xs); color: var(--muted); overflow-wrap: anywhere; }
.check-link { flex: none; }

.readiness-summary {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-2);
  margin-top: var(--sp-4);
}

.readiness-box {
  display: grid; justify-items: center; gap: 2px;
  padding: var(--sp-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--fs-xs);
  min-width: 0;
}
.readiness-box strong { font-size: var(--fs-xl); font-weight: var(--fw-bold); line-height: var(--lh-tight); font-variant-numeric: tabular-nums; }
.readiness-box.is-success { background: var(--success-soft); border-color: var(--success); color: var(--success-text); }
.readiness-box.is-warning { background: var(--warning-soft); border-color: var(--warning); color: var(--warning-text); }
.readiness-box.is-danger  { background: var(--danger-soft);  border-color: var(--danger);  color: var(--danger-text); }

.safety-card .card-title { display: flex; align-items: center; gap: var(--sp-2); }
.safety-list { display: grid; gap: var(--sp-2); padding-inline-start: var(--sp-4); font-size: var(--fs-sm); color: var(--muted); }

/* ── รายการแถวที่กดขยายได้ (<details> เป็นแถว) ──────────────────────────
   ‼️ ใช้แทนตารางเมื่อต้องการแถวรายละเอียด — <tr> อยู่ใน <details> ไม่ได้
   ‼️ คอลัมน์กำหนดด้วยคลาสต่อชุด ไม่ใช่ inline style (CSP) */
.xlist { display: grid; gap: 0; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); overflow: hidden; min-width: 0; }

.xlist-head,
.xlist-summary {
  display: grid;
  grid-template-columns: var(--xlist-cols);
  gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-2) var(--sp-3);
  min-width: 0;
}

.xlist-head {
  background: var(--surface2);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  color: var(--muted);
}

.xlist-row + .xlist-row { border-top: 1px solid var(--hairline); }

.xlist-summary {
  cursor: pointer;
  list-style: none;
  font-size: var(--fs-sm);
}
.xlist-summary::-webkit-details-marker { display: none; }
.xlist-summary:hover { background: var(--surface2); }
.xlist-summary:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.xlist-row[open] > .xlist-summary { background: var(--primary-soft); }

.xlist-cell { min-width: 0; overflow-wrap: anywhere; display: block; }
.xlist-cell .badge { vertical-align: middle; }
.xlist-caret { color: var(--faint); transition: transform var(--transition); justify-self: end; }
.xlist-row[open] > .xlist-summary .xlist-caret { transform: rotate(180deg); }

.xlist-body {
  display: grid; gap: var(--sp-3); justify-items: start;
  padding: var(--sp-3) var(--sp-4) var(--sp-4);
  background: var(--surface2);
  border-top: 1px solid var(--hairline);
}

.xlist-progress { display: grid; gap: var(--sp-1); }
.progress-thin { height: 5px; display: block; }
.progress-thin .progress-bar { display: block; }

/* คอลัมน์ต่อชุด — ตัวสุดท้ายคือ caret เสมอ */
.xlist-runs  { --xlist-cols: 56px minmax(140px, 1.4fr) 100px minmax(120px, 1fr) minmax(110px, 1fr) 130px 64px 20px; }
.xlist-files { --xlist-cols: minmax(200px, 1.3fr) minmax(160px, 1.4fr) 110px 90px 130px 20px; }
.xlist-cron  { --xlist-cols: minmax(130px, 1.2fr) 100px 130px 130px 80px 90px 20px; }

.kv-compact { grid-template-columns: minmax(100px, 160px) 1fr; gap: var(--sp-1) var(--sp-3); font-size: var(--fs-sm); width: 100%; }
.kv-compact dt, .kv-compact dd { padding-block: var(--sp-1); }

/* ‼️ จอกลาง/เล็ก: แถวกลายเป็นการ์ดซ้อนป้าย — ป้ายมาจาก data-label ที่ PHP ใส่ผ่าน __()
      ‼️ ครอบถึง 1200 เพราะระหว่าง 1025–1200 คอลัมน์ขวายังอยู่ ตารางในคอลัมน์ซ้ายกางไม่พอ */
@media (max-width: 1200px) {
  .xlist-head { display: none; }
  .xlist-summary { grid-template-columns: minmax(0, 1fr) 20px; gap: var(--sp-1) var(--sp-3); }
  .xlist-cell[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: var(--fs-xs);
    color: var(--muted);
  }
  .xlist-cell:first-child::before { display: none; }
  .xlist-cell:first-child { font-size: var(--fs-md); }
}

/* ── แถวสิ่งที่ต้องแก้ (compact · <details>) ──────────────────────────── */
.blocker-rows { display: grid; gap: var(--sp-2); }

.blocker-row {
  border: 1px solid var(--border);
  border-inline-start: 3px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
  min-width: 0;
}

.blocker-row.is-stop  { border-color: var(--danger);  border-inline-start-color: var(--danger);  background: var(--danger-soft); }
.blocker-row.is-warn  { border-color: var(--warning); border-inline-start-color: var(--warning); background: var(--warning-soft); }
.blocker-row.is-clear { border-color: var(--success); border-inline-start-color: var(--success); background: var(--success-soft); }
/* ‼️ กันชนที่ทำงานถูกต้อง (ย้ายครบแล้ว · ไม่มีข้อมูลใหม่) — เขียวเหมือน is-clear
   ‼️ แยกคลาสไว้เพราะความหมายต่างกัน: is-clear = ไม่มีอะไรติดเลย · is-done = ติดแต่เป็นข่าวดี */
.blocker-row.is-done  { border-color: var(--success); border-inline-start-color: var(--success); background: var(--success-soft); }

.blocker-summary {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  list-style: none;
  font-size: var(--fs-sm);
  min-width: 0;
}
.blocker-summary::-webkit-details-marker { display: none; }
summary.blocker-summary { cursor: pointer; }
summary.blocker-summary:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }

.blocker-row .blocker-icon { flex: none; margin: 0; }
.blocker-row .blocker-title { flex: 1 1 auto; font-weight: var(--fw-medium); min-width: 0; }
.blocker-row.is-stop  .blocker-icon, .blocker-row.is-stop  .blocker-title { color: var(--danger-text); }
.blocker-row.is-warn  .blocker-icon, .blocker-row.is-warn  .blocker-title { color: var(--warning-text); }
.blocker-row.is-clear .blocker-icon, .blocker-row.is-clear .blocker-title { color: var(--success-text); }
.blocker-row.is-done  .blocker-icon, .blocker-row.is-done  .blocker-title { color: var(--success-text); }
.blocker-count { flex: none; font-size: var(--fs-xs); white-space: nowrap; }

.blocker-detail {
  display: grid; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4) var(--sp-3) calc(var(--sp-3) + 18px + var(--sp-3));
  border-top: 1px solid var(--hairline);
  font-size: var(--fs-sm);
}
.blocker-detail .blocker-fix { margin: 0; }

/* ── ตารางงานที่เกี่ยวข้อง + แถวงานเขียนจริง ────────────────────────── */
.table-jobs td { vertical-align: middle; padding: var(--sp-3) var(--sp-4); font-size: var(--fs-sm); }
/* ‼️ ชื่องาน + คำอธิบายต้องได้พื้นที่ก่อน — ไม่งั้นถูกบีบเหลือคำละบรรทัดขณะที่ช่องปุ่มกินที่ */
.table-jobs td:first-child { width: 38%; min-width: 240px; }
.table-jobs .cell-actions { width: auto; min-width: 170px; text-align: end; white-space: normal; }
.table-jobs .migration-job { display: inline-grid; gap: var(--sp-1); justify-items: end; }
.table-jobs .migration-job .field-hint { font-size: var(--fs-xs); text-align: end; }
.table-jobs tr.is-next > td { background: var(--primary-soft); }
.job-why { display: block; font-size: var(--fs-xs); color: var(--muted); overflow-wrap: anywhere; }
.job-blocked { display: block; font-size: var(--fs-xs); color: var(--warning-text); overflow-wrap: anywhere; }

.job-row-list { display: grid; gap: var(--sp-3); }

.job-row {
  display: flex; gap: var(--sp-4); align-items: flex-start; justify-content: space-between; flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  min-width: 0;
}
.job-row.is-write { border-color: var(--danger-soft); }
.job-row-body { display: grid; gap: 2px; min-width: 0; flex: 1 1 240px; }
.job-row-name { font-weight: var(--fw-semi); overflow-wrap: anywhere; }
.job-row-why  { font-size: var(--fs-xs); color: var(--muted); overflow-wrap: anywhere; }
.job-row-meta { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; font-size: var(--fs-xs); margin-top: var(--sp-1); }
.job-row-side { flex: 0 1 auto; display: grid; gap: var(--sp-2); min-width: 0; }
.job-row-side .migration-job { display: grid; gap: var(--sp-2); }
.job-row-side .btn { height: auto; min-height: var(--btn-h); white-space: normal; padding-block: var(--sp-2); line-height: var(--lh-tight); overflow-wrap: anywhere; }
.job-row-side .field-hint { font-size: var(--fs-xs); }

/* danger zone อยู่ในการ์ดเดียวกับตารางงาน — เว้นระยะจากตาราง */
.card > .danger-zone { margin-top: var(--sp-4); }
.card > .danger-zone > .danger-head { padding: var(--sp-3) var(--sp-4); }
.card > .danger-zone > .danger-body { padding: var(--sp-4); gap: var(--sp-3); }

/* ── แถบค้นหา/ชิปกรองในการ์ด + ท้ายรายการ ────────────────────────── */
.listbar { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; justify-content: space-between; margin-bottom: var(--sp-3); min-width: 0; }
.listbar-search { display: flex; gap: var(--sp-2); flex: 1 1 260px; min-width: 0; }
.listbar-search .form-control { flex: 1 1 auto; min-width: 0; height: var(--btn-h-sm); padding-block: 0; }
.listbar-search .btn { flex: none; }
.listbar-chips { padding: 0; flex: 0 1 auto; }
.listbar-foot { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; justify-content: space-between; margin-top: var(--sp-3); min-width: 0; }
.listbar-foot .pagination { margin: 0; }

/* ── ตัวเดินงาน: กล่องล็อก · manifest · cron ─────────────────────────── */
.lock-note {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface2);
  margin-bottom: var(--sp-3);
  min-width: 0;
}
.lock-note-icon { flex: none; color: var(--faint); margin-top: 2px; }
.lock-note-body { display: grid; gap: 2px; min-width: 0; font-size: var(--fs-sm); }
.lock-note-body strong { font-weight: var(--fw-medium); overflow-wrap: anywhere; }

.manifest { display: flex; flex-wrap: wrap; gap: var(--sp-4); align-items: center; min-width: 0; }

.manifest-state {
  display: flex; gap: var(--sp-3); align-items: center;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface2);
  flex: 1 1 240px;
  min-width: 0;
}
.manifest-state.is-ok   { border-color: var(--success); background: var(--success-soft); }
.manifest-state.is-warn { border-color: var(--warning); background: var(--warning-soft); }
.manifest-state.is-ok .manifest-icon   { color: var(--success-text); }
.manifest-state.is-warn .manifest-icon { color: var(--warning-text); }
.manifest-icon { flex: none; color: var(--faint); }
.manifest-text { display: grid; gap: 1px; min-width: 0; font-size: var(--fs-sm); overflow-wrap: anywhere; }

.manifest-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: var(--sp-3); flex: 2 1 320px; min-width: 0; }
.manifest-fact { display: grid; gap: 1px; min-width: 0; }
.manifest-fact dt { font-size: var(--fs-xs); color: var(--muted); }
.manifest-fact dd { font-size: var(--fs-lg); font-weight: var(--fw-semi); font-variant-numeric: tabular-nums; }

/* ‼️ กล่องคำสั่งเลื่อนแนวนอนในตัวเอง — ห้ามดันทั้งหน้าให้เลื่อน */
.code-block { overflow-x: auto; max-width: 100%; white-space: pre; }
.code-row { display: flex; gap: var(--sp-3); align-items: center; justify-content: space-between; flex-wrap: wrap; min-width: 0; }
.code-row .field-hint { margin: 0; }
.copy-btn { flex: none; }
.copy-btn.is-copied { border-color: var(--success); color: var(--success-text); background: var(--success-soft); }

.chip-list { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-2); }
.chip { display: inline-block; padding: 0 var(--sp-2); border-radius: var(--radius-xs); background: var(--surface3); font-size: var(--fs-xs); overflow-wrap: anywhere; }

/* <details class="tech"> ที่อยู่ในคอลัมน์ขวาแทนการ์ด */
.card-tech > summary .badge { margin-inline-start: auto; }
.card-tech > summary .tech-caret { margin-inline-start: var(--sp-2); }

/* ‼️ การ์ดสถิติ overview ให้ตัวเลขไม่แย่งเนื้อที่กับ badge */
.overview .stat-value.is-text { display: inline-flex; align-items: center; gap: var(--sp-2); }
.overview-hint { margin-top: var(--sp-3); }

/* ‼️ ช่องรองซ่อนจาก summary บนจอที่แถวยุบเป็นการ์ด — ข้อมูลเดิมยังอยู่ในรายละเอียดของแถว */
@media (max-width: 1200px) {
  .xlist-cell.is-secondary { display: none; }
}

.xlist-files { --xlist-cols: minmax(210px, 1.2fr) minmax(170px, 1.6fr) 104px 84px 128px 16px; }
.xlist-cell.text-faint, .xlist-cell.text-mono { font-size: var(--fs-xs); }
.xlist-cell.cell-strong.text-mono { font-size: var(--fs-sm); }

/* <details class="tech"> ในคอลัมน์ขวา — ป้าย + badge ซ้อนกันเป็นสองบรรทัด ไม่แย่งความกว้าง */
.card-tech > summary .card-tech-label { display: grid; gap: var(--sp-1); flex: 1 1 auto; min-width: 0; }
.card-tech > summary .card-tech-label .badge { justify-self: start; }

/* ‼️ ตารางงาน 5 ช่องต้องพอดีคอลัมน์ซ้าย (~750px บนจอ 1440) — ไม่ให้ช่องปุ่มถูกตัดหาย */
.table-jobs { min-width: 0; }
.table-jobs td:first-child { width: 36%; min-width: 200px; }
.table-jobs td, .table-jobs th { padding-inline: var(--sp-3); }
.table-jobs .cell-actions { min-width: 150px; }
.table-jobs .cell-actions .btn { white-space: normal; height: auto; min-height: var(--btn-h-sm); padding-block: var(--sp-1); line-height: var(--lh-tight); }

/* ‼️ วัดจริงบนจอ 1440: คอลัมน์ซ้ายกว้าง ~740px — ผลรวม min ของคอลัมน์ต้องต่ำกว่านั้น ไม่งั้นช่องท้ายถูกตัด */
.xlist-head, .xlist-summary { gap: var(--sp-2); }
.xlist-files { --xlist-cols: minmax(160px, 1.2fr) minmax(150px, 1.5fr) 96px 80px 118px 16px; }
.xlist-runs  { --xlist-cols: 52px minmax(130px, 1.3fr) 90px minmax(120px, 1fr) minmax(110px, 1fr) 124px 56px 16px; }
.xlist-cron  { --xlist-cols: minmax(120px, 1.2fr) 92px 124px 124px 72px 88px 16px; }

/* ‼️ จอที่แถวยุบเป็นการ์ด: วางช่องเป็นสองคอลัมน์ ไม่ใช่ซ้อนทีละบรรทัด — วัดจริงบนมือถือ
      ประวัติ 15 แถวแบบซ้อนบรรทัดเดียวทำให้หน้าสูงขึ้นเกือบเท่าตัว (6,294 → 10,127px) */
@media (max-width: 1200px) {
  /* caret ลอยมุมขวาบน — ถ้าให้เป็นคอลัมน์ที่ 3 ช่องอื่นจะถูก auto-place ไปเบียดในคอลัมน์แคบนั้น (วัดจริงแล้ว) */
  .xlist-summary { position: relative; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); padding-inline-end: calc(var(--sp-3) + 20px + var(--sp-2)); }
  .xlist-cell:first-child,
  .xlist-files .xlist-cell:nth-child(2),
  .xlist-runs .xlist-cell:nth-child(2),
  .xlist-cron .xlist-cell:first-child { grid-column: 1 / -1; }
  .xlist-caret { position: absolute; top: var(--sp-2); inset-inline-end: var(--sp-3); }
}

/* ‼️ มือถือ: ตารางงานเหลือ ชื่อ · สถานะ · ปุ่ม — ความคืบหน้า/เวลาล่าสุดอยู่ในประวัติการทำงานท้ายหน้าอยู่แล้ว
      (วัดจริง: การ์ดต่อแถวสูง ~330px × 7 งาน ทำให้หน้ามือถือยาวเกือบสองเท่าของเดิม) */
@media (max-width: 767px) {
  .table-jobs td.is-secondary { display: none; }
  .table-jobs .cell-actions { text-align: start; }
  .table-jobs .migration-job { justify-items: start; }
  .table-jobs .migration-job .field-hint { text-align: start; }
}

/* ‼️ ประวัติบนจอแคบ: เลขงาน+ชื่องาน อยู่บรรทัดเดียว · สถานะ+ความคืบหน้า อีกบรรทัด (วัดจริง 183 → ~110px/แถว) */
@media (max-width: 1200px) {
  .xlist-runs .xlist-summary { grid-template-columns: auto minmax(0, 1fr); }
  .xlist-runs .xlist-cell:first-child,
  .xlist-runs .xlist-cell:nth-child(2) { grid-column: auto; }
  .xlist-runs .xlist-cell:nth-child(2)::before { display: none; }
  .xlist-runs .xlist-cell:nth-child(2) { align-self: end; }
}

/* ═══ หน้าแรกฝั่งทีม (Phase 22) ═══════════════════════════════════════════
   ‼️ ทุกค่าสี/ระยะ/ขนาด มาจาก token เท่านั้น (กฎเหล็กข้อ 8)
   ‼️ ไม่มี style ในหน้าและไม่มีไฟล์ CSS แยก — CSP ของระบบบล็อก inline style อยู่แล้ว
   ─────────────────────────────────────────────────────────────────────── */

/* ── หัวหน้า ─────────────────────────────────────────────────────────── */
.dash-hero {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-5);
  flex-wrap: wrap;
  margin-bottom: var(--sp-5);
}

.dash-hero-text { min-width: 0; }

.dash-eyebrow {
  display: block;
  color: var(--muted);
  font-size: var(--fs-sm);
  margin-bottom: var(--sp-1);
}

.dash-hero h1 {
  font-size: var(--fs-3xl);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  overflow-wrap: anywhere;
}

.dash-hero .page-sub {
  color: var(--muted);
  font-size: var(--fs-md);
  margin-top: var(--sp-1);
}

.dash-hero-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--sp-2);
  text-align: end;
  min-width: 0;
}

/* ‼️ เส้นสีส้มคือจุดเดียวที่ใช้สี accent บนหน้านี้ — ส้มเป็น accent ห้ามเป็นปุ่มหลัก */
.dash-hero-rule {
  width: 48px;
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--accent);
}

.dash-hero-time {
  color: var(--muted);
  font-size: var(--fs-sm);
  overflow-wrap: anywhere;
}

/* ── แถบแจ้งเตือนการย้ายข้อมูล ───────────────────────────────────────── */
.dash-notice {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  margin-bottom: var(--sp-5);
  border: 1px solid var(--warning-soft);
  border-radius: var(--radius-lg);
  background: var(--warning-soft);
  color: var(--text);
  min-width: 0;
  transition: border-color var(--transition), box-shadow var(--transition);
}

a.dash-notice:hover {
  text-decoration: none;
  border-color: var(--warning);
  box-shadow: var(--shadow-sm);
}

.dash-notice-icon {
  flex: none;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--on-solid);
}

.dash-notice-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.dash-notice-title { font-weight: var(--fw-semi); }
.dash-notice-text { color: var(--warning-text); font-size: var(--fs-sm); overflow-wrap: anywhere; }
.dash-notice-arrow { flex: none; color: var(--warning-text); }

/* ── แถว KPI ─────────────────────────────────────────────────────────── */
.kpi-row {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-4);
  margin-bottom: var(--sp-4);
}

.kpi {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  color: var(--text);
  min-width: 0;
  transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}

a.kpi:hover {
  text-decoration: none;
  border-color: var(--primary-soft2);
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.kpi-icon {
  flex: none;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border-radius: var(--radius);
  background: var(--primary-soft);
  color: var(--primary);
}

.kpi.is-warning .kpi-icon { background: var(--warning-soft); color: var(--warning); }
.kpi.is-danger  .kpi-icon { background: var(--danger-soft);  color: var(--danger); }
.kpi.is-success .kpi-icon { background: var(--success-soft); color: var(--success); }
.kpi.is-info    .kpi-icon { background: var(--info-soft);    color: var(--info); }

.kpi-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.kpi-label { color: var(--muted); font-size: var(--fs-md); line-height: var(--lh-tight); overflow-wrap: anywhere; }
.kpi-hint { color: var(--faint); font-size: var(--fs-xs); margin-top: var(--sp-1); overflow-wrap: anywhere; }
.kpi-arrow { flex: none; color: var(--faint); margin-top: var(--sp-1); }
a.kpi:hover .kpi-arrow { color: var(--primary); }

/* ‼️ ศูนย์ = ไม่มีอะไรต้องทำ จึงต้องไม่แย่งสายตา — จางลง ไม่ใช่ซ่อน */
.kpi.is-quiet { background: var(--surface2); box-shadow: none; }
.kpi.is-quiet .kpi-icon { background: var(--surface3); color: var(--faint); }
.kpi.is-quiet .summary-value { color: var(--muted); }
a.kpi.is-quiet:hover { background: var(--surface); }

/* ── สองคอลัมน์ 62/38 ────────────────────────────────────────────────── */
.dash-split {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: var(--sp-4);
  align-items: start;
  margin-bottom: var(--sp-4);
}

.dash-card { border-radius: var(--radius-lg); padding: var(--sp-5); }

.dash-card-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
  min-width: 0;
}

.dash-card-icon {
  flex: none;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: var(--radius-sm);
  background: var(--primary-soft);
  color: var(--primary);
}

.dash-card-icon.is-info    { background: var(--info-soft);    color: var(--info); }
.dash-card-icon.is-success { background: var(--success-soft); color: var(--success); }
.dash-card-icon.is-muted   { background: var(--surface3);     color: var(--faint); }

.dash-card-titles { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.dash-card-titles .card-title { font-size: var(--fs-lg); margin-bottom: 0; }
.dash-card-sub { color: var(--muted); font-size: var(--fs-sm); overflow-wrap: anywhere; }

.dash-card-link {
  flex: none;
  display: inline-flex; align-items: center; gap: var(--sp-1);
  color: var(--primary-text);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  white-space: nowrap;
}

.dash-card-link:hover { text-decoration: none; color: var(--primary); }

/* ── ช่องตัวเลขพร้อมแถบสัดส่วน ───────────────────────────────────────── */
.dash-tiles {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-3);
}

.dash-tile {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-4);
  border-radius: var(--radius);
  border: 1px solid transparent;
  background: var(--surface2);
  color: var(--text);
  min-width: 0;
  transition: border-color var(--transition), transform var(--transition);
}

a.dash-tile:hover { text-decoration: none; transform: translateY(-1px); }

.dash-tile.is-danger  { background: var(--danger-soft); }
.dash-tile.is-warning { background: var(--warning-soft); }
.dash-tile.is-info    { background: var(--info-soft); }
.dash-tile.is-success { background: var(--success-soft); }
.dash-tile.is-primary { background: var(--primary-soft); }

a.dash-tile.is-danger:hover  { border-color: var(--danger); }
a.dash-tile.is-warning:hover { border-color: var(--warning); }
a.dash-tile.is-info:hover    { border-color: var(--info); }
a.dash-tile.is-success:hover { border-color: var(--success); }
a.dash-tile.is-primary:hover { border-color: var(--primary); }

/* ‼️ ศูนย์ = ไม่มีอะไรต้องทำ — ถอดสีพื้นออกให้เหลือเทากลาง */
.dash-tile.is-quiet { background: var(--surface2); }
.dash-tile.is-quiet .dash-tile-value { color: var(--muted); }

.dash-tile-label { color: var(--muted); font-size: var(--fs-sm); line-height: var(--lh-tight); overflow-wrap: anywhere; }
.dash-tile-value { font-size: var(--fs-2xl); font-weight: var(--fw-bold); line-height: 1.15; min-width: 0; overflow-wrap: anywhere; }

.progress-xs { height: 5px; margin-top: auto; }
.progress-bar.is-danger  { background: var(--danger); }
.progress-bar.is-warning { background: var(--warning); }
.progress-bar.is-info    { background: var(--info); }
.progress-bar.is-success { background: var(--success); }
.progress-bar.is-primary { background: var(--primary); }

/* ── รายการตัวเลขแบบแถว ──────────────────────────────────────────────── */
.dash-list { display: flex; flex-direction: column; }

.dash-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-2);
  border-bottom: 1px solid var(--hairline);
  color: var(--text);
  min-width: 0;
  transition: background var(--transition);
}

.dash-row:last-child { border-bottom: 0; }
a.dash-row:hover { text-decoration: none; background: var(--surface2); }

.dash-row-icon {
  flex: none;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border-radius: var(--radius-xs);
  background: var(--surface3);
  color: var(--faint);
}

.dash-row-icon.is-danger  { background: var(--danger-soft);  color: var(--danger); }
.dash-row-icon.is-warning { background: var(--warning-soft); color: var(--warning); }
.dash-row-icon.is-info    { background: var(--info-soft);    color: var(--info); }
.dash-row-icon.is-success { background: var(--success-soft); color: var(--success); }
.dash-row-icon.is-primary { background: var(--primary-soft); color: var(--primary); }

.dash-row-label { flex: 1; min-width: 0; font-size: var(--fs-md); overflow-wrap: anywhere; }
.dash-row-value { flex: none; font-weight: var(--fw-semi); font-size: var(--fs-lg); }
.dash-row-arrow { flex: none; color: var(--faint); }
a.dash-row:hover .dash-row-arrow { color: var(--primary); }

.dash-row.is-quiet .dash-row-value { color: var(--muted); font-weight: var(--fw-medium); }

/* ── วงแหวนสุขภาพระบบ ────────────────────────────────────────────────── */
.donut-wrap { display: flex; align-items: center; gap: var(--sp-5); flex-wrap: wrap; }

.donut { position: relative; flex: none; width: 132px; height: 132px; }
.donut-svg { width: 100%; height: 100%; display: block; transform: rotate(-90deg); }

.donut-track {
  fill: none;
  stroke: var(--surface3);
  stroke-width: 12;
}

.donut-arc {
  fill: none;
  stroke-width: 12;
  stroke-linecap: butt;
}

.donut-arc.is-success { stroke: var(--success); }
.donut-arc.is-warning { stroke: var(--warning); }
.donut-arc.is-danger  { stroke: var(--danger); }
.donut-arc.is-info    { stroke: var(--info); }

.donut-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  text-align: center;
}

.donut-total { font-size: var(--fs-2xl); font-weight: var(--fw-bold); line-height: 1; }
.donut-total-label { color: var(--muted); font-size: var(--fs-xs); }

.donut-legend { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2); list-style: none; }

.donut-legend-row { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; font-size: var(--fs-sm); }

.donut-dot { flex: none; width: 10px; height: 10px; border-radius: var(--radius-pill); background: var(--faint); }
.donut-dot.is-success { background: var(--success); }
.donut-dot.is-warning { background: var(--warning); }
.donut-dot.is-danger  { background: var(--danger); }
.donut-dot.is-info    { background: var(--info); }

.donut-legend-label { flex: 1; min-width: 0; color: var(--muted); overflow-wrap: anywhere; }
.donut-legend-value { flex: none; font-weight: var(--fw-semi); }
.donut-legend-percent { flex: none; color: var(--faint); min-width: 3.5ch; text-align: end; }

/* ── ตารางในการ์ดหน้าแรก ─────────────────────────────────────────────── */
.dash-table { border: 0; box-shadow: none; border-radius: 0; }
.dash-table table { min-width: 0; }
.dash-table thead th { background: transparent; border-bottom: 1px solid var(--border); padding: var(--sp-2) var(--sp-3); }
.dash-table tbody td { padding: var(--sp-3); }
.dash-table .cell-strong { display: block; font-weight: var(--fw-medium); overflow-wrap: anywhere; }
.dash-table .cell-sub { display: block; color: var(--muted); font-size: var(--fs-xs); margin-top: 2px; overflow-wrap: anywhere; }
.dash-table .empty-state { padding: var(--sp-6) var(--sp-4); }

/* ── จอกลาง: คอลัมน์ขวาลงมาต่อท้าย ──────────────────────────────────── */
@media (max-width: 1180px) {
  .dash-split { grid-template-columns: 1fr; }
  .kpi-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── มือถือ ──────────────────────────────────────────────────────────── */
@media (max-width: 767px) {
  .dash-hero { align-items: flex-start; gap: var(--sp-3); }
  .dash-hero h1 { font-size: var(--fs-2xl); }
  .dash-hero-meta { align-items: flex-start; text-align: start; }

  .dash-notice { padding: var(--sp-3) var(--sp-4); gap: var(--sp-3); align-items: flex-start; }
  .dash-notice-icon { width: 36px; height: 36px; }
  .dash-notice-arrow { display: none; }

  .kpi-row { gap: var(--sp-3); }
  .kpi { padding: var(--sp-3) var(--sp-4); gap: var(--sp-3); }
  .kpi-icon { width: 38px; height: 38px; }
  .kpi .summary-value { font-size: var(--fs-2xl); }
  .kpi-arrow { display: none; }

  .dash-card { padding: var(--sp-4); }
  .dash-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* ‼️ ตารางยุบเป็นการ์ดรายแถวด้วย .table-cards อยู่แล้ว — เอาเส้นหัวตารางออกไม่ให้ซ้อน */
  .dash-table thead th { border-bottom: 0; }

  /**
   * ‼️‼️ ต้องคืน padding ให้ .table-cards เป็นคนคุม
   *    ‼️ `.dash-table tbody td` ตั้ง padding ไว้สำหรับ "ตารางที่ยังเป็นตาราง" และมันอยู่
   *       ท้ายไฟล์กว่า จึงชนะกฎของ .table-cards ที่ตั้ง `var(--sp-2) 0` ไว้
   *       ‼️ ผลคือทุกเซลล์บนมือถือมี padding รอบด้าน แถวการ์ดจึงสูงเกินจำเป็น
   */
  .dash-table tbody td { padding: var(--sp-2) 0; }

  /**
   * ‼️ ซ่อนคอลัมน์รองบนจอแคบ — รูปแบบเดียวกับ .table-jobs
   * ‼️ วัดจริงบนจอ 390: ห้าเซลล์ต่อการ์ดทำให้แถวสูง 372px และทั้งหน้ายาว 5,601px
   *    ‼️ เหลือสามเซลล์ (รหัส · ชื่อเรื่อง · สถานะ) อ่านครบและสั้นลงเกินครึ่ง
   * ‼️ ข้อมูลไม่หาย — ยังอยู่ครบบนเดสก์ท็อปและในหน้ารายละเอียดงาน
   */
  .dash-table td.is-secondary,
  .dash-table th.is-secondary { display: none; }

  .donut-wrap { gap: var(--sp-4); justify-content: center; }
  .donut-legend { flex-basis: 100%; }
}

/* ── จอเล็กมาก: KPI เรียงเดี่ยว ──────────────────────────────────────── */
/* ‼️ 359px ไม่ใช่ 420px — จอ 390 (iPhone) ต้องได้สองคอลัมน์
   ‼️ วัดจริง: บังคับคอลัมน์เดียวที่ 390 ทำให้หน้ายาว 5,977px ต้องปัดนิ้วหลายรอบ
      กว่าจะถึงการ์ดใบที่สอง ซึ่งขัดกับเหตุผลที่หน้าภาพรวมมีอยู่ */
@media (max-width: 359px) {
  .kpi-row { grid-template-columns: 1fr; }
  .dash-tiles { grid-template-columns: 1fr; }
}

/* ── หน้า Migration Center: ยุบงานที่ผ่านแล้วให้เตี้ยลง (Phase 22) ────────
   ‼️ ไม่ได้ซ่อนแถว — ซ่อนเฉพาะ "คำอธิบายว่างานนี้มีไว้ทำอะไร"
      ‼️ คำอธิบายมีค่ากับงานที่ยังไม่ได้ทำ · งานที่ผ่านไปแล้วไม่ต้องอธิบายซ้ำ
   ‼️ สถานะ ความคืบหน้า ผู้ทำ เวลา และปุ่มรันซ้ำ ยังอยู่ครบทุกอย่าง            */
.table-jobs tr.is-settled .job-why { display: none; }
.table-jobs tr.is-settled td { padding-block: var(--sp-2); }
.table-jobs tr.is-settled .cell-strong { color: var(--muted); font-weight: var(--fw-medium); }

/* ‼️ การ์ดที่พับได้ต้องมีสัญญาณว่ากดเปิดได้ — ไม่งั้นผู้ใช้นึกว่าเนื้อหาหายไป */
details.card-tech > summary.card-head { cursor: pointer; list-style: none; }
details.card-tech > summary.card-head::-webkit-details-marker { display: none; }
details.card-tech > summary.card-head .tech-caret { margin-inline-start: auto; color: var(--faint); transition: transform var(--transition); }
details.card-tech[open] > summary.card-head .tech-caret { transform: rotate(180deg); }
details.card-tech > summary.card-head:hover .card-title { color: var(--primary-text); }
