/* ==========================================================================
   EMS Hillkoff — ems-field.css  (แผน 09 · Phase U0)
   ต้นฉบับ: ems-template-1c/assets/css/ems-field.css  721 บรรทัด (คัดลอกทั้งไฟล์)
   ส่วนที่เพิ่ม: บล็อก <style> ของ mockup 3 ไฟล์ใน Implement_plan/ (ทั้งสามไฟล์เหมือนกันทุกไบต์)
   ───────────────────────────────────────────────────────────────────────
   สิ่งที่ "ต่างจากต้นฉบับ" — ครบทุกจุด (เลขบรรทัดคือของ "ต้นฉบับ template")

   ก. แก้ในตัวไฟล์ต้นฉบับ (10 จุด)
     :69   h1..h5{margin:0}          → .e-app h1..h5    (ข้อตัดสิน §1 ข้อ 11 — reset ระดับ global
                                        ทุบหัวเรื่องใน 84 หน้าเดิมช่วง bridge · ฟอนต์หัวยังมาจาก §14 BRIDGE)
     :74   img,svg{display:block}    → .e-app img,.e-app svg   (ข้อตัดสิน 11 — <img> ใน 40 ไฟล์เดิม)
     :495  .e-bottomnav{height:52px} → height:var(--e-navh)    (§3 หัวข้อ 19: ความสูงแถบล่างเป็น token
                                        ตัวเดียว ห้ามฝังเลข 52/66 ซ้ำ — แถบเปลี่ยนตามหมวดใน U2)
     :505-511  .e-fab-slot / .e-fab  → ลบ  (§3 หัวข้อ 19 "ตัดทิ้ง" · §0.3 ข้อ 3: CSS :594 ซ่อน FAB
                                        ทุกหน้าที่มี .e-actionbar อยู่แล้ว ⇒ ปุ่มนี้ไม่เคยโผล่ ·
                                        "สแกน QR" ย้ายไปอยู่ในวงล้อคำสั่งแทน)
     :516  คอมเมนต์ "เมนูพัด"        → เขียนใหม่เป็น "วงล้อคำสั่ง"
     :525-530, :536-537  คลาสของ "เมนูพัดแนวตั้ง" → ลบ  (แทนด้วย .e-air-node หัวข้อ 19b ·
                                        เกณฑ์ผ่าน U0: ต้องไม่เหลือชื่อคลาสนั้นในไฟล์สักจุด)
     :586  .e-content padding 76px   → calc(var(--e-navh) + 24px)   (เหตุผลเดียวกับ :495)
     :587,590,592,593  .e-bottomnav/.e-air ที่เปิดใน @media 768  → ย้ายไปหัวข้อ 19 ที่ 991.98px
                                        (ข้อตัดสิน §1 ข้อ 28 — ดูหมายเหตุ "เบรกพอยนต์" ข้างล่าง)
     :594  .e-actionbar ~ .e-bottomnav .e-fab-slot  → ลบ (ตามการลบ .e-fab)
     :609  @media print              → เพิ่ม .e-alertbar .e-air [data-e-air] .e-sheet .e-gsearch
                                        และคลาสเดิมของระบบ .sidebar .topbar .app-footer .sidebar-overlay
                                        (§2.4 ช: template ลืม .e-air ⇒ ปุ่มกลมติดกระดาษและ PDF)
     :624  .btn{min-height:38px}     → .btn:not(.btn-sm):not(.btn-xs)  (ข้อตัดสิน §1 ข้อ 10 —
                                        ของเดิมดัน .btn-sm ใน 75 ไฟล์ให้สูงขึ้น)

   ข. ต่อท้าย (หัวข้อใหม่ 14c → 19b ตาม §3 ของแผน)
     14c ALIAS · 14d BRIDGE เพิ่ม · 15b BASE เพิ่ม  → เขียนใหม่ในเฟสนี้ (ไม่มีใน mockup —
         mockup ไม่ได้โหลด bootstrap/app.css จึงไม่ต้องมี bridge)
     16 FORM · 17 DASHBOARD · 18 PM · 18b · 19 NAV · 19b วงล้อคำสั่ง  → คัดลอกจาก mockup
         (Implement_plan/09-ui-dashboard-mockup.html — บล็อก <style> บรรทัด 771-1237)

   ค. ของ mockup ที่ "ไม่" คัดลอกมา (เป็นของแถบพรีวิวใน artifact เท่านั้น — §3 ของแผน)
     ทั้งบล็อก MOCK-ONLY (mockup :724-770): token ความสูงแถบพรีวิว · คลาสตระกูลแถบพรีวิวทั้งหมด · กฎเติม SVG ให้ไอคอน ·
       .e-rail/.e-topbar ที่เลื่อนหลบแถบพรีวิว · .dropdown* ที่จำลอง Bootstrap (ของจริงมี Bootstrap อยู่แล้ว)
       — ยกเว้น [hidden]{display:none!important} ที่ยกมาไว้ในหัวข้อ 15b เพราะเป็นของจริง (§3 "2 BASE")
     .e-gate (mockup :891-894, 896-897) — ป้ายด่านสิทธิ์สำหรับรีวิว ของจริงคือ hasPermission()
     .e-view (mockup :971) — ตัวสลับมุมมองบนแถบพรีวิว
     ช่องเลือกหมวดบนแถบพรีวิว (mockup :1214-1216)

   ง. เบรกพอยนต์ (ข้อตัดสิน §1 ข้อ 28 — ผู้ใช้ยังไม่ตอบ ⇒ ทำตามค่าแนะนำ "ก) 991.98px")
     แถบล่าง · วงล้อ · safe-area · padding ล่างของ .e-content · .e-actionbar.is-bar
       ⇒ **991.98px** ให้ตรงกับที่ระบบเดิมยุบ sidebar (app.css:462) — ช่วง 768-992px คือแท็บเล็ตหน้างาน
     ส่วนที่ยังเป็น 768px โดยตั้งใจ (เป็นเรื่อง "ความกว้างของโทรศัพท์" ไม่ใช่ "โครง shell"):
       drawer/rail ของ template (:557-604) · ยุบแถบตัวกรอง 19b (ข้อตัดสิน 27 อ้าง 390px) ·
       ช่องกรอก 16px กัน iOS ซูม · .e-foot ที่ซ่อนบนมือถือ
     🔴 U2 ต้องยุบเบรกพอยนต์ที่เหลือ (rail/drawer + app.css:462 + JS) ให้เป็นค่าเดียวกัน

   จ. แก้ selector ให้ตรงของจริงตั้งแต่เฟสนี้ (§2.4 ซ ข้อ 5)
     แถบตัวกรองจริงของระบบคือ [data-filter-bar] (includes/filter_bar.php:70) ไม่ใช่ .e-filter
     ⇒ กฎยุบ/กางของหัวข้อ 19b รับทั้งสอง และ **ยกเว้น [data-auto-submit="0"]** (หน้ารายงาน 9 ไฟล์
       มีปุ่มสั่งออกรายงานอยู่ในแถบนั้นเอง — ยุบแล้วเปิดหน้ามาสั่งอะไรไม่ได้เลย §2.4 ซ ข้อ 6)
     ⇒ กฎยุบทั้งหมด scope ใต้ .e-app เพื่อให้ "เริ่มมีผลตอน U2 (shell ใหม่)" ไม่ใช่ตอน U1
       ที่ยังไม่มีวงล้อมาเปิดแถบตัวกรองให้ (ไม่งั้นมือถือจะกรองอะไรไม่ได้เลยทั้งเฟส U1)

   ฉ. ยังไม่มีหน้าใดโหลดไฟล์นี้ — U1 เป็นเฟสที่เพิ่ม <link> ใน header.php
   ========================================================================== */

/* ==========================================================================
   EMS Hillkoff — Industry theme / FIELD CARDS (แนวทาง 1c)
   ไฟล์เดียวจบ: tokens + shell (icon rail) + การ์ดหน้างาน + responsive + bridge
   โหลด "หลัง" bootstrap.min.css และหลัง app.css เสมอ
   --------------------------------------------------------------------------
   หลักการ 1c
   1. sidebar ยุบเหลือ icon rail 64px — คืนพื้นที่ให้เนื้อหา
   2. ทุกรายการเป็นการ์ด และมี "ปุ่มลงมือทำ" ในตัว สูงอย่างน้อย 44px
   3. หน้าจอเปิดด้วยงานของคนที่ล็อกอิน ไม่ใช่สถิติของทั้งบริษัท
   4. มุมเหลี่ยม 0 · ไม่มีเงา · เส้น 1px · สีเน้นเดียว steel #5980a6
   ========================================================================== */

/* ---------- 1. TOKENS ---------------------------------------------------- */
:root {
  --e-bg:          #f2f2f3;
  --e-surface:     #ffffff;
  --e-surface-alt: #f7f7f8;
  --e-text:        #1d1f20;
  --e-text-2:      #5d5d60;
  --e-text-3:      #7a7a7d;
  --e-text-4:      #98989b;

  --e-n-100: #f5f5f8; --e-n-150: #f0f0f1; --e-n-200: #e7e7ea;
  --e-n-300: #d4d4d7; --e-n-400: #b7b7ba; --e-n-500: #98989b;

  --e-a-100: #eef6ff; --e-a-200: #d6ebff; --e-a-300: #b5d9fd;
  --e-a-400: #94bce3; --e-a-500: #749dc4;
  --e-a:     #5980a6;                       /* accent หลัก */
  --e-a-600: #4d7196; --e-a-700: #416180; --e-a-800: #2c455d;
  --e-a-900: #1d2d3d;                       /* rail / header เข้ม */

  /* status — ทั้งระบบใช้แค่ 5 สีนี้ */
  --e-danger: #a3271f; --e-danger-bg: #fbeceb; --e-danger-bd: #e8c4c0;
  --e-warn:   #b5651d; --e-warn-bg:   #fdf1e3; --e-warn-bd:   #e8d3b4;
  --e-ok:     #3f6b52; --e-ok-bg:     #e9f2ec; --e-ok-bd:     #bcd6c6;
  --e-info:   #416180; --e-info-bg:   #eef6ff; --e-info-bd:   #b5d9fd;
  --e-mute:   #7a7a7d; --e-mute-bg:   #f0f0f1; --e-mute-bd:   #d4d4d7;

  --e-font-head: "Barlow Condensed", "IBM Plex Sans Thai", system-ui, sans-serif;
  --e-font-body: "Barlow", "IBM Plex Sans Thai", system-ui, sans-serif;
  --e-font-num:  "Barlow", ui-monospace, monospace;

  --e-radius:  0px;
  --e-line:      1px solid var(--e-n-300);
  --e-line-soft: 1px solid var(--e-n-200);
  --e-rail-w:   64px;
  --e-rail-w-wide: 256px;
  --e-topbar-h: 58px;
  --e-tap:      44px;      /* ปุ่มลงมือทำห้ามเล็กกว่านี้ */
  --e-gutter:   22px;
  --e-gap:      16px;

  /* bridge ให้ bootstrap เดิม */
  --bs-primary: #5980a6; --bs-primary-rgb: 89,128,166;
  --bs-body-bg: #f2f2f3; --bs-body-color: #1d1f20;
  --bs-body-font-family: "Barlow", "IBM Plex Sans Thai", system-ui, sans-serif;
  --bs-border-radius: 0; --bs-border-radius-sm: 0; --bs-border-radius-lg: 0;
  --bs-border-color: #d4d4d7;
  --bs-link-color: #416180; --bs-link-hover-color: #2c455d;
}

/* ---------- 2. BASE ------------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }
body {
  margin: 0; background: var(--e-bg); color: var(--e-text);
  font-family: var(--e-font-body); font-size: 13px; line-height: 1.55;
  -webkit-font-smoothing: antialiased; text-wrap: pretty;
}
/* ข้อตัดสิน §1 ข้อ 11: reset นี้ต้องไม่หลุดออกนอก shell ใหม่ (ต้นฉบับ :69 เป็น global) */
.e-app h1, .e-app h2, .e-app h3, .e-app h4, .e-app h5 { font-family: var(--e-font-head); font-weight: 600; line-height: 1.18; margin: 0; }
a { color: var(--e-a-700); text-decoration: none; }
a:hover { color: var(--e-a-800); }
:focus-visible { outline: 2px solid var(--e-a); outline-offset: 2px; }
::selection { background: var(--e-a-200); }
/* ข้อตัดสิน §1 ข้อ 11: ต้นฉบับ :74 เป็น global ⇒ กระทบ <img> ใน 40 ไฟล์เดิมช่วง bridge */
.e-app img, .e-app svg { display: block; max-width: 100%; }
button { font: inherit; }

.e-num    { font-family: var(--e-font-head); font-weight: 600; font-variant-numeric: tabular-nums; }
.e-code   { font: 600 12px var(--e-font-num); color: var(--e-a-700); }
.e-kicker { font: 600 9.5px var(--e-font-head); letter-spacing: .12em; text-transform: uppercase; color: var(--e-text-2); }
.e-muted  { color: var(--e-text-3); }
.e-dim    { color: var(--e-text-4); }
.e-ellipsis { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.e-spacer { margin-left: auto; }
.e-only-sm { display: none; }

/* ---------- 3. SHELL: nav rail (ย่อได้/เต็มได้) + topbar ------------------
   เมนูเยอะ → จัดเป็นหมวด 9 หมวด
   · โหมดย่อ 64px  : ไอคอนหมวด + flyout รายการเมื่อชี้/แตะ
   · โหมดเต็ม 256px: หัวหมวด + รายการทั้งหมด (พับได้) — จำค่าไว้ใน localStorage
   ------------------------------------------------------------------------ */
.e-app { display: flex; min-height: 100vh; }

.e-rail {
  width: var(--e-rail-w); flex: none; background: var(--e-a-900);
  display: flex; flex-direction: column; align-items: center;
  position: sticky; top: 0; height: 100vh; z-index: 1045;
}
.e-rail-top {
  width: 100%; flex: none; display: flex; flex-direction: column; align-items: center;
  gap: 5px; padding: 10px 0 8px; border-bottom: 1px solid rgba(255,255,255,.09);
}
.e-rail-mark {
  width: 34px; height: 34px; flex: none; background: var(--e-a); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font: 600 16px var(--e-font-head); text-decoration: none;
}
/* โลโก้จริงแทนตัวอักษรย่อ (9 ก.ย. 2569)
   🔴 ไฟล์โลโก้มีพื้นหลัง "ขาวทึบ" ไม่โปร่งใส แต่ rail เป็นพื้นเข้ม --e-a-900
      ⇒ วางดิบ ๆ ได้สี่เหลี่ยมขาวโผล่กลางแถบเข้ม ⇒ ทำเป็นแผ่นขาวมุมมนให้ดูตั้งใจ
   ⚠️ คลาส .has-logo เป็นคลาสใหม่ที่ไม่มีที่ไหนใช้มาก่อน ⇒ ไม่กระทบ element เดิมสักตัว
      วันที่ได้ไฟล์โลโก้พื้นโปร่งใส/สีขาว ให้ถอด .has-logo ออกแล้วลบบล็อกนี้ได้เลย */
.e-rail-mark.has-logo { background: #fff; border-radius: 8px; padding: 3px; overflow: hidden; }
.e-rail-mark.has-logo img { width: 100%; height: 100%; object-fit: contain; display: block; }
.e-rail-brand { display: none; min-width: 0; }
.e-rail-brand-n { font: 600 14px var(--e-font-head); color: #fff; white-space: nowrap; }
.e-rail-brand-s { font-size: 10.5px; color: #6d8aa5; white-space: nowrap; }
.e-rail-toggle {
  width: 30px; height: 24px; flex: none; background: none; border: 0; padding: 0;
  color: #6d8aa5; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 12px;
}
.e-rail-toggle:hover { color: #fff; background: rgba(255,255,255,.08); }

.e-rail-scroll {
  flex: 1; width: 100%; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 6px 0 10px;
}
.e-rail-scroll::-webkit-scrollbar { width: 3px; }
.e-rail-scroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,.2); }

/* ปุ่มระดับบน: ลิงก์เดี่ยว + หัวหมวด — โครงเดียวกัน */
.e-rail-link, .e-rail-cat {
  width: 48px; min-height: 46px; flex: none; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px; padding: 4px 0;
  color: var(--e-a-400); text-decoration: none; background: none; border: 0;
  cursor: pointer; text-align: center; font-family: var(--e-font-head);
}
.e-rail-link i, .e-rail-cat > i { font-size: 17px; }
.e-rail-link span, .e-rail-cat span { font: 500 8px var(--e-font-head); letter-spacing: .01em; }
.e-rail-cat-l, .e-rail-caret, .e-rail-n { display: none; }
.e-rail-link:hover, .e-rail-cat:hover { background: rgba(255,255,255,.08); color: #fff; }
.e-rail-link.active { background: rgba(255,255,255,.12); color: #fff; }
.e-rail-link.active span { font-weight: 600; }
.e-rail-grp { width: 100%; display: flex; flex-direction: column; align-items: center; position: relative; }
.e-rail:not(.is-wide) .e-rail-cat.has-active { background: rgba(255,255,255,.12); color: #fff; }
.e-rail:not(.is-wide) .e-rail-grp.is-flyout > .e-rail-cat { background: rgba(255,255,255,.16); color: #fff; }

/* รายการในหมวด — โหมดย่อแสดงเป็น flyout (JS วางตำแหน่งด้วย position:fixed) */
.e-rail-sub { display: none; }
.e-rail:not(.is-wide) .e-rail-grp.is-flyout .e-rail-sub {
  display: block; position: fixed; width: 248px; background: #16232f;
  border: 1px solid rgba(255,255,255,.16); padding: 2px 0 6px; z-index: 1060;
  max-height: calc(100vh - 16px); overflow-y: auto;
}
.e-rail-subhd {
  padding: 10px 14px 6px; font: 600 8.5px var(--e-font-head);
  letter-spacing: .16em; text-transform: uppercase; color: #6d8aa5;
}
.e-rail-item {
  display: flex; align-items: center; gap: 10px; min-height: 38px; padding: 0 14px;
  color: var(--e-a-400); text-decoration: none; font: 500 13px var(--e-font-body);
  border-left: 2px solid transparent;
}
.e-rail-item i { flex: none; width: 17px; text-align: center; font-size: 14px; color: #6d8aa5; }
.e-rail-item span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.e-rail-item:hover { background: rgba(255,255,255,.08); color: #fff; }
.e-rail-item:hover i { color: #fff; }
.e-rail-item.active { background: rgba(255,255,255,.13); color: #fff; border-left-color: var(--e-a-400); font-weight: 600; }
.e-rail-n { margin-left: auto; flex: none; font: 600 10.5px var(--e-font-num); color: #6d8aa5; }
.e-rail:not(.is-wide) .e-rail-grp.is-flyout .e-rail-n, .e-rail.is-wide .e-rail-n { display: block; }
.e-rail-n.is-danger { color: #e08d85; }
.e-rail-n.is-warn { color: #dfae7c; }

.e-rail-foot {
  flex: none; width: 100%; margin-top: auto; padding: 8px; display: flex;
  align-items: center; gap: 9px; justify-content: center; border-top: 1px solid rgba(255,255,255,.09);
}
.e-rail-user { display: flex; align-items: center; gap: 9px; min-width: 0; text-decoration: none; }
.e-rail-avatar {
  width: 36px; height: 36px; flex: none; background: var(--e-a); color: #fff; text-decoration: none;
  display: flex; align-items: center; justify-content: center; font: 600 15px var(--e-font-head);
}
.e-rail-user-bd { display: none; min-width: 0; }
.e-rail-user-n { font: 600 12px var(--e-font-head); color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.e-rail-user-s { font-size: 10.5px; color: #6d8aa5; }
.e-rail-out { display: none; margin-left: auto; flex: none; color: #6d8aa5; font-size: 15px; }
.e-rail-out:hover { color: #fff; }

/* ---------- 3b. โหมดเมนูเต็ม (.is-wide — มือถือใช้โหมดนี้เสมอ) ----------- */
.e-rail.is-wide { width: var(--e-rail-w-wide); align-items: stretch; }
.e-rail.is-wide .e-rail-top { flex-direction: row; align-items: center; gap: 10px; padding: 12px 11px 11px; }
.e-rail.is-wide .e-rail-brand { display: block; flex: 1; }
.e-rail.is-wide .e-rail-scroll { align-items: stretch; padding: 6px 0 14px; }
.e-rail.is-wide .e-rail-grp { align-items: stretch; }
.e-rail.is-wide .e-rail-link, .e-rail.is-wide .e-rail-cat {
  width: auto; flex-direction: row; justify-content: flex-start; gap: 11px;
  min-height: 40px; padding: 0 13px; text-align: left;
}
.e-rail.is-wide .e-rail-link i, .e-rail.is-wide .e-rail-cat > i { font-size: 15px; width: 18px; text-align: center; }
.e-rail.is-wide .e-rail-cat-s { display: none; }
.e-rail.is-wide .e-rail-link span, .e-rail.is-wide .e-rail-cat .e-rail-cat-l {
  display: block; font: 600 13px var(--e-font-head); letter-spacing: 0;
}
.e-rail.is-wide .e-rail-caret { display: block; margin-left: auto; font-size: 10px; transition: transform .16s ease; }
.e-rail.is-wide .e-rail-cat.has-active { color: #fff; }
.e-rail.is-wide .e-rail-grp.open > .e-rail-cat { color: #fff; }
.e-rail.is-wide .e-rail-grp.open .e-rail-caret { transform: rotate(180deg); }
/* เส้นนำสายตาแนวตั้งของกลุ่มย่อย — วางที่กึ่งกลางไอคอนของหมวดแม่พอดี (13 + 18/2 = 22px)
   🔴🔴 วาดด้วย **พื้นหลังไล่สี ไม่ใช่ ::before ที่ position:absolute** — เจตนา ไม่ใช่ความชอบส่วนตัว:
   ① ::before แบบนั้นบังคับให้ `.e-rail-sub` ต้องเป็น `position: relative`
      ⇒ inline style `left`/`top` ที่ `openFlyout()` (`ems-field.js:198-201`) เขียนค้างไว้
        **ตื่นขึ้นมาทำงานทันที** (โหมดหดเขียนไว้ · `closeFlyouts()` ลบแค่คลาส ไม่ลบ inline style)
      ⇒ ย่อเมนู → ชี้หมวด → ขยายเมนู = เมนูย่อยกระเด็นไปทับหมวดอื่น (เจอจริง · ผู้ใช้รายงาน 9 ก.ย. 2569)
   ② `position: relative` ยังสร้าง containing block ให้ลูกที่เป็น `position: fixed` ด้วย — ตระกูล D194
   ⇒ พื้นหลังไล่สี **ไม่ต้องมี position เลย** ⇒ ค่า inline ที่ค้างอยู่กลับไปเป็นค่าที่ไม่มีผลเหมือนเดิม
     และไม่มีอะไรวางทับลิงก์ให้ต้องกัน `pointer-events` (ปลอดภัยโดยรูปทรง ไม่ใช่โดยวินัย · D153)
   ⚠️ `background-clip: content-box` ตัดเส้นให้จบที่รายการสุดท้าย (padding ล่าง 6px) ไม่ล้ำลงไปในช่องว่าง */
.e-rail.is-wide .e-rail-grp.open .e-rail-sub {
  display: block; padding: 1px 0 6px;
  background-image: linear-gradient(to right,
    transparent 22px, rgba(255,255,255,.12) 22px, rgba(255,255,255,.12) 23px, transparent 23px);
  background-clip: content-box;
}
.e-rail.is-wide .e-rail-subhd { display: none; }
/* เมนูย่อยโหมดขยาย — โชว์ไอคอนเหมือนโหมด flyout แล้วย่อหน้าลึกกว่าหมวดแม่ 1 ขั้น
   หมวดแม่ (:257-261): padding 13 + ไอคอน 18 + gap 11 ⇒ ข้อความเริ่มที่ 42px
   รายการลูก        : ขอบ 2 + padding 24 + ไอคอน 17 + gap 10 ⇒ ข้อความเริ่มที่ 53px
   ⇒ ไอคอนลูกเยื้องจากไอคอนแม่ 13px · ข้อความลูกเยื้องจากข้อความแม่ 11px
   🔴 `ขอบ 2` ในสมการคือ `border-left: 2px solid transparent` ที่กฎฐาน (:224) ตั้งไว้ให้แถบ active
      ⇒ padding-left ของลูกเริ่มนับ **หลัง** ขอบนั้น ส่วน .e-rail-cat ไม่มีขอบเลย
      ⇒ เขียน `padding-left: 26px` ตรง ๆ จะได้ไอคอนที่ 28px ไม่ใช่ 26px (วัดจริงด้วย probe_rail.js)
   ⚠️ ไอคอนถูกพิมพ์อยู่แล้วที่ header.php:298 — เดิมถูกกฎ `display:none` บรรทัดนี้ซ่อนไว้เฉย ๆ
      ตอนนี้ปล่อยให้ตกไปใช้กฎฐาน .e-rail-item i (:226) ร่วมกับ flyout ⇒ ไม่มีสำเนากฎซ้ำ */
.e-rail.is-wide .e-rail-item { padding: 0 13px 0 24px; min-height: 36px; font-size: 12.5px; }
.e-rail.is-wide .e-rail-item i { font-size: 13px; }
.e-rail.is-wide .e-rail-foot { justify-content: flex-start; padding: 9px 12px; }
.e-rail.is-wide .e-rail-avatar { width: 32px; height: 32px; font-size: 13px; }
.e-rail.is-wide .e-rail-user-bd, .e-rail.is-wide .e-rail-out { display: block; }

.e-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.e-topbar {
  min-height: var(--e-topbar-h); flex: none; background: var(--e-surface);
  border-bottom: var(--e-line); display: flex; align-items: center;
  gap: 14px; padding: 0 var(--e-gutter); position: sticky; top: 0; z-index: 30;
}
/* แผน 35 · F9 ข้อ 37 (ข้อตัดสิน §27.1 ⑤) — ปลายทาง # ของการโหลดเต็ม (ลิงก์จากหน้าอื่น · ทางถอย hardLoad ของ ems-nav.js) ต้องไม่ไปอยู่ใต้แถบบน sticky
   เดิมเบราว์เซอร์เลื่อนให้ขอบบนของปลายทางชิดขอบจอ = ซ่อนอยู่ใต้แถบบน 58–60px · ระยะ +12 เท่ากับ anchorY() ของตัวขับ SPA (ห่างกันไม่เกิน 2px:
   ตัวแปรเป็น 58 แต่แถบจริงสูง 60 ที่ 390 — ตัวขับวัดแถบจริง)
   ⚠️ เฉพาะปลายทาง # (:target) — ไม่ใช้ scroll-padding-top ของเอกสาร (จะกระทบ scrollIntoView ใน 11 ไฟล์ + การเลื่อนตามโฟกัส)
   ⚠️ ตัวขับ SPA เลื่อนด้วย window.scrollTo ซึ่ง scroll-margin ไม่มีผล ⇒ ไม่เลื่อนซ้อน · ตัววัด: _tests/ui/verify_p35_f9_theme.php */
:target { scroll-margin-top: calc(var(--e-topbar-h) + 12px); }
.e-topbar-t { font: 600 18px/1.1 var(--e-font-head); }
.e-topbar-s { font-size: 11.5px; color: var(--e-text-3); }
.e-topbar-ti { min-width: 90px; flex: 0 1 auto; }
.e-topbar-t, .e-topbar-s { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.e-topbar-acts { margin-left: auto; display: flex; align-items: center; gap: 10px; }

/* ค้นหาทั้งระบบ (global search) — อยู่บน topbar ทุกหน้า, กด / เพื่อโฟกัส */
.e-gsearch { position: relative; flex: 1 1 260px; min-width: 0; max-width: 460px; margin-left: 4px; }
.e-gsearch-in {
  display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 11px;
  border: var(--e-line); background: var(--e-surface-alt);
}
.e-gsearch-in:focus-within { background: var(--e-surface); outline: 2px solid var(--e-a); outline-offset: -1px; }
.e-gsearch-in > i { color: var(--e-text-4); font-size: 14px; }
.e-gsearch input {
  flex: 1; min-width: 0; border: 0; outline: none; background: transparent;
  font: 400 13px var(--e-font-body); color: var(--e-text);
}
.e-gsearch input::-webkit-search-cancel-button { display: none; }
.e-gsearch kbd {
  flex: none; font: 600 10px var(--e-font-num); color: var(--e-text-4);
  border: var(--e-line-soft); background: var(--e-surface); padding: 1px 5px;
}
.e-topbar .e-gsearch-btn { display: none; }
.e-gsearch-panel {
  display: none; position: absolute; top: calc(100% + 5px); left: 0; right: 0;
  background: var(--e-surface); border: var(--e-line); max-height: 64vh; overflow-y: auto; z-index: 60;
}
.e-gsearch.is-open .e-gsearch-panel { display: block; }
.e-gs-hd {
  padding: 7px 12px 5px; background: var(--e-surface-alt); border-bottom: var(--e-line-soft);
  font: 600 9px var(--e-font-head); letter-spacing: .14em; text-transform: uppercase; color: var(--e-text-2);
}
/* 🔴 U9.1 · แถวผลค้นสด — ของเดิม min-height: 42px ซึ่งอยู่ **นอก** media query
      ⇒ เตี้ยกว่า 44px ทั้ง 2 ความกว้าง (ของค้าง E จาก U8.3) · ต่างจาก .e-btn/.e-pill
        ที่ธีมยกให้บนมือถืออยู่แล้ว ⇒ D170 ไม่ได้ตัดข้อนี้ออก · ห่างแค่ 2px จึงแก้ที่กฎฐานเลย */
.e-gs-row {
  display: flex; align-items: center; gap: 10px; min-height: var(--e-tap); padding: 6px 12px;
  border-bottom: var(--e-line-soft); color: var(--e-text); text-decoration: none;
}
.e-gs-row:last-of-type { border-bottom: 0; }
.e-gs-row:hover, .e-gs-row.is-cur { background: var(--e-a-100); color: var(--e-text); }
.e-gs-ic {
  width: 26px; height: 26px; flex: none; background: var(--e-surface-alt); border: var(--e-line-soft);
  display: flex; align-items: center; justify-content: center; color: var(--e-text-3); font-size: 13px;
}
.e-gs-bd { min-width: 0; flex: 1; }
.e-gs-t { display: block; font-size: 12.5px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.e-gs-s { display: block; font-size: 10.5px; color: var(--e-text-4); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.e-gs-empty { padding: 16px 12px; font-size: 12px; color: var(--e-text-3); }
.e-gs-foot {
  display: flex; gap: 12px; padding: 7px 12px; background: var(--e-surface-alt);
  border-top: var(--e-line-soft); font-size: 10.5px; color: var(--e-text-4);
}
.e-icon-btn {
  background: none; border: 0; padding: 0; color: var(--e-text-2);
  font-size: 16px; line-height: 1; cursor: pointer; position: relative;
  min-width: 32px; min-height: 32px; display: inline-flex; align-items: center; justify-content: center;
}
.e-icon-btn:hover { color: var(--e-a-700); }
.e-badge-dot {
  position: absolute; top: 0; right: 0; background: var(--e-danger); color: #fff;
  font: 600 9px var(--e-font-body); min-width: 15px; height: 15px;
  display: flex; align-items: center; justify-content: center; padding: 0 3px;
}
.e-burger { display: none; background: none; border: 0; color: inherit; font-size: 20px; padding: 0; cursor: pointer; }
.e-back { display: inline-flex; align-items: center; color: var(--e-text-2); font-size: 17px; }
.e-scrim { display: none; position: fixed; inset: 0; background: rgba(29,45,61,.45); z-index: 1044; }

.e-content { flex: 1; min-width: 0; padding: 20px var(--e-gutter) 28px; }

/* หัวข้อของบล็อกในหน้า */
.e-sec-hd { display: flex; align-items: baseline; gap: 10px; margin: 0 0 12px; flex-wrap: wrap; }
.e-sec-hd h2 { font-size: 16px; }
.e-sec-hd .e-note { font-size: 12px; color: var(--e-text-3); }
.e-sec-hd a { font-size: 12px; }

/* ---------- 4. LAYOUT ---------------------------------------------------- */
.e-cards { display: grid; gap: var(--e-gap); grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.e-cards-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.e-cards-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.e-cards-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.e-stack { display: flex; flex-direction: column; gap: var(--e-gap); }
.e-row { display: flex; align-items: center; gap: 10px; }
.e-row-wrap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.e-split-2 { display: grid; grid-template-columns: 300px 1fr; gap: 20px; }

/* ---------- 5. PANEL / STAT ---------------------------------------------- */
.e-panel { background: var(--e-surface); border: var(--e-line); min-width: 0; }
.e-panel-hd {
  min-height: 38px; display: flex; align-items: center; gap: 8px; padding: 8px 14px;
  border-bottom: var(--e-line); background: var(--e-surface-alt); font: 600 13px var(--e-font-head);
}
.e-panel-bd { padding: 14px 16px; }

/* แถวตัวเลขสรุป — แถบสีบนหัวการ์ดบอกความหมาย ไม่ใช่พื้นสีทั้งใบ */
.e-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
.e-stat { background: var(--e-surface); border: var(--e-line); border-top: 4px solid var(--e-n-400); padding: 13px 15px; }
.e-stat-label { font: 600 9.5px var(--e-font-head); letter-spacing: .12em; text-transform: uppercase; color: var(--e-text-2); margin-bottom: 5px; }
.e-stat-value { font: 600 32px/1 var(--e-font-head); }
.e-stat-sub { font-size: 11px; color: var(--e-text-3); margin-top: 4px; }
.e-stat.is-danger { border-top-color: var(--e-danger); } .e-stat.is-danger .e-stat-label, .e-stat.is-danger .e-stat-value { color: var(--e-danger); }
.e-stat.is-warn   { border-top-color: var(--e-warn); }   .e-stat.is-warn .e-stat-label,   .e-stat.is-warn .e-stat-value   { color: var(--e-warn); }
.e-stat.is-info   { border-top-color: var(--e-a); }      .e-stat.is-info .e-stat-label,   .e-stat.is-info .e-stat-value   { color: var(--e-info); }
.e-stat.is-ok     { border-top-color: var(--e-ok); }     .e-stat.is-ok .e-stat-label,     .e-stat.is-ok .e-stat-value     { color: var(--e-ok); }

/* การ์ดความคืบหน้าวันนี้ (วงแหวน + ข้อความ) */
.e-today { background: var(--e-surface); border: var(--e-line); padding: 16px; display: flex; align-items: center; gap: 18px; }
.e-today-t { font: 600 15px var(--e-font-head); margin-bottom: 5px; }
.e-today-s { font-size: 12px; color: var(--e-text-2); line-height: 1.7; }

/* ---------- 6. BUTTONS / CHIPS / FILTERS --------------------------------- */
.e-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 38px; padding: 0 15px; border: var(--e-line); background: var(--e-surface);
  color: var(--e-text); font: 600 13px var(--e-font-head); cursor: pointer;
  border-radius: var(--e-radius); text-decoration: none; white-space: nowrap;
}
.e-btn i { font-size: 14px; }
.e-btn:hover { background: var(--e-a-100); border-color: var(--e-a-300); color: var(--e-text); }
.e-btn:active { background: var(--e-a-200); }
.e-btn-primary { background: var(--e-a); border-color: var(--e-a); color: #fff; }
.e-btn-primary:hover { background: var(--e-a-600); border-color: var(--e-a-600); color: #fff; }
.e-btn-primary:active { background: var(--e-a-700); }
.e-btn-outline { border-color: var(--e-a); color: var(--e-a-700); }
.e-btn-outline:hover { background: var(--e-a-100); }
.e-btn-ghost { border-color: transparent; background: transparent; color: var(--e-a-700); }
.e-btn-ghost:hover { background: var(--e-a-100); }
.e-btn-danger { background: var(--e-danger); border-color: var(--e-danger); color: #fff; }
.e-btn-danger:hover { background: #8c211a; border-color: #8c211a; color: #fff; }
.e-btn-lg { height: 48px; font-size: 14.5px; padding: 0 18px; }
.e-btn-block { width: 100%; }
.e-btn:disabled, .e-btn[aria-disabled="true"] { opacity: .45; pointer-events: none; }

.e-chip {
  display: inline-flex; align-items: center; gap: 5px; height: 21px; padding: 0 9px;
  font: 600 11px var(--e-font-head); background: var(--e-mute-bg); color: var(--e-mute); white-space: nowrap;
}
.e-chip.is-danger { background: var(--e-danger-bg); color: var(--e-danger); }
.e-chip.is-warn   { background: var(--e-warn-bg);   color: var(--e-warn); }
.e-chip.is-ok     { background: var(--e-ok-bg);     color: var(--e-ok); }
.e-chip.is-info   { background: var(--e-info-bg);   color: var(--e-info); }
.e-chip-sm { height: 20px; font-size: 10.5px; padding: 0 8px; }
/* chip แบบเส้นขอบ + จุดสถานะ (ใช้บนรูปเครื่องจักร) */
.e-chip-o {
  display: inline-flex; align-items: center; gap: 5px; height: 20px; padding: 0 8px;
  background: var(--e-surface); border: 1px solid currentColor; font: 600 10.5px var(--e-font-head);
}
.e-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: none; }

.e-pill {
  display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px;
  border: var(--e-line); background: var(--e-surface); color: var(--e-text-2);
  font: 600 12px var(--e-font-head); cursor: pointer;
}
.e-pill:hover { border-color: var(--e-a-400); color: var(--e-text); }
.e-pill.active { background: var(--e-a-800); border-color: var(--e-a-800); color: #fff; }
.e-pill .e-n { font-variant-numeric: tabular-nums; opacity: .75; }

.e-seg { display: inline-flex; border: var(--e-line); }
.e-seg > * {
  height: 38px; display: inline-flex; align-items: center; gap: 6px; padding: 0 14px;
  background: var(--e-surface); color: var(--e-text-2); font: 600 13px var(--e-font-head);
  border: 0; cursor: pointer;
}
.e-seg > *.active { background: var(--e-a-800); color: #fff; }

.e-field { display: flex; flex-direction: column; gap: 5px; }
.e-field > label { font: 600 9.5px var(--e-font-head); letter-spacing: .12em; text-transform: uppercase; color: var(--e-text-2); }
.e-input, .e-select, .e-search {
  height: 38px; padding: 0 12px; border: var(--e-line); background: var(--e-surface);
  font: 400 13px var(--e-font-body); color: var(--e-text); border-radius: 0;
}
textarea.e-input { height: auto; min-height: 84px; padding: 9px 12px; }
.e-input:focus, .e-select:focus, .e-search:focus { outline: 2px solid var(--e-a); outline-offset: -1px; }
.e-searchbox { display: flex; align-items: center; gap: 8px; height: 38px; padding: 0 12px; border: var(--e-line); background: var(--e-surface); flex: 1; max-width: 340px; }
.e-searchbox input { border: 0; outline: none; background: transparent; width: 100%; font: 400 13px var(--e-font-body); }

/* ---------- 7. JOB CARD (แกนหลักของแนวทางนี้) ---------------------------- */
.e-card {
  background: var(--e-surface); border: var(--e-line); border-left: 5px solid var(--e-n-400);
  display: flex; flex-direction: column;
}
.e-card.is-danger { border-left-color: var(--e-danger); }
.e-card.is-warn   { border-left-color: var(--e-warn); }
.e-card.is-ok     { border-left-color: var(--e-ok); }
.e-card.is-info   { border-left-color: var(--e-a); }
.e-card-bd { padding: 14px 16px 12px; flex: 1; }
.e-card-hd { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; flex-wrap: wrap; }
.e-card-due { margin-left: auto; display: inline-flex; align-items: center; gap: 5px; font: 600 11.5px var(--e-font-head); color: var(--e-text-2); }
.e-card-due.is-danger { color: var(--e-danger); } .e-card-due.is-warn { color: var(--e-warn); }
.e-card-due.is-ok { color: var(--e-ok); } .e-card-due.is-info { color: var(--e-info); }
.e-card-title { font: 600 16px/1.35 var(--e-font-head); margin-bottom: 10px; }
.e-card-meta { display: flex; align-items: center; gap: 10px; font-size: 11.5px; color: var(--e-text-2); flex-wrap: wrap; }
.e-card-meta i { color: var(--e-n-400); }

/* แถบเครื่องจักรในการ์ด */
.e-equip { display: flex; align-items: center; gap: 9px; padding: 8px 10px; background: var(--e-surface-alt); border: var(--e-line-soft); margin-bottom: 10px; }
.e-equip-ic { width: 32px; height: 32px; flex: none; background: var(--e-n-200); display: flex; align-items: center; justify-content: center; color: var(--e-text-3); }
.e-equip-code { font: 600 11.5px var(--e-font-num); color: var(--e-a-800); }
.e-equip-loc { font-size: 10.5px; color: var(--e-text-4); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* แถบปุ่มลงมือทำ — เส้นคั่นเกิดจาก gap 1px */
.e-card-acts { display: flex; gap: 1px; background: var(--e-n-200); border-top: var(--e-line-soft); }
.e-act {
  flex: 1; min-height: var(--e-tap); display: inline-flex; align-items: center; justify-content: center;
  gap: 7px; background: var(--e-surface); color: var(--e-text); border: 0;
  font: 600 13px var(--e-font-head); cursor: pointer; text-decoration: none; padding: 0 8px;
}
.e-act i { font-size: 14px; }
.e-act:hover { background: var(--e-a-100); color: var(--e-text); }
.e-act.is-primary { background: var(--e-a); color: #fff; }
.e-act.is-primary:hover { background: var(--e-a-600); color: #fff; }
.e-act-icon { flex: none; width: 52px; color: var(--e-text-2); }

/* meter (ชั่วโมง / รอบ PM) */
.e-meterrow { display: flex; align-items: center; gap: 8px; }
.e-meter { flex: 1; height: 6px; background: var(--e-n-200); position: relative; }
.e-meter > span { position: absolute; inset: 0 auto 0 0; background: var(--e-a); }
.e-meter-sm { height: 5px; }

/* ---------- 8. EQUIPMENT CARD -------------------------------------------- */
.e-eq { background: var(--e-surface); border: var(--e-line); display: flex; flex-direction: column; }
.e-eq-thumb {
  height: 118px; background: var(--e-surface-alt); border-bottom: var(--e-line-soft);
  display: flex; align-items: center; justify-content: center; color: var(--e-n-300);
  font-size: 42px; position: relative;
}
.e-eq-code { position: absolute; top: 9px; left: 9px; font: 600 10.5px var(--e-font-num); background: var(--e-a-900); color: #fff; padding: 3px 7px; }
.e-eq-status { position: absolute; bottom: 9px; left: 9px; }
.e-eq-qr { position: absolute; top: 9px; right: 9px; width: 26px; height: 26px; background: var(--e-surface); border: var(--e-line); display: flex; align-items: center; justify-content: center; font-size: 12px; color: var(--e-text-2); }
.e-eq-bd { padding: 12px 14px 13px; flex: 1; display: flex; flex-direction: column; }
.e-eq-name { font: 600 15px/1.3 var(--e-font-head); margin-bottom: 2px; }
.e-eq-brand { font-size: 11px; color: var(--e-text-4); margin-bottom: 9px; }
.e-eq-meta { display: flex; flex-direction: column; gap: 4px; font-size: 11.5px; color: var(--e-text-2); margin-bottom: 11px; }
.e-eq-meta i { color: var(--e-n-400); }
.e-eq-pm { margin-top: auto; background: var(--e-surface-alt); border: var(--e-line-soft); padding: 8px 10px; }
.e-eq-pm-hd { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 5px; }
.e-eq-pm-hd .e-kicker { color: var(--e-text-4); }
.e-eq-pm-v { font: 600 12px var(--e-font-head); }
/* แถวเครื่องจักรบนมือถือ */
.e-eqrow { background: var(--e-surface); border: var(--e-line); display: flex; }
.e-eqrow-thumb { width: 96px; flex: none; background: var(--e-surface-alt); border-right: var(--e-line-soft); display: flex; align-items: center; justify-content: center; font-size: 30px; color: var(--e-n-300); position: relative; }
.e-eqrow-code { position: absolute; bottom: 6px; left: 6px; right: 6px; text-align: center; font: 600 9.5px var(--e-font-num); background: var(--e-a-900); color: #fff; padding: 2px 0; }
.e-eqrow-bd { flex: 1; min-width: 0; padding: 11px 13px 12px; }

/* ---------- 9. CHECKLIST / TIMER (หน้า execute) -------------------------- */
.e-timer { display: flex; align-items: center; gap: 14px; background: var(--e-a-900); color: #fff; padding: 12px 16px; }
.e-timer-v { font: 600 30px/1 var(--e-font-head); font-variant-numeric: tabular-nums; }
.e-timer .e-kicker { color: var(--e-a-400); }
.e-check { border: var(--e-line); background: var(--e-surface); }
.e-check-row { display: flex; align-items: flex-start; gap: 12px; padding: 12px 14px; border-bottom: var(--e-line-soft); min-height: var(--e-tap); cursor: pointer; }
.e-check-row:last-child { border-bottom: 0; }
.e-check-row:hover { background: var(--e-a-100); }
.e-check-box { width: 22px; height: 22px; flex: none; border: 2px solid var(--e-n-400); display: flex; align-items: center; justify-content: center; color: transparent; font-size: 13px; margin-top: 1px; }
.e-check-row.done .e-check-box { background: var(--e-a); border-color: var(--e-a); color: #fff; }
.e-check-row.done .e-check-t { color: var(--e-text-3); text-decoration: line-through; }
.e-check-t { font-size: 13.5px; }
.e-check-s { font-size: 11px; color: var(--e-text-4); margin-top: 2px; }

/* callout / alert */
.e-callout { border: 1px solid var(--e-mute-bd); border-left: 4px solid var(--e-mute); background: var(--e-surface); padding: 12px 14px; }
.e-callout.is-danger { border-color: var(--e-danger-bd); border-left-color: var(--e-danger); }
.e-callout.is-warn { border-color: var(--e-warn-bd); border-left-color: var(--e-warn); }
.e-callout-t { font: 600 14px var(--e-font-head); margin-bottom: 4px; }
.e-alertbar { display: flex; align-items: center; gap: 9px; padding: 10px 16px; background: var(--e-danger); color: #fff; font: 600 13px var(--e-font-head); }
.e-alertbar.is-warn { background: var(--e-warn); }

/* ---------- 10. TABLE (เหลือไว้ให้หน้ารายงาน) ---------------------------- */
.e-table-wrap { border: var(--e-line); background: var(--e-surface); overflow-x: auto; }
.e-table { width: 100%; border-collapse: collapse; }
.e-table thead th {
  text-align: left; padding: 8px 10px; background: var(--e-surface-alt);
  font: 600 9px var(--e-font-head); letter-spacing: .12em; text-transform: uppercase;
  color: var(--e-text-2); border-bottom: var(--e-line); white-space: nowrap;
}
.e-table tbody td { padding: 0 10px; height: 46px; border-bottom: var(--e-line-soft); font-size: 12.5px; vertical-align: middle; }
.e-table tbody tr:last-child td { border-bottom: 0; }
.e-table tbody tr:hover td { background: var(--e-a-100); }
.e-table .e-t-num { text-align: right; font: 600 12px var(--e-font-num); }
/* หัวคอลัมน์ตัวเลข — แผน 35 · F8 ข้อ 17: กฎข้างบน (0,2,0) ชนะ `.e-table thead th` (0,1,2) ⇒ <th class="e-t-num"> เคยได้ฟอนต์ตัวเลข 12px
   ขณะหัวข้างเคียงได้ 9px ⇒ คืนฟอนต์ของหัวตาราง (ค่าเดียวกับกฎ thead th ข้างบน) โดยยังชิดขวาตาม .e-t-num · เซลล์ td.e-t-num ไม่ถูกแตะ
   ⚠️ เปลี่ยนฟอนต์ของ `.e-table thead th` เมื่อไร ต้องเปลี่ยนบรรทัดนี้ด้วย — ตัววัด: _tests/ui/probe_p35_f8.js ข้อ 17 (เทียบกับหัวข้างเคียงทุกตัว) */
.e-table thead th.e-t-num { font: 600 9px var(--e-font-head); }

/* ---------- 11. MOBILE NAV ----------------------------------------------- */
.e-bottomnav {
  display: none; position: sticky; bottom: 0; z-index: 40; background: var(--e-surface);
  border-top: var(--e-line); height: var(--e-navh); align-items: stretch;   /* ต้นฉบับ :495 = 52px — §3 หัวข้อ 19 ให้ใช้ token ตัวเดียว */
}
.e-bottomnav a {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; color: var(--e-text-4); text-decoration: none; font-family: var(--e-font-head);
}
.e-bottomnav a i { font-size: 17px; }
.e-bottomnav a span { font: 500 9px var(--e-font-head); }
.e-bottomnav a.active { color: var(--e-a); }
.e-bottomnav a.active span { font-weight: 600; }
/* ต้นฉบับ :505-511 = .e-fab-slot + .e-fab (ปุ่ม FAB กลางแถบล่าง) — ตัดทิ้งตาม §3 หัวข้อ 19
   เหตุผล: CSS ต้นฉบับ :594 ซ่อน FAB ทุกหน้าที่มี .e-actionbar อยู่แล้ว (§0.3 ข้อ 3) ⇒ ไม่เคยโผล่จริง
   "สแกน QR" ที่เคยเป็น FAB ย้ายไปเป็นก้อนแรกของวงล้อคำสั่ง (§2.4 จ) */
.e-actionbar { display: none; gap: 8px; padding: 9px 12px; background: var(--e-surface); border-top: var(--e-line); position: sticky; bottom: 0; z-index: 44; }
.e-actionbar .e-btn { flex: 1; min-height: 44px; font-size: 14px; }
.e-actionbar .e-btn-sq { flex: none; width: 52px; padding: 0; }

/* ปุ่มวงกลมลอย + วงล้อคำสั่ง (แสดงที่ <= 991.98px เท่านั้น — ดูหัวข้อ 19b) */
.e-air { display: none; }
.e-air-btn {
  width: 56px; height: 56px; border-radius: 50%; border: 0; padding: 0; cursor: pointer;
  background: var(--e-a); color: #fff; display: flex; align-items: center; justify-content: center;
  font-size: 22px; box-shadow: 0 3px 14px rgba(29,45,61,.34); position: relative; z-index: 2;
}
.e-air-btn:active { background: var(--e-a-700); }
body.e-air-open .e-air-btn { background: var(--e-a-900); }
/* ต้นฉบับ :525-530 = กล่องเมนูพัดแนวตั้ง — แทนด้วย .e-air-node หัวข้อ 19b */
.e-air-ic {
  width: 46px; height: 46px; flex: none; border-radius: 50%; background: var(--e-surface);
  border: 1px solid var(--e-n-300); color: var(--e-a-800); display: flex; align-items: center;
  justify-content: center; font-size: 18px; box-shadow: 0 2px 10px rgba(29,45,61,.2);
}
/* ต้นฉบับ :536-537 = สีของก้อนพัดแนวตั้ง (is-primary / :active) — ย้ายไปเป็น .e-air-node หัวข้อ 19b */
.e-air-lbl {
  white-space: nowrap; background: var(--e-a-900); color: #fff;
  font: 600 11.5px var(--e-font-head); padding: 4px 8px;
}

/* ---------- 12. RESPONSIVE ----------------------------------------------- */
@media (max-width: 1400px) {
  .e-cards-4 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 1200px) {
  .e-cards-3, .e-cards-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .e-split-2 { grid-template-columns: 260px 1fr; }
  .e-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 992px) {
  .e-split-2 { grid-template-columns: minmax(0, 1fr); }
  .e-cards-2, .e-cards-3, .e-cards-4 { grid-template-columns: minmax(0, 1fr); }
  .e-cards { grid-template-columns: minmax(0, 1fr); }
}
/* ≤768 — มือถือหน้างาน: rail กลายเป็น drawer, bottom nav ทำงาน */
@media (max-width: 768px) {
  :root { --e-gutter: 14px; --e-gap: 12px; }
  .e-rail {
    position: fixed; inset: 0 auto 0 0; width: var(--e-rail-w-wide);
    transform: translateX(-100%); transition: transform .22s ease;
  }
  .e-rail-toggle { display: none; }
  body.e-drawer-open .e-rail { transform: none; }
  body.e-drawer-open .e-scrim { display: block; }
  .e-burger { display: inline-flex; }
  .e-topbar { background: var(--e-a-900); border-bottom: 0; color: #fff; padding: 10px 14px; }
  .e-topbar .e-topbar-t { color: #fff; font-size: 16px; }
  .e-topbar .e-topbar-s { color: var(--e-a-400); }
  .e-topbar .e-icon-btn, .e-topbar .e-back { color: #fff; }
  .e-topbar .e-btn, .e-topbar .e-seg { display: none; }   /* ปุ่มหลักย้ายลง .e-actionbar */
  /* ค้นหา: บนมือถือซ่อนช่องไว้ใต้ topbar แล้วเปิดด้วยปุ่มไอคอน */
  .e-topbar .e-gsearch-btn { display: inline-flex; }
  .e-gsearch { display: none; }
  body.e-gsearch-open .e-gsearch {
    display: block; position: absolute; top: 100%; left: 0; right: 0; max-width: none;
    margin: 0; padding: 8px 12px 10px; background: var(--e-a-900); z-index: 50;
  }
  body.e-gsearch-open .e-gsearch-in { background: rgba(255,255,255,.12); border-color: rgba(255,255,255,.2); height: 40px; }
  body.e-gsearch-open .e-gsearch-in > i { color: #8fa9c1; }
  body.e-gsearch-open .e-gsearch input { color: #fff; }
  body.e-gsearch-open .e-gsearch input::placeholder { color: #8fa9c1; }
  .e-gsearch kbd { display: none; }
  .e-gsearch-panel { top: calc(100% + 2px); left: 12px; right: 12px; max-height: 70vh; }
  .e-content { padding: 14px var(--e-gutter) calc(var(--e-navh) + 24px); }   /* ต้นฉบับ :586 = 76px */
  /* ต้นฉบับ :587, :590, :592, :593 (เปิด .e-bottomnav/.e-air) ย้ายไปหัวข้อ 19 ที่ 991.98px — ข้อตัดสิน §1 ข้อ 28 */
  /* ต้นฉบับ :594 .e-actionbar ~ .e-bottomnav .e-fab-slot — ลบพร้อม .e-fab */
  .e-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .e-today { flex-direction: row; }
  .e-hide-sm { display: none !important; }
  .e-only-sm { display: revert; }
  .e-card-title { font-size: 16px; }
  .e-act { min-height: 46px; font-size: 13.5px; }
}
@media (max-width: 420px) {
  .e-stats { grid-template-columns: 1fr 1fr; }
  .e-stat-value { font-size: 27px; }
}

/* ---------- 13. PRINT ---------------------------------------------------- */
@media print {
  .e-rail, .e-topbar, .e-bottomnav, .e-actionbar, .e-scrim, .e-card-acts,
  .e-alertbar, .e-air, [data-e-air], .e-sheet, .e-gsearch,
  .sidebar, .topbar, .app-footer, .sidebar-overlay { display: none !important; }
  /* ต้นฉบับ :609 มีแค่ 6 ตัวแรก · §2.4 ช: ลืม .e-air ⇒ ปุ่มกลมติดกระดาษและ PDF (qr_print · po_print · daily pdf)
     4 ตัวท้ายเป็นคลาสเดิมของระบบ ต้องซ่อนด้วยตลอดช่วง bridge (U1-U9) */
  body { background: #fff; }
  .e-content { padding: 0; }
  .e-card, .e-eq, .e-panel { break-inside: avoid; }
}

/* ==========================================================================
   14. BRIDGE — คลาสเดิมของ EMS (bootstrap + app.css) ให้เข้าธีมทันที
   ใช้ระหว่าง migration หน้าไหนยังไม่แปลง markup ก็เปลี่ยนโฉมไปพร้อมกัน
   ลบทิ้งได้เมื่อแปลง markup ครบทุกหน้าแล้วเท่านั้น
   ========================================================================== */
.card, .modal-content, .dropdown-menu, .list-group-item, .form-control, .form-select,
.input-group-text, .btn, .badge, .alert, .nav-tabs .nav-link, .pagination .page-link { border-radius: 0 !important; }
.card { border: var(--e-line); box-shadow: none !important; }
.card-header { background: var(--e-surface-alt); border-bottom: var(--e-line); font-family: var(--e-font-head); font-weight: 600; }
.btn:not(.btn-sm):not(.btn-xs) { font-family: var(--e-font-head); font-weight: 600; min-height: 38px; }   /* ต้นฉบับ :624 = .btn ทุกตัว — ข้อตัดสิน §1 ข้อ 10 (ดัน .btn-sm ใน 75 ไฟล์ให้สูงขึ้น) */
.btn-primary { --bs-btn-bg: var(--e-a); --bs-btn-border-color: var(--e-a); --bs-btn-hover-bg: var(--e-a-600); --bs-btn-hover-border-color: var(--e-a-600); --bs-btn-active-bg: var(--e-a-700); }
.btn-outline-primary { --bs-btn-color: var(--e-a-700); --bs-btn-border-color: var(--e-n-300); --bs-btn-hover-bg: var(--e-a-100); --bs-btn-hover-color: var(--e-a-800); --bs-btn-hover-border-color: var(--e-a-300); }
.badge { font-family: var(--e-font-head); font-weight: 600; }
.table { --bs-table-bg: var(--e-surface); font-size: 12.5px; }
.table > thead th { background: var(--e-surface-alt); font-family: var(--e-font-head); font-size: 9px; letter-spacing: .12em; text-transform: uppercase; color: var(--e-text-2); }
h1, h2, h3, h4, h5, .card-title { font-family: var(--e-font-head); }
.sidebar { background: var(--e-a-900) !important; border-right: 0 !important; }
.sidebar .sidebar-logo, .sidebar .sidebar-bottom { border-color: rgba(255,255,255,.1) !important; }
.sidebar .sidebar-logo-icon, .sidebar .sidebar-new-btn { background: var(--e-a) !important; border-radius: 0 !important; }
.sidebar .sidebar-logo-name { color: #fff !important; font-family: var(--e-font-head); }
.sidebar .sidebar-logo-sub { color: var(--e-a-500) !important; }
.sidebar .nav-section, .sidebar .nav-group-header { color: #5d7891 !important; font-family: var(--e-font-head); letter-spacing: .16em; }
.sidebar .nav-link { color: var(--e-n-400) !important; border-radius: 0 !important; }
.sidebar .nav-link:hover { background: rgba(255,255,255,.06) !important; color: #fff !important; }
.sidebar .nav-link.active { background: rgba(255,255,255,.12) !important; color: #fff !important; }
.sidebar .sidebar-logout-link { color: var(--e-n-400) !important; }
.topbar { background: var(--e-surface); border-bottom: var(--e-line); box-shadow: none !important; }
.topbar-search { border: var(--e-line) !important; border-radius: 0 !important; background: var(--e-surface-alt) !important; }
.topbar-user-avatar, .user-menu-avatar-initial { border-radius: 0 !important; background: var(--e-a) !important; font-family: var(--e-font-head); }
.notif-badge { border-radius: 0 !important; background: var(--e-danger) !important; }
.main-content { background: var(--e-bg); }


/* ---------- 14b. KPI CHIPS (แผงตัวเลขบนหน้า dashboard) -------------------
   ชิปตัวเลขบนพื้น surface-alt ในแผง — ยกตัวขึ้นจากพื้นด้วยเส้น + ไล่สีบาง ๆ
   ------------------------------------------------------------------------ */
.e-kpi-bd { padding: 11px 12px 12px; background: var(--e-surface-alt); }
.e-kpi-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px; }
.e-kpi-bd .e-kpi-grid { grid-template-columns: repeat(auto-fill, minmax(138px, 1fr)); }
.e-kpi-grid-lead { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
.e-kpi {
  position: relative; display: flex; flex-direction: column; gap: 1px;
  padding: 9px 11px 10px; background: var(--e-surface); border: var(--e-line-soft);
  border-left: 3px solid var(--e-n-300); color: inherit; text-decoration: none;
  transition: transform .12s ease, border-color .12s ease, box-shadow .12s ease;
}
.e-kpi::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 1px;
  background: linear-gradient(90deg, rgba(255,255,255,.95), rgba(255,255,255,0));
}
a.e-kpi:hover { transform: translateY(-1px); border-color: var(--e-a-300); box-shadow: 0 2px 9px rgba(29,45,61,.12); color: inherit; }
.e-kpi-l { font: 600 9.5px var(--e-font-head); letter-spacing: .1em; text-transform: uppercase; color: var(--e-text-2); }
.e-kpi-v { font: 600 23px/1.08 var(--e-font-head); font-variant-numeric: tabular-nums; }
.e-kpi-v small { font: 600 12px var(--e-font-head); color: var(--e-text-3); margin-left: 3px; }
.e-kpi-s { font-size: 10.5px; color: var(--e-text-4); }
.e-kpi-money .e-kpi-v { font-size: 17px; letter-spacing: -.01em; }
.e-kpi.is-quiet .e-kpi-v { color: var(--e-text-4); }
.e-kpi.is-danger { border-left-color: var(--e-danger); background: linear-gradient(180deg, var(--e-danger-bg), var(--e-surface) 60%); }
.e-kpi.is-danger .e-kpi-l, .e-kpi.is-danger .e-kpi-v { color: var(--e-danger); }
.e-kpi.is-warn { border-left-color: var(--e-warn); background: linear-gradient(180deg, var(--e-warn-bg), var(--e-surface) 60%); }
.e-kpi.is-warn .e-kpi-l, .e-kpi.is-warn .e-kpi-v { color: var(--e-warn); }
.e-kpi.is-info { border-left-color: var(--e-a); background: linear-gradient(180deg, var(--e-a-100), var(--e-surface) 60%); }
.e-kpi.is-info .e-kpi-l, .e-kpi.is-info .e-kpi-v { color: var(--e-info); }
.e-kpi.is-ok { border-left-color: var(--e-ok); background: linear-gradient(180deg, var(--e-ok-bg), var(--e-surface) 60%); }
.e-kpi.is-ok .e-kpi-l, .e-kpi.is-ok .e-kpi-v { color: var(--e-ok); }

/* ชิปหลัก — แถวบนสุด: ตัวเลขใหญ่ + ไอคอนจาง */
.e-kpi-lg { padding: 12px 14px 13px; border: var(--e-line); border-left-width: 4px; }
.e-kpi-lg .e-kpi-v { font-size: 34px; }
.e-kpi-lg .e-kpi-l { font-size: 10px; }
.e-kpi-lg .e-kpi-s { font-size: 11px; }
.e-kpi-ic { position: absolute; right: 11px; top: 11px; font-size: 19px; color: var(--e-n-300); }
.e-kpi-lg.is-danger .e-kpi-ic { color: #dcb3ae; }
.e-kpi-lg.is-warn .e-kpi-ic { color: #ddc3a0; }
.e-kpi-lg.is-info .e-kpi-ic { color: #a9c6e2; }
.e-kpi-hint { display: flex; align-items: center; gap: 4px; font-size: 10.5px; margin-top: 3px; }
.e-panel-hd .e-kpi-tag { margin-left: auto; font: 600 10.5px var(--e-font-num); color: var(--e-text-4); }
@media (max-width: 768px) {
  .e-kpi-grid, .e-kpi-bd .e-kpi-grid, .e-kpi-grid-lead { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .e-kpi-lg { padding: 10px 12px 11px; }
  .e-kpi-lg .e-kpi-v { font-size: 27px; }
  .e-kpi-ic { display: none; }
}
/* การ์ดใหญ่ที่เป็นเงิน/ชั่วโมง `.e-kpi-lg.e-kpi-money` — แผน 35 · F8 ข้อ 31 (ข้อตัดสิน ⓑ 14 ต.ค. 2569)
   🔴 เดิม `.e-kpi-money .e-kpi-v` (17px · 0,2,0) แพ้ `.e-kpi-lg .e-kpi-v` (0,2,0) ที่มาทีหลัง ⇒ คลาส money ไม่มีผลกับการ์ดใหญ่เลย
      ⇒ ยอด 13 อักขระ (฿9,999,999.99) ล้นกล่องตัวเลข 66px ในการ์ดแคบที่ 1280px · 4px ที่ 390px
   ➡ การ์ดเป็น container · ขนาดตัวเลข = min(เพดานเดิม, ความกว้างกล่องตัวเลข ÷ (13 อักขระ × .46em))
      .46em ต่ออักขระ = ค่ากว้างที่สุดที่วัดได้ของ "฿ ตัวเลข , ." (ค่าเดียวกับการ์ดต้นทุนวัสดุของ F3)
      ⇒ ยอดเงิน 13 อักขระ + ชั่วโมง 15 อักขระอยู่บรรทัดเดียวในกล่อง · การ์ดกว้างยังได้เพดานเดิม 34 / 27px
   ⚠️ 0,3,0 ชนะกฎ 0,2,0 ของธีมโดยไม่พึ่งลำดับ แต่แพ้กฎ 0,3,0 ในบล็อก style ของหน้าที่มาทีหลัง
      (การ์ดต้นทุนวัสดุของ reports/work_summary.php คงสูตรตามความยาวยอดของหน้า)
   ⚠️ เบราว์เซอร์ที่ไม่รู้จัก cqi ไม่เข้าบล็อกนี้ ⇒ คงขนาดเดิม 34 / 27px
   ⚠️ การ์ดเล็ก .e-kpi-money (17px) และการ์ดใหญ่ที่ไม่ใช่เงิน ไม่ถูกแตะ · ตัววัด: _tests/ui/probe_p35_f8.js ข้อ 31 */
@supports (font-size: 1cqi) {
  .e-kpi-lg.e-kpi-money { container-type: inline-size; }
  .e-kpi-lg.e-kpi-money .e-kpi-v { font-size: min(34px, calc(100cqi / (13 * .46))); }
  @media (max-width: 768px) {
    .e-kpi-lg.e-kpi-money .e-kpi-v { font-size: min(27px, calc(100cqi / (13 * .46))); }
  }
}

/* ---------- 15. LIST / MINI STAT / LEGEND -------------------------------
   แถวรายการในแผง (วัสดุใกล้หมด, กิจกรรม, เอกสาร) และตัวเลขสรุปย่อ
   ------------------------------------------------------------------------ */
.e-list { display: flex; flex-direction: column; }
.e-list-row { display: flex; align-items: center; gap: 10px; padding: 9px 14px; border-bottom: var(--e-line-soft); min-height: var(--e-tap); }
.e-list-row:last-child { border-bottom: 0; }
.e-list-row:hover { background: var(--e-a-100); }
.e-list-ic { width: 30px; height: 30px; flex: none; background: var(--e-surface-alt); border: var(--e-line-soft); display: flex; align-items: center; justify-content: center; color: var(--e-text-3); font-size: 14px; }
.e-list-bd { flex: 1; min-width: 0; }
.e-list-t { display: block; font-size: 12.5px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.e-list-s { display: block; font-size: 10.5px; color: var(--e-text-4); }
.e-list-v { flex: none; text-align: right; font: 600 12.5px var(--e-font-head); font-variant-numeric: tabular-nums; }
.e-list-v small { display: block; font: 400 10.5px var(--e-font-body); color: var(--e-text-4); }
.e-mini { display: flex; align-items: baseline; gap: 10px; padding: 7px 0; border-bottom: var(--e-line-soft); }
.e-mini:last-child { border-bottom: 0; }
.e-mini-l { font-size: 12px; color: var(--e-text-2); }
.e-mini-v { margin-left: auto; font: 600 16px var(--e-font-head); font-variant-numeric: tabular-nums; }
.e-mini.is-danger .e-mini-v { color: var(--e-danger); }
.e-mini.is-warn .e-mini-v { color: var(--e-warn); }
.e-legend { display: flex; flex-direction: column; gap: 7px; }
.e-legend-row { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--e-text-2); }
.e-legend-sw { width: 9px; height: 9px; flex: none; }
.e-legend-v { margin-left: auto; font: 600 12.5px var(--e-font-head); color: var(--e-text); font-variant-numeric: tabular-nums; }

/* ==========================================================================
   14c. ALIAS — ตัวแปรเดิมของ app.css ชี้มาที่ token ของธีม
   เขียนใหม่ในเฟส U0 (ไม่มีใน template และไม่มีใน mockup เพราะ mockup ไม่ได้โหลด app.css)
   app.css:5-14 ประกาศ --navy / --primary / --sidebar-* ไว้ใน :root ของตัวเอง
   ⇒ ไฟล์นี้โหลดหลัง app.css เสมอ (กติกา §00 ข้อ 5) การประกาศซ้ำที่นี่จึงชนะ
   ⇒ inline style var(--navy) 57 จุดใน 38 ไฟล์ + app.css อีก 25 จุด เข้าธีมทันทีโดยไม่ต้องแตะไฟล์
   🔴 ห้ามแตะ --sidebar-width: geometry ของ .main-content/.app-footer อ้างค่านี้อยู่ (app.css:30,168,437)
      เปลี่ยนแล้วหน้าเดิมทั้ง 84 หน้าจะเลื่อน — โครง rail ใหม่มาใน U2
   ========================================================================== */
:root {
  --navy:               var(--e-a-900);
  --primary:            var(--e-a);
  --sidebar-bg:         var(--e-a-900);
  --sidebar-text:       var(--e-n-400);
  --sidebar-active-bg:  rgba(255, 255, 255, .12);
  --sidebar-active-text:#ffffff;
  --sidebar-hover-bg:   rgba(255, 255, 255, .06);
  --sidebar-border:     rgba(255, 255, 255, .10);
}

/* ==========================================================================
   14d. BRIDGE เพิ่ม — จุดที่หัวข้อ 14 ของ template ครอบไม่ถึง (§0.3 ข้อ 10 · §3 ของแผน)
   BRIDGE ของ template มี 26 บรรทัด (:616-645) แต่ของจริงยังเหลือ "โค้ง/เงา/navy" ที่:
     .text-primary 67 ไฟล์ · .bg-primary 37 · .section-card 31 · .status-badge 30 ·
     .rounded-circle 37 · .shadow* 32 · .dash-stat-card:hover 15 · .form-check-input 25
   ⚠️ สีของป้ายสถานะ 10 helper (functions.php) เป็น inline style — CSS ทับไม่ได้ ต้องรอ U3
   ⚠️ inline style 1,812 จุดใน 84 หน้า ก็ทับไม่ได้เช่นกัน — ทยอยหายไปตอนแปลง markup U4-U7
   ลบทั้งหัวข้อนี้ได้เมื่อ U9 เท่านั้น
   ========================================================================== */
:root {
  --bs-box-shadow:        none;
  --bs-box-shadow-sm:     none;
  --bs-box-shadow-lg:     none;
  --bs-box-shadow-inset:  none;
  --bs-border-radius-pill: 0;
  --bs-border-radius-xl:  0;
  --bs-border-radius-xxl: 0;
  /* 5 สีสถานะของธีม (กติกา §00 ข้อ 4) แทนพาเลต Tailwind ที่ Bootstrap ใช้อยู่ */
  --bs-danger:     #a3271f; --bs-danger-rgb:    163, 39, 31;
  --bs-warning:    #b5651d; --bs-warning-rgb:   181, 101, 29;
  --bs-success:    #3f6b52; --bs-success-rgb:   63, 107, 82;
  --bs-info:       #416180; --bs-info-rgb:      65, 97, 128;
  --bs-secondary:  #7a7a7d; --bs-secondary-rgb: 122, 122, 125;
}

/* มุมโค้ง/เงาที่เหลือ */
.section-card { border-radius: 0 !important; border: var(--e-line); box-shadow: none !important; }
.status-badge { border-radius: 0 !important; }
.rounded-circle, .rounded-pill, .rounded, .rounded-1, .rounded-2, .rounded-3 { border-radius: 0 !important; }
.shadow, .shadow-sm, .shadow-lg { box-shadow: none !important; }
.dash-stat-card, .dash-stat-card:hover { box-shadow: none !important; border-radius: 0 !important; }
.timeline-dot { border-radius: 0 !important; }
.email-tab, .email-tabbar, .email-tab-phase,
.user-menu, .user-menu-avatar, .user-menu-avatar-initial,
.upload-drop-zone,
.admin-chip, .admin-chip-add, .admin-chip-btn, .admin-chip-count,
.project-stat-tab,
.avatar-sm, .avatar-lg,
.checklist-item, .checklist-check,
.daily-run-badge, .daily-datebar, .daily-est, .daily-snap-head, .daily-pivot-wrap, .daily-timeline-wrap {
  border-radius: 0 !important;
}

/* sidebar เดิม 2 จุดที่หัวข้อ 14 ครอบไม่ถึง — เจอตอน U1 (sidebar กลายเป็นพื้นเข้มแล้วแต่ 2 กฎนี้ยังคิดว่าพื้นขาว)
   app.css:188  .sidebar-logout-link:hover  พื้นชมพูอ่อน #fef2f2 + ตัวแดง = แถบขาววาบบนแถบเข้ม
   app.css:92   .sidebar-new-btn:hover      ถูก !important ของหัวข้อ 14 กลบจนปุ่มไม่ตอบสนองการชี้ */
.sidebar .sidebar-logout-link:hover { background: rgba(163, 39, 31, .22) !important; color: #fff !important; }
.sidebar .sidebar-new-btn:hover { background: var(--e-a-600) !important; }

/* สี navy ที่เหลือ */
.text-primary { color: var(--e-a-700) !important; }   /* --e-a บนพื้นขาวอ่านยาก จึงใช้ a-700 กับ "ตัวหนังสือ" */
.bg-primary   { background-color: var(--e-a) !important; }
.bg-soft-primary   { background-color: var(--e-a-100) !important; }
.bg-soft-secondary { background-color: var(--e-mute-bg) !important; }
.bg-soft-info      { background-color: var(--e-info-bg) !important; }
.bg-soft-success   { background-color: var(--e-ok-bg) !important; }
.bg-soft-warning   { background-color: var(--e-warn-bg) !important; }
.bg-soft-danger    { background-color: var(--e-danger-bg) !important; }

/* ฟอร์มเดิม — วงแหวนโฟกัสต้องมีชั้นเดียว (ธีมมี :focus-visible outline อยู่แล้ว :72) */
.form-control:focus, .form-select:focus, .form-check-input:focus {
  border-color: var(--e-a); box-shadow: none;
}
.form-check-input { border-radius: 0 !important; border-color: var(--e-n-400); }
.form-check-input:checked { background-color: var(--e-a); border-color: var(--e-a); }

/* ── กฎที่ตั้งใจให้ "เริ่มมีผลตอน U2" (เมื่อ shell ใหม่พิมพ์ .e-app ลงหน้า) ──────
   ถ้าเปิดใช้ตั้งแต่ U1 จะพังทันที: .main-content อ้าง margin-left: var(--sidebar-width)
   อยู่ (app.css:30,168) เนื้อหาจะไหลไปอยู่ใต้ sidebar เดิม */
body:has(.e-app) .main-content { margin: 0; padding: 0; }
@media (max-width: 991.98px) {
  /* .app-footer เป็น position:fixed สูง 40px ทุกหน้า (app.css:434-446) — แย่งที่กับแถบล่างใหม่ (§2.4 ช) */
  body:has(.e-app) .app-footer { display: none; }
  /* #toastContainer อยู่มุมขวาล่าง z-index 9999 (app.js:196-198) — ทับปุ่มวงล้อทุกครั้ง
     ยกขึ้นเหนือแถบล่าง · ห้ามดัน z-index ของวงล้อให้เกิน 1050 (จะไปทับ modal) */
  body:has(.e-app) #toastContainer { bottom: calc(var(--e-navh) + 70px) !important; }
}
/* 🔴 แผน 14 · รอบ 14 (§14.9 · ข้อตัดสินผู้ใช้ ท่า ค) — กล่องแจ้งเตือนต้องไม่บังปุ่มที่อยู่ข้างหลัง
   Bootstrap ตั้ง .toast-container ให้คลิกทะลุอยู่แล้ว แต่คืน .toast เป็น pointer-events:auto
   ⇒ กล่องที่โผล่ใต้เคอร์เซอร์ซึ่งค้างบนปุ่มบันทึก (ปุ่มท้ายฟอร์มห่างขอบล่างแค่ 28px) บังปุ่มทั้งปุ่ม
     และ Bootstrap หยุดนับถอยหลังเพราะได้ mouseover ⇒ ค้างจนกว่าเมาส์จะขยับออก (วัดบน production 113 วินาที)
   ⚠️ ปุ่ม × ต้องคืน auto ไม่งั้นปิดกล่องไม่ได้ · เวลาแสดงตามความยาวข้อความที่ต้องคู่กันอยู่ใน app.js showToast() */
#toastContainer .toast { pointer-events: none; }
#toastContainer .toast .btn-close { pointer-events: auto; }
/* แผน 35 · F9 ข้อ 35 — ข้อความของ toast หดได้ + ปุ่ม × ไม่ถูกบีบ "ทุกความกว้าง" (เดิมกฎหดได้อยู่ใน ≤768px เท่านั้น · F8 ข้อ 11)
   ปุ่มกับข้อความเป็นลูกของ .d-flex (showToast ของ app.js) ⇒ ข้อความยาวดันปุ่ม: ไทยยาว ปุ่มเหลือ 9.86×21 · คำยาวไม่มีช่องว่าง ปุ่มหลุดพ้นจอ (7×21 ที่ x≈1480 ในจอ 1280)
   ⚠️ min-width: 0 ให้ข้อความหดแล้วตัดบรรทัดในกล่อง · flex-shrink: 0 ให้ปุ่มคงขนาดตั้งต้น 21×21 (ไม่ใช่การยก — เดสก์ท็อปไม่ยกเป้าแตะ · D170)
   ⚠️ ≤768px ปุ่มได้ 44×44 จากกฎในหัวข้อเป้าแตะของมือถือ (ไม่เปลี่ยน) · ตัววัด: _tests/ui/verify_p35_f9_theme.php (probe_p35_f9.html) */
#toastContainer .toast .toast-body { min-width: 0; }
#toastContainer .toast .btn-close { flex-shrink: 0; }
/* 🔴 แผน 14 · รอบ 14 ต่อ (§21.6 · D329 · ผู้ใช้เลือกทาง 1 16 ก.ย. 2569) — เมนูที่วางแบบ fixed ต้องลอยเหนือแถบท้ายหน้า
   strategy:fixed (data-bs-popper-config ที่ปุ่ม) พาเมนูหลุดจาก .e-table-wrap แล้ว แต่ Bootstrap ให้ .dropdown-menu z-index 1000
   ขณะที่ .app-footer (app.css:434) เป็น fixed z-index 1030 ⇒ แถวท้ายตอนหน้าเลื่อนสุด รายการสุดท้ายจมใต้แถบ
   (วัด production 1920px: บัง 27/44px รวมจุดกลาง · Popper ไม่กลับทิศเพราะรายการยังอยู่ในวิวพอร์ต)
   ⚠️ 1040 = ช่องว่างระหว่าง .app-footer 1030 กับ ฉากมืด .e-scrim 1044 · rail 1045 · ปุ่มวงล้อ .e-air 1046 · .e-sheet 1047 · modal 1050
      ห้ามดันเกิน 1043 ไม่งั้นเมนูที่เปิดค้างจะทับฉากมืด/rail ตอนเปิด drawer บนมือถือ
   ⚠️ จับด้วยรูปทรง (ปุ่มที่ประกาศ strategy fixed + เมนูพี่น้องที่ตามมา) ไม่ผูกชื่อคลาสของหน้า ⇒ ปุ่มแบบเดียวกันในหน้าอื่นได้ผลเอง */
[data-bs-popper-config*='"strategy":"fixed"'] ~ .dropdown-menu { z-index: 1040; }

/* ==========================================================================
   15b. BASE เพิ่ม (§3 ของแผน หัวข้อ "2 BASE")
   ========================================================================== */
/* .e-chip{display:inline-flex} (:340) ชนะ UA stylesheet ของ [hidden] ⇒ ชิปที่ซ่อนด้วย hidden จะโผล่
   เป็นกฎของจริง ไม่ใช่ของ mockup — ยกมาจากบล็อก MOCK-ONLY ของ mockup :731 */
[hidden] { display: none !important; }

/* ── U2.2: 3 อย่างที่ shell ของจริงต้องใช้ แต่ template ไม่มี ────────────────── */
/* flash ของระบบมี 3 ชนิด (success / error / info) — template มีแค่ is-danger กับ is-warn */
.e-callout.is-ok   { border-color: var(--e-ok-bd);   border-left-color: var(--e-ok); }
.e-callout.is-info { border-color: var(--e-info-bd); border-left-color: var(--e-info); }
/* รูปโปรไฟล์จริงใน rail-foot (UPLOAD_URL . $user['avatar']) — template มีแต่ตัวอักษรแรก */
.e-rail-avatar { overflow: hidden; }
.e-rail-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* ปุ่มปิด drawer บนมือถือ (แทน #sidebarClose เดิม) — โผล่เฉพาะตอนที่ rail กลายเป็น drawer
   ห้ามใช้ .d-lg-none ของ Bootstrap: เบรกพอยนต์ของมันคือ 992 แต่ drawer ยุบที่ 768
   ⇒ ช่วง 768-992px จะได้ปุ่มปิดที่ปิดอะไรไม่ได้ */
.e-rail-close { display: none; color: #6d8aa5; }
.e-rail-close:hover { color: #fff; }
@media (max-width: 768px) { .e-rail-close { display: inline-flex; } }

/* ==========================================================================
   16. FORM (เพิ่มใน U0 · ที่มา mockup :771-887 — หน้ากรอกทุกโมดูลใช้ร่วมกัน)
   ใช้ token เดิมทั้งหมด · ไม่มี border-radius / box-shadow · ปุ่ม/แถวที่แตะได้สูง ≥ 44px
   ของจริง: CKEditor 5 แทนที่ textarea ด้วย .ck-editor → ต้องมี selector คู่กัน:
     .ck.ck-editor__main > .ck-editor__editable { border-radius:0; border-color:var(--e-n-300); min-height:250px; font:400 13px var(--e-font-body) }
     .note-editor .ck-editor__editable { min-height:120px }   .ck.ck-toolbar { border-radius:0; background:var(--e-surface-alt) }
     .e-field.is-invalid .ck.ck-editor__main > .ck-editor__editable { outline:2px solid var(--e-danger); outline-offset:-1px }
   ========================================================================== */
/* ช่องกรอกต้องเด่นจาก label (ผู้ใช้ 2 ก.ย. 2569: "input กับ label กลมกลืนกัน"):
   พื้นแผงฟอร์ม = surface-alt (#f7f7f8) · ช่องกรอกขาวขอบเข้มขึ้น (n-400 #b7b7ba) จึงเห็นเป็น "ช่อง" ชัด ·
   label เป็นตัวหนังสือปกติ 12px สีเข้ม (ไม่ใช่ kicker 9.5px ตัวพิมพ์ใหญ่ — ภาษาไทยไม่มีตัวพิมพ์ใหญ่ ทำให้ label กับค่าในช่องดูเหมือนกัน) */
.e-form .e-panel-bd { background: var(--e-surface-alt); }
.e-form .e-field > label, .e-form .e-field > legend { font: 600 12px/1.3 var(--e-font-body); letter-spacing: 0; text-transform: none; color: var(--e-text); padding: 0; margin: 0 0 1px; }
.e-form .e-field > label i, .e-form .e-field > legend i { color: var(--e-text-3); }
.e-form fieldset.e-field { border: 0; padding: 0; margin: 0; min-width: 0; }
.e-form .e-input, .e-form .e-select, .e-form .e-editor, .e-form .e-searchbox { border-color: var(--e-n-400); background: var(--e-surface); }
.e-form .e-input-group-text { border-color: var(--e-n-400); background: var(--e-n-150); }
.e-form .e-input:hover, .e-form .e-select:hover, .e-form .e-editor:hover { border-color: var(--e-a-500); }
.e-form .e-input::placeholder, .e-form textarea::placeholder { color: var(--e-text-4); }
.e-form .e-opt, .e-form .e-check, .e-form .e-eqpick { border-color: var(--e-n-400); }
.e-form .e-eqpick-grid { background: var(--e-surface); border: var(--e-line); }
.e-form .e-help { color: var(--e-text-3); }
.e-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 16px; }
.e-form-grid .e-col-2 { grid-column: 1 / -1; }
.e-form-stack { display: flex; flex-direction: column; gap: 14px; }
.e-req { color: var(--e-danger); margin-left: 3px; }
.e-help { font-size: 11px; color: var(--e-text-4); }
.e-field-err { display: none; font-size: 11.5px; color: var(--e-danger); }
.e-field.is-invalid .e-input, .e-field.is-invalid .e-editor { outline: 2px solid var(--e-danger); outline-offset: -1px; }
.e-field.is-invalid .e-field-err { display: block; }
.e-input-group { display: flex; }
.e-input-group .e-input { flex: 1; min-width: 0; }
.e-input-group-text { display: inline-flex; align-items: center; padding: 0 12px; border: var(--e-line); border-left: 0; background: var(--e-surface-alt); font: 600 12px var(--e-font-head); color: var(--e-text-2); white-space: nowrap; }
.e-split-form { display: grid; grid-template-columns: 5fr 7fr; gap: var(--e-gap); align-items: start; }
.e-panel-hd .e-panel-cnt { margin-left: auto; font: 600 10.5px var(--e-font-num); color: var(--e-text-4); }
.e-back { min-width: var(--e-tap); min-height: var(--e-tap); justify-content: center; }

/* ตัวเลือกแบบกล่อง (ความสำคัญ / สถานะ) — ทั้งกล่องแตะได้ · สถานะเลือกผูกกับ :checked (ทนการคืนค่าฟอร์มของเบราว์เซอร์) */
.e-opt-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); gap: 8px; }
.e-opt { position: relative; display: flex; align-items: center; gap: 8px; min-height: var(--e-tap); padding: 0 12px; border: var(--e-line); background: var(--e-surface); cursor: pointer; margin: 0; }
.e-opt input { position: absolute; opacity: 0; pointer-events: none; width: 0; height: 0; }
.e-opt .e-opt-ck { margin-left: auto; color: var(--e-a); opacity: 0; font-size: 15px; }
.e-opt:hover { border-color: var(--e-a-400); }
.e-opt.is-on, .e-opt:has(input:checked) { border-color: var(--e-a); outline: 1px solid var(--e-a); outline-offset: -2px; background: var(--e-a-100); }
.e-opt.is-on .e-opt-ck, .e-opt:has(input:checked) .e-opt-ck { opacity: 1; }
.e-opt:has(input:focus-visible) { outline: 2px solid var(--e-a); outline-offset: 2px; }

/* กล่องเลือกเครื่องจักร (แทน .equip-grid-card เดิม) */
.e-eqpick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); gap: 10px; max-height: 430px; overflow-y: auto; overscroll-behavior: contain; padding: 2px; }
.e-eqpick { position: relative; display: flex; flex-direction: column; border: var(--e-line); background: var(--e-surface); cursor: pointer; margin: 0; }
.e-eqpick input { position: absolute; opacity: 0; pointer-events: none; width: 0; height: 0; }
.e-eqpick-thumb { height: 74px; background: var(--e-surface-alt); border-bottom: var(--e-line-soft); display: flex; align-items: center; justify-content: center; color: var(--e-n-300); font-size: 26px; position: relative; overflow: hidden; }
.e-eqpick-thumb img { width: 100%; height: 100%; object-fit: cover; }   /* ของจริง: <img src=UPLOAD_URL.primary_image> */
.e-eqpick-thumb .e-eqpick-img { position: absolute; left: 6px; bottom: 6px; font: 600 9.5px var(--e-font-head); letter-spacing: .06em; text-transform: uppercase; color: var(--e-text-3); background: var(--e-surface); border: var(--e-line-soft); padding: 1px 5px; }
.e-eqpick-ck { position: absolute; top: 6px; right: 6px; width: 22px; height: 22px; background: var(--e-a); color: #fff; display: none; align-items: center; justify-content: center; font-size: 13px; }
.e-eqpick-bd { display: block; padding: 7px 9px 8px; min-width: 0; }
.e-eqpick-n { display: block; font: 600 12px/1.3 var(--e-font-body); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.e-eqpick-s { display: block; font-size: 10.5px; color: var(--e-text-4); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 1px; }
.e-eqpick .e-chip { margin-top: 5px; }
.e-eqpick:hover { border-color: var(--e-a-400); }
.e-eqpick.is-on, .e-eqpick:has(input:checked) { border-color: var(--e-a); outline: 1px solid var(--e-a); outline-offset: -2px; background: var(--e-a-100); }
.e-eqpick.is-on .e-eqpick-ck, .e-eqpick:has(input:checked) .e-eqpick-ck { display: flex; }
.e-eqpick.is-hidden { display: none; }
.e-pick-foot { display: flex; align-items: center; gap: 12px; margin-top: 10px; font-size: 12px; color: var(--e-text-2); flex-wrap: wrap; }
.e-pick-foot .e-num { font-size: 15px; }
.e-link-btn { background: none; border: 0; padding: 0 8px; min-height: var(--e-tap); display: inline-flex; align-items: center; color: var(--e-a-700); font: 600 12px var(--e-font-head); cursor: pointer; }
.e-link-btn:hover { color: var(--e-a-800); text-decoration: underline; }

/* รายชื่อติ๊กเลือก (ช่างทีม) — .e-check-row.is-live = แถวที่ครอบ checkbox จริง (ems-field.js ต้องข้ามแถวนี้ ไม่ toggle เอง) */
.e-check-scroll { max-height: 236px; overflow-y: auto; overscroll-behavior: contain; }
.e-check-row.is-live { margin: 0; position: relative; }
.e-check-row.is-live input { position: absolute; opacity: 0; pointer-events: none; width: 0; height: 0; }
.e-check-row.is-live:has(input:checked) .e-check-box { background: var(--e-a); border-color: var(--e-a); color: #fff; }
.e-check-row.is-live:has(input:focus-visible) { outline: 2px solid var(--e-a); outline-offset: -2px; }

/* กล่อง rich text (ภาพจำลองของ CKEditor 5 — ดูหมายเหตุหัวข้อ 16) */
.e-editor { border: var(--e-line); background: var(--e-surface); }
.e-editor-tb { display: flex; flex-wrap: wrap; gap: 2px; padding: 5px 7px; border-bottom: var(--e-line-soft); background: var(--e-surface-alt); }
.e-editor-tb button { height: 28px; min-width: 28px; padding: 0 6px; border: 0; background: transparent; color: var(--e-text-2); font: 600 11.5px var(--e-font-head); cursor: pointer; display: inline-flex; align-items: center; gap: 4px; }
.e-editor-tb button:hover { background: var(--e-a-100); color: var(--e-a-800); }
.e-editor-tb .sep { width: 1px; background: var(--e-n-300); margin: 4px 4px; }
.e-editor textarea { width: 100%; border: 0; min-height: 150px; padding: 10px 12px; font: 400 13px/1.55 var(--e-font-body); color: var(--e-text); resize: vertical; outline: none; display: block; background: transparent; }
.e-editor.is-sm textarea { min-height: 88px; }
.e-editor:focus-within { outline: 2px solid var(--e-a); outline-offset: -1px; }

/* ปุ่มท้ายฟอร์ม (เดสก์ท็อป) — บนมือถือ .e-actionbar.is-bar รับหน้าที่แทน */
.e-form-foot { display: flex; justify-content: flex-end; align-items: center; gap: 8px; padding-top: 14px; border-top: var(--e-line); }
.e-form-foot .e-help { margin-right: auto; }
/* แถบปุ่มจริงติดขอบล่าง (ข้อตัดสิน §1 ข้อ 16) — ใช้กับหน้าฟอร์ม/execute · ems-field.js ต้องข้าม .e-actionbar.is-bar ตอนสร้าง air menu */
@media (max-width: 768px) {
  .e-form-foot { display: none; }
  .e-split-form { grid-template-columns: minmax(0, 1fr); }
  .e-form-grid { grid-template-columns: minmax(0, 1fr); }
  .e-actionbar.is-bar { display: flex; }
  .e-input, .e-select, .e-search, .e-searchbox input, .e-gsearch input, .e-editor textarea { font-size: 16px; }   /* กัน iOS ซูมตอนโฟกัส */
  .e-input, .e-select { height: 44px; }
  .e-eqpick-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; max-height: min(55vh, 430px); }
  .e-eqpick-thumb { height: 56px; font-size: 20px; }
  .e-eqpick-bd { padding: 6px 8px 7px; }
  .e-check-scroll { max-height: none; }
}
@media (max-width: 992px) { .e-split-form { grid-template-columns: minmax(0, 1fr); } }

/* หัวเรื่องบน topbar: ชิปสถานะของใบที่กำลังแก้ */
.e-topbar-s .e-chip { vertical-align: middle; margin-left: 4px; }
/* ป้าย "ใหม่" ในเมนู (รายการที่ UI ใหม่เสนอเพิ่ม — ยังไม่มีหน้ารองรับ) · สีเป็น token ใหม่ที่เสนอเข้า :root (ตรงกับ .e-rail-n.is-warn ของ template) */
:root { --e-rail-warn: #dfae7c; --e-rail-warn-bd: #7a5a3a; }
.e-rail-new { display: none; }
.e-rail.is-wide .e-rail-link .e-rail-new, .e-rail.is-wide .e-rail-item .e-rail-new,
.e-rail:not(.is-wide) .e-rail-grp.is-flyout .e-rail-item .e-rail-new {
  display: inline-block; margin-left: auto; flex: none; font: 600 8.5px/1.6 var(--e-font-head); letter-spacing: .1em;
  text-transform: uppercase; color: var(--e-rail-warn); border: 1px solid var(--e-rail-warn-bd); padding: 0 4px; white-space: nowrap; overflow: visible;
}
.e-rail.is-wide .e-rail-item .e-rail-n + .e-rail-new, .e-rail:not(.is-wide) .e-rail-grp.is-flyout .e-rail-item .e-rail-n + .e-rail-new { margin-left: 8px; }
.e-rail.is-wide .e-rail-link > span:first-of-type { flex: 1; min-width: 0; }
.e-rail-brand-n, .e-rail-brand-s { overflow: hidden; text-overflow: ellipsis; }

/* ==========================================================================
   17. DASHBOARD (เพิ่มใน U0 · ที่มา mockup :888-967 · ใช้ใน U4)
   ========================================================================== */
/* ตัดออกจาก mockup :891-894, :896-897 = .e-gate (ป้ายด่านสิทธิ์สำหรับรีวิว) — ของจริงคือ hasPermission() รอบ section */
.e-panel-hd a.e-more { margin-left: auto; font: 600 11.5px var(--e-font-head); white-space: nowrap; min-height: 32px; display: inline-flex; align-items: center; }

/* การ์ดตัวเลขที่มี "ชิปลิงก์" อยู่ข้างใน — ลิงก์หลักยืดคลุมทั้งใบ (stretched-link) ชิปเป็นลิงก์ที่สองซ้อนบน (คอมเมนต์เดิม :1877-1880) */
.e-kpi.has-chips { display: flex; flex-direction: column; }
.e-kpi .e-kpi-link { color: inherit; text-decoration: none; display: flex; flex-direction: column; gap: 1px; }
.e-kpi .e-kpi-link::after { content: ''; position: absolute; inset: 0; }
.e-kpi .e-kpi-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; position: relative; z-index: 1; min-height: 28px; align-items: center; }
.e-kpi .e-kpi-chips .e-chip { height: auto; min-height: 28px; font-size: 10.5px; padding: 0 9px; }
.e-kpi .e-kpi-chips a.e-chip:hover { outline: 1px solid currentColor; }
.e-kpi.is-flag-danger { border-left-color: var(--e-danger); }   /* ขอบแดง (ธงในถัง) โดยไม่เปลี่ยนสีตัวเลข · .is-flag-warn ใช้เมื่อ due_soon > 0 */
.e-kpi.is-flag-warn { border-left-color: var(--e-warn); }
.e-kpi-s .e-b-danger { color: var(--e-danger); font-weight: 600; }
.e-kpi-s .e-b-info { color: var(--e-info); font-weight: 600; }
.e-kpi-s .e-b-warn { color: var(--e-warn); font-weight: 600; }
.e-kpi-s .e-b-ok { color: var(--e-ok); font-weight: 600; }
.e-kpi-s .e-b-mute { color: var(--e-text-3); font-weight: 600; }
.e-kpi-lg .e-kpi-s { line-height: 1.5; }

/* แถบชั่วโมงซ้อน 4 มิติ (ใบงาน / PM / CM / ผู้รับเหมา) — .e-meter เดิมมี 1 span จึงต้องมีตัวใหม่ */
.e-meter-stack { display: flex; height: 8px; background: var(--e-n-200); overflow: hidden; }
.e-meter-stack > span { display: block; height: 100%; }
.e-meter-stack .is-wo { background: var(--e-a); }
.e-meter-stack .is-pm { background: var(--e-ok); }
.e-meter-stack .is-cm { background: var(--e-warn); }
.e-meter-stack .is-ss { background: var(--e-a-700); }
.e-legend-inline { display: flex; flex-wrap: wrap; gap: 10px 14px; font-size: 11px; color: var(--e-text-2); }
.e-legend-inline > span { display: inline-flex; align-items: center; gap: 5px; }
.e-rank-row { display: grid; grid-template-columns: 26px 30px minmax(0, 1fr) 72px; align-items: center; gap: 10px; padding: 8px 14px; border-bottom: var(--e-line-soft); min-height: var(--e-tap); }
.e-rank-row:last-child { border-bottom: 0; }
.e-rank-n { font: 600 13px var(--e-font-head); color: var(--e-text-4); text-align: center; }
.e-rank-row.is-top .e-rank-n { color: var(--e-warn); }
.e-rank-av { width: 30px; height: 30px; background: var(--e-a-900); color: #fff; display: flex; align-items: center; justify-content: center; font: 600 12px var(--e-font-head); }
.e-rank-bd { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.e-rank-bd a { font-size: 12.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--e-text); }
.e-rank-v { text-align: right; font: 600 12.5px var(--e-font-head); font-variant-numeric: tabular-nums; white-space: nowrap; }
.e-strip { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); border-bottom: var(--e-line-soft); }
.e-strip > div { padding: 9px 12px; border-right: var(--e-line-soft); }
.e-strip > div:last-child { border-right: 0; }
.e-strip .e-kicker { display: block; margin-bottom: 2px; }
.e-strip .e-num { font-size: 17px; white-space: nowrap; }

/* กราฟแท่ง SVG เส้นบาง (แทน Chart.js) */
.e-chart { width: 100%; height: auto; display: block; font-family: var(--e-font-num); }
.e-chart .ax { stroke: var(--e-n-300); stroke-width: 1; }
.e-chart .grid { stroke: var(--e-n-200); stroke-width: 1; }
.e-chart .lbl { font-size: 10px; fill: var(--e-text-3); }
.e-chart .val { font-size: 10.5px; font-weight: 600; fill: var(--e-text); }
.e-chart .bar { fill: var(--e-a); }
.e-chart .bar.is-zero { fill: var(--e-n-300); }
.e-chart-wrap { padding: 10px 14px 6px; }

/* แถวรายการงานทำร่วมกัน */
.e-collab-row { display: grid; grid-template-columns: 58px minmax(0, 1fr) 96px; gap: 10px; align-items: start; padding: 9px 14px; border-bottom: var(--e-line-soft); }
.e-collab-row:last-child { border-bottom: 0; }
.e-collab-row .e-list-s { white-space: normal; }
.e-collab-hr { text-align: right; font: 600 12.5px var(--e-font-head); font-variant-numeric: tabular-nums; }
.e-collab-tm { display: block; font-size: 10.5px; color: var(--e-text-4); text-align: right; }

/* การ์ดรายงานเมื่อวาน */
.e-daily { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 12px 14px; }
.e-daily-ic { width: 40px; height: 40px; flex: none; background: var(--e-surface-alt); border: var(--e-line); display: flex; align-items: center; justify-content: center; font-size: 18px; color: var(--e-text-2); }
.e-daily-bd { flex: 1; min-width: 200px; }
.e-daily-t { font: 600 14px var(--e-font-head); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.e-daily-s { font-size: 11.5px; color: var(--e-text-3); margin-top: 3px; }
.e-daily-acts { display: flex; gap: 8px; flex-wrap: wrap; }
.e-daily-acts .e-btn { min-height: var(--e-tap); }

/* ท้ายหน้า (ของเดิม .app-footer) */
.e-foot { display: flex; justify-content: space-between; gap: 12px; padding: 12px var(--e-gutter); border-top: var(--e-line); font-size: 11px; color: var(--e-text-4); }
@media (max-width: 768px) { .e-foot { display: none; } }

/* ==========================================================================
   18. PM (เพิ่มใน U0 · ที่มา mockup :968-1073 · ใช้ใน U6 — preventive_maintenance/index.php + execute.php + pm_checklist_card.php)
   ========================================================================== */
/* ตัดออกจาก mockup :971 = .e-view (ตัวสลับมุมมองบนแถบพรีวิวของ artifact) */
.e-split-exec { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: var(--e-gap); align-items: start; }
@media (max-width: 992px) { .e-split-exec { grid-template-columns: minmax(0, 1fr); } }
/* การ์ดแผน PM: แถวเครื่องจักร + แถบชั่วโมง */
.e-card .e-equip + .e-equip { margin-top: -4px; }
.e-equip-more { font: 600 11px var(--e-font-head); color: var(--e-text-3); margin-left: auto; white-space: nowrap; }
.e-card-hours { display: flex; align-items: center; gap: 8px; font-size: 11.5px; color: var(--e-text-2); margin-top: 8px; }
.e-card-hours .e-meter { flex: 1; }
.e-card-hours b { color: var(--e-text); font-family: var(--e-font-head); }
.e-card-hours.is-over b { color: var(--e-danger); }
/* เช็คลิสต์ตามงานประจำเครื่อง (pm_checklist_card) — แถวครอบ radio จริง .pmchk-res + ช่องหมายเหตุ .pmchk-note */
.e-chk-grp { border: var(--e-line); background: var(--e-surface); }
.e-chk-grp + .e-chk-grp { margin-top: 12px; }
.e-chk-hd { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--e-surface-alt); border-bottom: var(--e-line); font: 600 13px var(--e-font-head); }
.e-chk-hd .e-code { font-weight: 500; }
.e-chk-row { padding: 10px 12px 12px; border-bottom: var(--e-line-soft); }
.e-chk-row:last-child { border-bottom: 0; }
.e-chk-top { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 8px; }
.e-chk-t { font-size: 13.5px; font-weight: 600; }
.e-chk-s { font-size: 11px; color: var(--e-text-4); margin-top: 2px; }
.e-chk-s.is-warn { color: var(--e-warn); }
.e-chk-ctl { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; align-items: center; }
@media (max-width: 992px) { .e-chk-ctl { grid-template-columns: minmax(0, 1fr); } }
/* กลุ่มผลตรวจ 4 ค่า — .e-seg ที่ครอบ radio จริง (label + input) สี pass/fail/na ตาม pmTaskResultBadge */
.e-res { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); border: var(--e-line); }
.e-res label { position: relative; display: flex; align-items: center; justify-content: center; gap: 5px; min-height: var(--e-tap); padding: 0 6px; margin: 0; cursor: pointer; font: 600 12px var(--e-font-head); color: var(--e-text-2); background: var(--e-surface); border-right: var(--e-line); white-space: nowrap; }
.e-res label:last-child { border-right: 0; }
.e-res input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.e-res label:hover { background: var(--e-a-100); }
.e-res label:has(input:checked), .e-res label.is-on { background: var(--e-a-800); color: #fff; }
.e-res label.is-ok:has(input:checked), .e-res label.is-ok.is-on { background: var(--e-ok); }
.e-res label.is-danger:has(input:checked), .e-res label.is-danger.is-on { background: var(--e-danger); }
.e-res label.is-mute:has(input:checked), .e-res label.is-mute.is-on { background: var(--e-mute); }
.e-res label:has(input:focus-visible) { outline: 2px solid var(--e-a); outline-offset: -2px; z-index: 1; }
@media (max-width: 420px) { .e-res label { font-size: 11px; padding: 0 4px; } .e-res label i { display: none; } }
.e-input.is-invalid { outline: 2px solid var(--e-danger); outline-offset: -1px; }
/* กล่องชั่วโมงที่คำนวณได้ / จับเวลา — ใช้ .e-timer ของ template */
.e-timer.is-calc .e-timer-v { font-size: 24px; }
.e-timer .e-btn { flex: none; }
/* ตารางแผน PM (มุมมองตาราง) — แถวเกินกำหนด: แถบซ้ายแดง (แทน table-danger ระบายทั้งแถว) */
.e-table tr.is-overdue > td:first-child { border-left: 4px solid var(--e-danger); }
.e-table tr.is-overdue > td { background: var(--e-danger-bg); }
.e-table td small { display: block; font-size: 10.5px; color: var(--e-text-4); }
.e-table td .e-btn-ghost { min-width: 38px; padding: 0 8px; }
.e-t-eq { display: inline-flex; align-items: center; gap: 4px; }
.e-t-eq img { width: 14px; height: 14px; object-fit: cover; }
.e-t-tech { display: flex; align-items: center; gap: 6px; }
.e-t-av { width: 20px; height: 20px; flex: none; background: var(--e-a-900); color: #fff; display: inline-flex; align-items: center; justify-content: center; font: 600 10px var(--e-font-head); }
/* เซลล์ช่างในตาราง — บรรทัดที่ 1 "รูป + ชื่อหัวหน้าช่าง" · บรรทัดที่ 2 "ไอคอนคน + จำนวนทั้งหมด"
   (ผู้ใช้ขอ 10 ก.ย. 2569 · รอบที่ 2 หลังทดลองแบบ "รูปอย่างเดียว" แล้วอ่านไม่ออกบนจอจริง)
   พ่นจาก emsTechCell() ใน includes/functions.php ที่เดียว — ห้ามพิมพ์มาร์กอัปเองในหน้า
   🔴 **บทเรียนของรอบก่อน**: เคยยุบเหลือรูปล้วน 24px เรียงกันโดยเอาชื่อไปไว้ใน `title`
      ⇒ วัดได้ว่า "เป็นระเบียบ" ทุกตัวเลข (แถวสูงเท่ากัน 24px · คอลัมน์แคบลงจาก 200 เหลือ 164px)
      แต่ **ผู้ใช้เปิดจอจริงแล้วแยกไม่ออกว่าใครเป็นใคร** ⇒ ตัวเลขที่ดีขึ้นทุกตัวไม่ได้แปลว่าใช้งานได้
      · ข้อมูลที่ต้อง hover ถึงจะเห็น = ข้อมูลที่ไม่มีในตาราง
   🔴 โครงใช้คลาสที่ธีมมีอยู่แล้วทั้งหมด (.e-td-stack · .e-row-wrap · .e-ellipsis · td small)
      ที่เพิ่มตรงนี้มีแค่ 2 อย่าง: ขยายรูปเป็น 24px (20px ของธีมเล็กเกินไปสำหรับจอจริง)
      กับจัดไอคอนให้ตรงกลางกับตัวเลข
   ⚠️ ตารางทั้ง 3 หน้าถูกบังคับซ่อนที่ ≤768px (สลับเป็นการ์ด) ⇒ กฎชุดนี้เป็นของเดสก์ท็อปล้วน
      เกณฑ์เป้าแตะ 44px ของ D170 ไม่บังคับ และของในเซลล์นี้เป็น span ไม่ใช่ลิงก์ */
.e-tech-cell .e-t-av { width: 24px; height: 24px; font-size: 11px; }
/* 🔴 ต้องทับ `.e-table td small { display: block }` ของธีม (อยู่เหนือขึ้นไปในไฟล์เดียวกัน)
      เพราะ block ทำให้ไอคอนกับตัวเลขอิงเส้นฐานคนละที่แล้วดูเหลื่อมกัน
      น้ำหนักตัวเลือก: ของธีม `.e-table td small` = (0,1,2) · ของบรรทัดนี้ = (0,2,2)
      ⇒ **ชนะด้วยน้ำหนัก ไม่ใช่ด้วยลำดับ** จึงย้ายตำแหน่งได้โดยไม่พัง (ต่างจาก D125 ที่ชนะด้วยลำดับ)
      ⚠️ ถ้าวันหนึ่งมีคนลด `.e-tech-cell` ออกจากตัวเลือกนี้ น้ำหนักจะตกไปเท่าธีมทันที
         แล้วผู้ชนะจะกลับไปขึ้นกับลำดับในไฟล์เงียบ ๆ */
.e-table td .e-tech-cell small { display: inline-flex; align-items: center; gap: 4px; }
/* ฟิลเตอร์บาร์แบบ 1c (แทน includes/filter_bar.php) */
.e-filter { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 12px; }
.e-filter .e-select { min-width: 150px; height: 38px; }
.e-filter .e-input[type="date"] { width: 150px; }
.e-filter .e-lbl { font: 600 11px var(--e-font-head); color: var(--e-text-3); }
@media (max-width: 768px) { .e-filter .e-select, .e-filter .e-searchbox, .e-filter .e-input[type="date"] { flex: 1 1 46%; max-width: none; min-width: 0; width: auto; } }
.e-pills { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 16px; }
@media (max-width: 768px) { .e-pills { overflow-x: auto; flex-wrap: nowrap; padding-bottom: 4px; margin-left: calc(-1 * var(--e-gutter)); margin-right: calc(-1 * var(--e-gutter)); padding-left: var(--e-gutter); padding-right: var(--e-gutter); } .e-pills .e-pill { flex: none; min-height: var(--e-tap); } }
/* หน้าฟอร์ม/execute บนมือถือ: แถบปุ่มจริงแทน bottom nav */
@media (max-width: 768px) { body.e-has-bar .e-bottomnav { display: none; } }
/* รูปภาพประกอบ */
.e-photo-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; margin-top: 8px; }
.e-photo-grid > div { aspect-ratio: 1; background: var(--e-surface-alt); border: var(--e-line-soft); display: flex; align-items: center; justify-content: center; color: var(--e-n-300); font-size: 22px; }

/* dashboard: ตาราง/รายการ/แผงย่อย */
.e-panel .e-table-wrap { border: 0; }
.e-panel-bd.is-tight { padding: 10px 14px; }
.e-empty { padding: 14px 16px; font-size: 12px; color: var(--e-text-3); }
.e-table td.e-empty { height: auto; padding: 14px 16px; }
.e-table .e-t-title { max-width: 280px; }
.e-td-stack { display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }
.e-bar-row { display: grid; grid-template-columns: minmax(0, 1fr) 120px 56px; align-items: center; gap: 10px; padding: 7px 14px; border-bottom: var(--e-line-soft); font-size: 12.5px; }
.e-bar-row:last-child { border-bottom: 0; }
.e-bar-row.e-bar-hd { padding-top: 8px; padding-bottom: 4px; border-bottom: 0; }
.e-bar-row .e-meter { height: 8px; }
.e-bar-row .e-t-num { text-align: right; font: 600 12px var(--e-font-num); }
.e-panel-foot { border-top: var(--e-line-soft); padding: 9px 14px; font-size: 12px; display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.e-panel-foot a { font-weight: 600; min-height: 32px; display: inline-flex; align-items: center; }
.e-sec-hd .e-chip { align-self: center; }
.e-sec-hd a.e-spacer { min-height: 32px; display: inline-flex; align-items: center; }
.e-list-row > a.e-icon-btn { min-width: var(--e-tap); min-height: var(--e-tap); align-self: stretch; }

@media (max-width: 768px) {
  .e-table .e-t-title { max-width: 150px; }
  .e-table tbody td { padding: 6px 8px; height: auto; }
  .e-list-t { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .e-list-row .e-list-v { max-width: 84px; white-space: normal; font-size: 11px; line-height: 1.3; }
  .e-collab-row { grid-template-columns: 58px minmax(0, 1fr); }
  .e-collab-row > span:last-child { grid-column: 2; display: flex; gap: 8px; align-items: baseline; }
  .e-collab-hr, .e-collab-tm { text-align: left; }
  .e-bar-row { grid-template-columns: minmax(0, 1fr) 80px 52px; gap: 8px; padding: 7px 12px; }
  .e-bar-row.e-bar-hd { display: none; }
  .e-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .e-strip > div:nth-child(3n) { border-right: 0; }
  .e-strip > div:nth-child(-n+3) { border-bottom: var(--e-line-soft); }
  .e-strip .e-num { font-size: 15px; }
  .e-strip > div { padding: 8px 10px; }
  .e-panel-hd { flex-wrap: wrap; }
  .e-panel-hd .e-seg { margin-left: 0 !important; flex-basis: 100%; }
  .e-panel-hd .e-seg > * { flex: 1; justify-content: center; min-height: var(--e-tap); white-space: nowrap; }
  .e-daily-acts { width: 100%; }
  .e-daily-acts .e-btn { flex: 1; }
  .e-link-btn { padding: 0 10px; }
}

/* ── 18b. PM — แก้ตามผลตรวจ 3 มุม (2 ก.ย. 2569) ─────────────────────────── */
/* .e-equip: template ให้รหัส/ที่ตั้งเป็น inline จึงชนกันในบรรทัดเดียว → block (ส่งกลับ template ใน U0) */
.e-equip-bd { min-width: 0; flex: 1; }
.e-equip-code, .e-equip-loc { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.e-equip-code { white-space: normal; }
/* ตัวกรอง: label + ช่องวันที่ เป็นกลุ่มเดียว (บนมือถือ label เคยหลุดไปคนละแถวกับช่อง) */
.e-filter-date { display: inline-flex; align-items: center; gap: 6px; }
.e-filter-date .e-lbl { margin: 0; }
/* ตาราง: avatar จริงใน .e-t-av (index.php:298-307 พิมพ์ <img> เมื่อ tu.avatar ไม่ว่าง) · วันแถวเกินกำหนด */
.e-t-av img { width: 100%; height: 100%; object-fit: cover; display: block; }
.e-table tr.is-overdue .e-t-date { color: var(--e-danger); font-weight: 600; }
/* การ์ดที่ไม่ผูกเครื่อง · กล่องชั่วโมงที่คำนวณได้ · ปุ่มบล็อกสูง 44 */
.e-card-noeq { margin-bottom: 10px; }
.e-calc-box { display: flex; align-items: center; gap: 12px; }
.e-calc-box > i { font-size: 22px; color: var(--e-a-700); }
.e-calc-box .e-num { font-size: 18px; }
.e-btn-tap { min-height: var(--e-tap); }
/* เช็คลิสต์โหมดอ่านอย่างเดียว (pm_checklist_card.php:113,135-144) */
.e-chk-ro { display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: center; font-size: 12px; color: var(--e-text-3); }
/* air menu (สร้างจาก .e-actionbar ของหน้ารายการ) ต้องไม่ลอยทับหน้าที่มีแถบล่างของตัวเอง */
body.e-has-bar [data-e-air] { display: none !important; }
@media (max-width: 768px) {
  .e-topbar .e-topbar-s .e-code { color: inherit; }                       /* .e-code สี a-700 บนพื้นเข้มอ่านไม่ออก */
  .e-filter .e-select, .e-filter .e-searchbox, .e-filter .e-input { height: var(--e-tap); }
  .e-content .e-btn, .e-content .e-seg > * { min-height: var(--e-tap); }  /* ช่างใส่ถุงมือ (MIGRATION §0 ข้อ 6) */
  /* ปุ่ม × ที่หัวโมดัลของ Bootstrap วัดได้ 29×29px ที่ 390px — ต่ำกว่าเกณฑ์เป้าแตะของจอสัมผัส (แผน 34 · N2)
     ย้ายมาจากบล็อก style ของ includes/admin_ui.php รูปเดิมทุกตัวอักษร ⇒ ครอบโมดัลทั้งระบบ ไม่ใช่เฉพาะโมดูล admin
     ⚠️ อยู่ใน media query นี้โดยตั้งใจ (D170) — บนเดสก์ท็อปปุ่มต้องคง 29×29px
     ⚠️ ปุ่มนี้เป็น content-box ⇒ ขนาดจริงคือ 44 + padding ของ Bootstrap ข้างละ 8 = 60×60px
        (margin ติดลบของ Bootstrap ทำให้กินที่ในหัวโมดัล 44px ⇒ หัวโมดัลส่วนใหญ่สูงขึ้นราว 20px ที่ ≤768px)
     ⚠️ ผูกกับหัวโมดัลเท่านั้น — ปุ่ม × นอกโมดัลมีกฎของตัวเองถัดไป (แผน 35 · F8) · ตัววัด: _tests/ui/probe_mclose.js */
  .modal .modal-header .btn-close { min-width: var(--e-tap); min-height: var(--e-tap); }
  /* ปุ่ม × นอกโมดัล 3 จุด — แผน 35 · F8 ข้อ 11 (ข้อตัดสิน ต6): กล่องแจ้งมุมจอ (showToast ของ app.js) · กล่องคำตอบของ SPA
     (showReplyBox ของ ems-nav.js) · แผงเลื่อนของความคิดเห็น (includes/comments_card.php) — ก่อนแก้ 21×21 · 19.5×19.5 · 29×29 ที่ 390px
     และปุ่มของ toast ถูก flex บีบเหลือ 7–10px เมื่อข้อความยาว (min-width ที่ตั้งตรงนี้คือสิ่งที่กันการบีบ)
     ⚠️ border-box = 44×44 พอดี (ปุ่มของโมดัลได้ 44 + padding = 60) · ความสูงของกล่องที่ 390px วัดจริง (mclose_run --diff · F8 รอบ 2):
        หัวแผงเลื่อน 57 → 61px (+4 · margin ติดลบ 8px ของ Bootstrap รับส่วนเกินไว้เกือบหมด — ยอมรับ: น้อยกว่าหัวโมดัลที่สูงขึ้น ~20px ตั้งแต่ N2)
        · กล่อง toast ข้อความ 1–3 บรรทัดสูงเท่าเดิม · กล่องคำตอบ 46 → 64px (min-height ข้างล่าง) · ที่ 1280px เท่าเดิมทุกกล่อง
     ⚠️ ข้อความของ toast ต้องหดได้ (min-width: 0) — คำยาวไม่มีช่องว่างดันปุ่มออกนอกกล่องจนพ้นจอ ⇒ หลังแก้ตัดบรรทัดในกล่องแทน (115 → 158px)
        🆕 แผน 35 · F9 ข้อ 35: กฎนั้นย้ายไปใช้ทุกความกว้างแล้ว (คู่กับปุ่มที่ไม่ถูกบีบ) — อยู่ถัดกฎ pointer-events ของ #toastContainer
     ⚠️ กล่องคำตอบวางปุ่มแบบ absolute (top/right 10px ใน style ของปุ่ม) ⇒ กล่องต้องเว้นขวาและสูงพอ ไม่งั้นปุ่มทับข้อความ/ยื่นพ้นกล่อง
     ⚠️ กล่องข้อความหลังบันทึก (flash) ไม่อยู่ในขอบเขต · เดสก์ท็อปไม่ถูกยก (D170) · ตัววัด: probe_mclose.js + mutate_mclose.php (R2 T3 R4 R5) */
  #toastContainer .toast .btn-close,
  [data-e-spa-reply] > .btn-close,
  .offcanvas-header .btn-close { box-sizing: border-box; min-width: var(--e-tap); min-height: var(--e-tap); }
  [data-e-spa-reply] { padding-right: calc(var(--e-tap) + 18px); min-height: calc(var(--e-tap) + 20px); }
  .e-topbar .e-icon-btn { min-width: 40px; min-height: 40px; }
  .e-filter-date { flex: 1 1 46%; }
  [data-pm-listview] { display: none; }                                   /* มือถือใช้การ์ดอย่างเดียว (ครบกว่าตาราง) */
  .e-kpi-grid-lead > :nth-child(5):last-child { grid-column: 1 / -1; }   /* ใบที่ 5 ไม่โดดเดี่ยว */
}


/* ══ 19. เมนูล่างตามหมวด + วงล้อคำสั่ง (S-Pen Air Command) — แผน 09 · §2.4 ══════════
   ที่มา: บล็อก <style> ของ mockup :1105-1213 (ตัด :1214-1216 ที่เป็นของแถบพรีวิว)
   · แถบล่าง = ปุ่มหมวด 1 ช่อง + เมนูของหมวดนั้นสูงสุด 4 ช่อง (390px ⇒ ช่องละ 78px)
   · วงล้อ = อัปเกรดของ .e-air เดิม (ข้อตัดสิน §1 ข้อ 26) ห้ามมีปุ่มลอย 2 ตัว
   · ความสูงแถบล่างเป็น token เดียว --e-navh ที่ทั้งวงล้อ · .e-content · .e-bottomnav อ้างอิง
     (template ผูก bottom:66px กับ height:52px เป็นเลขตายตัว พอแถบเปลี่ยนตามหมวดจะเพี้ยน)
   ── ต่างจาก mockup 2 อย่าง (ข้อตัดสิน §1 ข้อ 28 — ผู้ใช้ยังไม่ตอบ ทำตามค่าแนะนำ "ก") ──
   1) เบรกพอยนต์ของแถบล่าง/วงล้อ 768px → **991.98px** ให้ตรงกับที่ระบบเดิมยุบ sidebar
      (app.css:462) เพราะช่วง 768-992px คือแท็บเล็ต/มือถือแนวนอนที่หน้างานใช้จริง
   2) บล็อกนี้ยกกฎ 4 บรรทัดที่ template เปิด .e-bottomnav/.e-air ไว้ที่ 768px
      (template :587, :590, :592, :593) มารวมไว้ที่เดียว
   ⚠️ ที่ยังเป็น 768px โดยตั้งใจ: drawer/rail ของ template · การยุบแถบตัวกรอง (19b) ·
      ช่องกรอก 16px กัน iOS ซูม · .e-topbar .e-btn ที่ซ่อนตัวเอง (template :572)
      ⇒ ช่วง 768-992px จะเห็นทั้งปุ่มบน topbar และวงล้อ (คำสั่งซ้ำกัน แต่ไม่มีอะไรหาย)
      🔴 U2 ต้องยุบเบรกพอยนต์ที่เหลือให้เป็นค่าเดียวกันทั้ง CSS และ JS
   ══════════════════════════════════════════════════════════════════════════════ */
:root { --e-navh: 54px; }

@media (max-width: 991.98px) {
  /* ── ย้ายมาจาก template :587, :590, :592, :593 (เดิมอยู่ใน @media 768px) ────── */
  .e-air { display: block; position: fixed; right: 14px; z-index: 1046; }
  body.e-drawer-open .e-air, body.e-gsearch-open .e-air { display: none; }
  /* แผน 35 · F9 ข้อ 36 (ข้อตัดสิน §27.1 ④) — แผงเลื่อน (Bootstrap Offcanvas · วันนี้มีตัวเดียว = แผงความคิดเห็นของ includes/comments_card.php
     ที่ตั้ง z-index 1046 เท่าปุ่มวงล้อ) ⇒ ปุ่มวงล้ออยู่หลังใน DOM จึงลอยทับมุมขวาล่างของแผงที่ ≤768px
     ⚠️ ต้องครอบ .showing ด้วย — วัดจริง 15 ต.ค. 2569 ที่ 390: ช่วงแผงเลื่อนเข้า ปุ่มทับแผง 7 จาก 7 ตัวอย่าง (Bootstrap ใส่ .show หลัง transition จบ)
        ช่วงปิด Bootstrap ยังคง .show ไว้จนแผงออกพ้น ⇒ ไม่ต้องครอบ .hiding
     ⚠️ ไม่กระทบโมดัล / แผ่นล่าง / rail (ตัวเลือกผูกกับ .offcanvas เท่านั้น) · ตัววัด: _tests/ui/verify_p35_f9_theme.php */
  body:has(.offcanvas.show) .e-air, body:has(.offcanvas.showing) .e-air { display: none; }
  body.e-air-open .e-scrim { display: block; }

  /* ── ปุ่มหลักของหน้าฟอร์ม/execute (ข้อตัดสิน §1 ข้อ 16 + ข้อ 23) ───────────────
     หัวข้อ 16 และ 18 มีกฎชุดเดียวกันที่ 768px อยู่แล้ว บล็อกนี้ขยายให้ครอบแท็บเล็ตด้วย
     ไม่งั้นช่วง 768-992px จะได้ทั้งปุ่ม "บันทึก" ท้ายฟอร์มและแถบล่างพร้อมกัน */
  .e-form-foot { display: none; }
  .e-actionbar.is-bar { display: flex; }
  body.e-has-bar .e-bottomnav { display: none; }

  /* ── แถบล่าง ─────────────────────────────────────────────────────────────── */
  .e-bottomnav {
    display: flex; position: sticky; bottom: 0;
    height: calc(var(--e-navh) + env(safe-area-inset-bottom, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);   /* iPhone: ทั้งโปรเจกต์ยังไม่มี safe-area สักที่ */
    gap: 0;
  }
  .e-bottomnav > * {
    flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 2px; padding: 0 2px; background: none; border: 0; color: var(--e-text-4); text-decoration: none; cursor: pointer;
  }
  .e-bn-ic { position: relative; display: block; line-height: 1; }
  .e-bottomnav .e-bn-ic i { font-size: 17px; }
  .e-bn-l { font: 500 9px var(--e-font-head); line-height: 1.1; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .e-bottomnav a.active, .e-bottomnav .active .e-bn-l { color: var(--e-a); }
  .e-bottomnav a.active .e-bn-l { font-weight: 600; }
  .e-bn-item.is-fill { opacity: .72; }                 /* ทางลัดประจำที่เติมเพราะหมวดนี้มีเมนูน้อย */
  .e-bn-grp { color: var(--e-a-800); background: var(--e-a-100); position: relative; }
  .e-bn-grp .e-bn-l { font-weight: 600; }
  .e-bn-caret { position: absolute; top: 3px; right: 6px; font-size: 8px; opacity: .55; }
  .e-bn-n {
    position: absolute; top: -5px; left: 11px; min-width: 15px; height: 15px; padding: 0 3px;
    display: inline-flex; align-items: center; justify-content: center;
    font: 600 9px var(--e-font-head); background: var(--e-a-800); color: #fff;
  }
  .e-bn-n.is-danger { background: var(--e-danger); } .e-bn-n.is-warn { background: var(--e-warn); }
  .e-bn-dot { position: absolute; top: -2px; right: -4px; width: 6px; height: 6px; background: var(--e-ok); }
  .e-content { padding-bottom: calc(var(--e-navh) + 24px + env(safe-area-inset-bottom, 0px)); }
}

/* แผ่นเลื่อนขึ้น — สลับหมวด · เมนูที่เหลือ · คำสั่งที่ล้นวงล้อ */
.e-sheet { position: fixed; inset: 0; z-index: 1047; background: rgba(29, 45, 61, .38); display: flex; align-items: flex-end; }
.e-sheet-bd {
  width: 100%; max-height: 76vh; overflow: auto; background: var(--e-bg); border-top: var(--e-line);
  padding: 0 0 calc(12px + env(safe-area-inset-bottom, 0px));
}
.e-sheet-hd { position: sticky; top: 0; z-index: 1; display: flex; align-items: center; gap: 8px; padding: 10px 12px; background: var(--e-surface); border-bottom: var(--e-line); }
.e-sheet-hd-t { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; font: 600 14px var(--e-font-head); color: var(--e-text); }
.e-sheet-sec { padding: 12px 12px 5px; font: 600 9.5px var(--e-font-head); letter-spacing: .08em; text-transform: uppercase; color: var(--e-text-4); }
.e-sheet-list { display: flex; flex-direction: column; background: var(--e-surface); border-top: var(--e-line-soft); border-bottom: var(--e-line-soft); }
.e-sheet-row {
  display: flex; align-items: center; gap: 10px; min-height: var(--e-tap); padding: 0 12px; width: 100%;
  background: none; border: 0; border-bottom: var(--e-line-soft); text-align: left; cursor: pointer;
  font: 500 13px var(--e-font-body); color: var(--e-text); text-decoration: none;
}
.e-sheet-row:last-child { border-bottom: 0; }
.e-sheet-row > span:first-of-type { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.e-sheet-row i { font-size: 15px; color: var(--e-text-3); flex: none; }
.e-sheet-row.active { background: var(--e-a-100); color: var(--e-a-900); }
.e-sheet-row.active i { color: var(--e-a); }
.e-sheet-row .e-bn-n { position: static; }
.e-sheet-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); background: var(--e-surface); border-top: var(--e-line-soft); border-bottom: var(--e-line-soft); }
.e-sheet-grid > * { border-right: var(--e-line-soft); border-bottom: var(--e-line-soft); }   /* เส้นคั่นจากขอบการ์ด ไม่ใช่ gap+พื้นเทา (แถวสุดท้ายจะได้ไม่มีช่องเทาค้าง) */
.e-sheet-grid > :nth-child(3n) { border-right: 0; }
.e-sheet-g {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  min-height: 74px; padding: 8px 4px; background: var(--e-surface); border: 0; cursor: pointer;
  font: 600 11px var(--e-font-head); color: var(--e-text-2); text-align: center;
}
.e-sheet-g i { font-size: 18px; color: var(--e-a-700); }
.e-sheet-g small { font: 400 9.5px var(--e-font-body); color: var(--e-text-4); }
.e-sheet-g span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.e-sheet-note { padding: 10px 12px 2px; font: 400 11px var(--e-font-body); color: var(--e-text-4); }
body.e-sheet-open { overflow: hidden; }

/* วงล้อคำสั่ง */
@media (max-width: 991.98px) {
  .e-air {
    bottom: calc(var(--e-navh) + 14px + env(safe-area-inset-bottom, 0px));
    transform: translate(var(--dx, 0px), var(--dy, 0px)); transition: transform .18s ease;
  }
  .e-air.is-placed { bottom: auto; }
  .e-air.is-drag { transition: none; }
  /* ป้ายในวงล้อยาวได้ 2 บรรทัด ไม่ต้องตัดคำ (mockup มีกฎนี้ที่ 768px — ย้ายมาที่เบรกพอยนต์ของวงล้อ
     เพราะ openAir() คำนวณระยะกันขอบจากครึ่งความกว้างก้อน = 48px) */
  .e-air-node { width: 96px; }
  .e-air-node .e-air-lbl { white-space: normal; line-height: 1.15; text-align: center; }
}
.e-air-btn { position: relative; z-index: 2; touch-action: none; }
.e-air-ring { position: absolute; left: 28px; top: 28px; width: 0; height: 0; }
.e-air-node {
  position: absolute; left: 0; top: 0; display: flex; flex-direction: column; align-items: center; gap: 3px; width: 88px;
  transform: translate(-50%, -50%) scale(.5); opacity: 0; pointer-events: none; cursor: pointer;
  transition: transform .17s ease, opacity .13s linear;
}
body.e-air-open .e-air-node {
  transform: translate(calc(-50% + var(--x, 0px)), calc(-50% + var(--y, 0px))) scale(1);
  opacity: 1; pointer-events: auto; transition-delay: calc(var(--i, 0) * 22ms);
}
.e-air-node .e-air-lbl { white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.e-air-node.is-primary .e-air-ic { background: var(--e-a); border-color: var(--e-a); color: #fff; }
.e-air-node:active .e-air-ic { background: var(--e-a-100); }
.e-air-node:focus-visible .e-air-ic { outline: 2px solid var(--e-a); outline-offset: 2px; }
body.e-air-open .e-air-btn { background: var(--e-a-900); }

/* แถบตัวกรองกะพริบเมื่อสั่งจากวงล้อ (ตอบว่า "เปิดให้แล้ว อยู่ตรงนี้") */
.e-filter.is-flash, [data-filter-bar].is-flash { outline: 2px solid var(--e-a); outline-offset: 3px; }

/* พิมพ์: template ซ่อน rail/topbar/bottomnav/actionbar แล้ว แต่ลืม .e-air → ปุ่มติดกระดาษและ PDF
   (หัวข้อ 13 PRINT ด้านบนเติมให้แล้ว บรรทัดนี้เก็บไว้คู่กันเพื่อให้หัวข้อ 19b ถอดออกได้ทั้งก้อนใน U9) */
@media print { .e-air, [data-e-air], .e-sheet { display: none !important; } }

/* บรรทัดฐานของโปรเจกต์: อนิเมชันใหม่ต้องมีสวิตช์ปิด (app.css:288-290) — ปิดเฉพาะของวงล้อ
   ห้ามใช้ *{transition:none} จะดับ drawer/การ์ดที่ทดสอบผ่านแล้ว */
@media (prefers-reduced-motion: reduce) {
  .e-air, .e-air-node { transition: none !important; transition-delay: 0s !important; }
  .e-filter.is-flash, [data-filter-bar].is-flash { outline-style: dashed; }
}

/* §19b — แก้ตามภาพจริงที่ 390px (2 ก.ย. 2569) · ที่มา mockup :1218-1237
   ── ต่างจาก mockup 2 อย่าง (§2.4 ซ ข้อ 5-6) ──
   1) แถบตัวกรองจริงของระบบคือ [data-filter-bar] (includes/filter_bar.php:70 · app.js:214)
      ไม่ใช่ .e-filter ที่ไม่มีในโปรดักชันสักบรรทัด — กฎยุบ/กางจึงรับทั้งสองชื่อ
      และ **ยกเว้น [data-auto-submit="0"]** เพราะหน้ารายงาน 9 ไฟล์เก็บปุ่มสั่งออกรายงาน
      ไว้ในแถบตัวกรองนั้นเอง (filter_bar.php:12) — ยุบแล้วเปิดหน้ามาสั่งอะไรไม่ได้เลย
   2) กฎยุบ scope ใต้ .e-app ⇒ เริ่มมีผลตอน U2 (shell ใหม่) ไม่ใช่ตอน U1 ที่ยังไม่มีวงล้อ
      มาเปิดแถบตัวกรองให้ (ไม่งั้นมือถือจะกรองอะไรไม่ได้เลยทั้งเฟส U1) */
@media (max-width: 768px) {
  /* แถบตัวกรอง 5 ช่องกินจอแรกเกือบครึ่งและล้นขอบขวา → ยุบไว้ เปิดจากวงล้อ/ชิป "ตัวกรอง" */
  .e-app .e-filter,
  .e-app [data-filter-bar]:not([data-auto-submit="0"]) { display: none; }
  body.e-filter-open .e-app .e-filter { display: flex; flex-wrap: wrap; gap: 8px; }
  body.e-filter-open .e-app [data-filter-bar]:not([data-auto-submit="0"]) { display: revert; }
  body.e-filter-open .e-filter > .e-searchbox,
  body.e-filter-open .e-filter > .e-select,
  body.e-filter-open .e-filter > .e-btn { flex: 1 1 100%; width: 100%; min-width: 0; }
  body.e-filter-open .e-filter > .e-filter-date { flex: 1 1 calc(50% - 4px); min-width: 0; }
  body.e-filter-open .e-filter .e-filter-date .e-input { flex: 1; width: 100%; min-width: 0; }
  body.e-filter-open .e-filter > [data-pm-listview] { display: none; }   /* สลับการ์ด/ตารางไม่มีความหมายบนมือถือ */
  .e-pill.is-filter-chip { display: inline-flex !important; align-items: center; gap: 5px; }
}
@media (min-width: 769px) { .e-pill.is-filter-chip { display: none; } }   /* เดสก์ท็อปแถบตัวกรองแสดงอยู่แล้ว */

/* ปุ่มปิดแผ่นต้องแตะติดตั้งแต่ครั้งแรก (ของ template คือ 32px) */
.e-sheet-hd .e-icon-btn { min-width: var(--e-tap); min-height: var(--e-tap); }


/* ══ 20. เป้าแตะ — แหล่งความจริงเดียว (U9.1 · 7 ก.ย. 2569 · D170 + D171) ═══════════
   🔴🔴 ทำไมบล็อกนี้ถึงมาอยู่ในไฟล์ธีมได้ ทั้งที่ D4 เคยห้ามแตะไฟล์นี้
      D4 เขียนตอน U6.1 ซึ่ง **ยังไม่มีชั้นทดสอบเลยสักชั้น** · วันนี้มี probe 10 โมดูล ×
      2 ความกว้าง (baseline FAIL 0 ทุกบรรทัด) + renderer 84 ตัว ⇒ ผู้ใช้ผ่อน D4 แล้ว (D171)
      ➡ **แตะไฟล์นี้เมื่อไร ต้องรัน probe ครบ 10 โมดูล × 2 ความกว้าง + renderer 84 ตัวก่อนคอมมิตเสมอ**

   🔴 D170 — เกณฑ์ 44px บังคับ **เฉพาะจอสัมผัส (≤768px)** เท่านั้น (ผู้ใช้ตัดสิน 7 ก.ย. 2569)
      ⇒ บนเดสก์ท็อป `.e-btn` 38px · `.e-seg > *` 38px · `.e-pill` 32px **ถือว่าถูก ไม่ใช่บั๊ก**
        (ยังผ่าน WCAG 2.2 SC 2.5.8 ระดับ AA ที่กำหนด 24×24px · เลข 44 มาจาก Apple HIG ของจอสัมผัส)
      ⚠️ แต่ของที่เตี้ย **ทั้ง 2 ความกว้าง** ยังต้องยกเหมือนเดิม — D170 ตัดแค่ฝั่งเดสก์ท็อป

   ── 20a · ยุบกฎที่หน้าเขียนซ้ำกันเอง ────────────────────────────────────────
   วัดจริง 7 ก.ย. 2569 (ตัดคอมเมนต์ก่อนนับ · D110): กฎเป้าแตะถูกก๊อปไปแล้ว **52 ไฟล์ 212 จุด**
   (เอกสารแผนเขียนว่า "27 ไฟล์ 32 จุด" — ผิดทั้งคู่) · 9 ตัวเลือกข้างล่างกินไปแล้ว **111 จุด**
     `.e-panel-foot a` ซ้ำ 27 ไฟล์ · `.e-sec-hd a.e-spacer` 22 · `.e-panel-hd a.e-more` 17 ·
     `.e-sec-hd > a` 11 · `.e-sec-hd .e-note a` 10 · `.e-callout a, .e-help a` 9 ·
     `.e-content .e-icon-btn` 8 · `.e-table .e-icon-btn` 5 · `.e-table td a` 2
   🔴 **คัดลอกมาตามรูปเดิมทุกตัวอักษร และวางไว้ "นอก" media query** เหมือนที่หน้าทำอยู่วันนี้
      ⇒ เป็นการ **ยุบความซ้ำ ไม่ใช่เปลี่ยนพฤติกรรม** · หน้าไหนที่มีกฎอยู่แล้วต้องได้ค่าเท่าเดิมเป๊ะ
      ⚠️ ห้ามย้ายเข้า `@media (max-width:768px)` ตาม D170 เพราะจะทำให้ 52 หน้าที่วันนี้ได้ 44px
        บนเดสก์ท็อป **เปลี่ยนหน้าตาพร้อมกัน** — การรีแฟกเตอร์ต้องไม่เปลี่ยนสิ่งที่ผู้ใช้เห็น
   ⚠️ ตัวที่ **ไม่** ยุบเข้ามาโดยตั้งใจ: `.e-content .e-panel-bd a` (กว้างเกินไป — จะจับลิงก์
      ทุกตัวในเนื้อแผงรวมทั้งที่อยู่กลางย่อหน้า) · `.pur-mcard-n a` `.stk-reflink` `.stk-act`
      (คลาสเฉพาะหน้า) · `.fc .fc-button` `.choices__inner` (ไลบรารีภายนอก · อยู่ 20b)

   🔴 `display:inline-flex; align-items:center` **ตัดออกไม่ได้** — `<a>` เป็น inline
      ซึ่ง `min-height` ไม่มีผลเลย ⇒ ต้องมาคู่กันเสมอ (นี่คือเหตุผลที่ทุกหน้าเขียนครบ 3 อย่าง)
   ══════════════════════════════════════════════════════════════════════════════ */
.e-panel-foot a,
.e-sec-hd a.e-spacer,
.e-sec-hd > a,
.e-sec-hd .e-note a,
.e-panel-hd a.e-more,
.e-callout a,
.e-help a,
.e-table td a { display: inline-flex; align-items: center; min-height: var(--e-tap); }

.e-content .e-icon-btn,
.e-table .e-icon-btn { min-width: var(--e-tap); min-height: var(--e-tap); }

/* ── 20a-2 · ปุ่มข้อความในการ์ดความคิดเห็น (`includes/comments_card.php`) ─────────
   วัดจริงได้ **23px** บนเดสก์ท็อป ⇒ **ตก WCAG 2.2 AA (24×24) ไป 1px**
   🔴 probe_pj.js และ probe_ss.js **ยกเว้น `#cmtCard` ไว้ทั้งคู่** ด้วยเหตุผลว่า partial นี้
      แปลงไปแล้วใน U3 และใช้ร่วม 6 หน้า view ⇒ แตะแล้วกระทบหน้าที่ปิดเฟสไปแล้ว (D4)
      ⇒ **D171 ผ่อน D4 เฉพาะไฟล์ธีมนี้** จึงแก้ที่นี่ได้โดยไม่ต้องแตะ partial เลยสักบรรทัด
        และได้ผลกับทั้ง 6 หน้าพร้อมกัน แทนที่จะยกเว้นเพิ่มอีกโมดูล
   ⚠️ ตั้งแค่ `min-height` ไม่แตะ padding — Bootstrap ใส่ `!important` ให้ `.p-0` อยู่
      (คอมเมนต์ comments_card.php:84 เตือนไว้) ⇒ ระยะห่างระหว่างคอมเมนต์ไม่เปลี่ยน
   ⚠️ ที่ ≤768px ปุ่มพวกนี้ได้ ≥44px อยู่แล้ว (วัดแล้ว) ⇒ กฎนี้ยกเฉพาะพื้นเดสก์ท็อป */
#cmtCard .btn-link { display: inline-flex; align-items: center; min-height: 24px; }

/* ── 20b · ของที่ยังไม่มีหน้าไหนแก้เลย — เตี้ย "ทั้ง 2 ความกว้าง" ────────────────
   วัดจริงที่ 390px ด้วย probe ทั้ง 10 โมดูล (7 ก.ย. 2569) หลังหักไลบรารีภายนอกออกแล้ว
   ⚠️ ต่างจาก 20a ตรงที่ **ของพวกนี้ไม่เคยได้ 44px ที่ไหนเลย** ⇒ วางใน media query ตาม D170 ได้
      (ไม่มีหน้าไหนพึ่งค่าเดสก์ท็อปของมันอยู่ จึงไม่ใช่การเปลี่ยนสิ่งที่ผู้ใช้เห็นบนเดสก์ท็อป) */
/* ① ปุ่ม × ลบชิปของ Choices.js — วัดได้ **23×14px** · ขึ้นที่ `ss/form.php` 12 จุด และ
      `cm/form.php` 6 จุด · เตี้ยที่สุดในทั้งระบบ
   🔴🔴 **อยู่นอก media query โดยตั้งใจ — ต่างจากข้อ ② ข้างล่าง**
      D170 บอกว่าเดสก์ท็อป **ไม่บังคับ** 44px · แต่ไม่ได้บอกว่า "ต่ำกว่า AA ก็ได้"
      23×14px **ตกแม้แต่ WCAG 2.2 SC 2.5.8 ระดับ AA (24×24)** ⇒ ต้องแก้ทุกความกว้าง
      และค่านี้ไม่ใช่ "เจตนาของธีม" ด้วย — เป็นค่าตั้งต้นของไลบรารีที่ไม่มีใครเลือก
   🔴 ต้องยก "ทั้งชิป" ด้วย ไม่ใช่ยกแต่ปุ่ม — ปุ่มเป็นส่วนท้ายของชิปที่สูงตามชิป
      ถ้ายกแต่ปุ่ม ชิปจะสูง 29px เท่าเดิมแล้วปุ่มล้นออกนอกกรอบ */
.choices__list--multiple .choices__item { min-height: var(--e-tap); display: inline-flex; align-items: center; }
.choices[data-type*="select-multiple"] .choices__button { min-width: var(--e-tap); min-height: var(--e-tap);
                                                          background-position: center; }

@media (max-width: 768px) {
  /* ② ชิปในการ์ด KPI — ธีมตั้ง 28px ไว้เองที่ `.e-kpi .e-kpi-chips .e-chip` (0,3,0)
        ⇒ ตัวทับต้องน้ำหนัก **เท่ากันเป๊ะ** แล้ววางทีหลัง ไม่งั้นแพ้เงียบ ๆ (บทเรียน D129)
     🔴 ยกเว้นไม่ได้ — `ss/index.php:245` เขียนไว้เองว่า "nodate ไม่มีช่องของตัวเองใน filter bar"
        ⇒ ชิปนี้คือ **ทางเดียว** ที่จะกรอง "ยังไม่นัดวัน"
     ⚠️ **อยู่ใน media query ต่างจากข้อ ①** เพราะ 86×28px **ผ่าน WCAG AA อยู่แล้ว**
        และ 28px เป็นค่าที่ธีมตั้งใจตั้งไว้เองที่ `.e-kpi .e-kpi-chips .e-chip` (:1021)
        ⇒ บนเดสก์ท็อปปล่อยตามเจตนาเดิมได้ตาม D170 · probe ยกเว้นให้เฉพาะ >768px */
  .e-kpi .e-kpi-chips .e-chip { min-height: var(--e-tap); }
}

/* ══ 21. รายการคำบรรยายรูป — มาร์กอัปมาจาก emsImageCaptions() (includes/functions.php) ══
   ใช้ที่ 5 จุด: pm/view · cm/view · ss/view · ss/execute · wo/view

   ⚠️ เลข 21 เคยเป็นหัวข้อ View Transitions ที่ถูกถอดออกทั้งชุดในแผน 11 · S1 (D194/D199)
      — นี่ไม่ใช่หัวข้อเดิมที่กลับมา แต่เป็นของใหม่ที่มาอยู่ในเลขที่ว่างไว้
   🔴 กฎอยู่ในธีม ไม่ใช่ในบล็อก <style> ของหน้า เพราะมาร์กอัปมาจากฟังก์ชันกลางตัวเดียว
      ⇒ หน้าที่ 6 ที่เรียกฟังก์ชันนั้นได้หน้าตาถูกทันที **โดยไม่ต้องมีใครจำว่าต้องก๊อป CSS ตามไปด้วย**
   ⚠️ ห้ามใส่กฎของ .e-photo-grid หรือขนาดรูปที่นี่ — คนละคอมโพเนนต์กัน (ดูเหตุผลใน includes/lightbox.php) */
.e-caps { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.e-cap { display: grid; grid-template-columns: 14px minmax(0, 1fr) auto; align-items: start;
         gap: 2px 8px; padding: 6px 9px; background: var(--e-surface-alt); border: var(--e-line-soft);
         font-size: 11.5px; line-height: 1.55; color: var(--e-text-2); }
.e-cap-ic { color: var(--e-text-4); font-size: 12px; line-height: 1.55; }
.e-cap-bd { min-width: 0; }
/* ตัด 2 บรรทัด — คลาส .is-open ที่ ems-field.js สลับให้เป็นตัวปลด
   ⚠️ -webkit-line-clamp ต้องมาคู่กับ -webkit-box + -webkit-box-orient เสมอ ขาดตัวใดตัวหนึ่ง
      = ไม่ตัดเลยโดยไม่มีอะไรฟ้อง
   ⚠️ overflow-wrap: anywhere กันคำบรรยายที่เป็นรหัสยาวไม่มีช่องว่าง (เช่น FF42000/LF3349)
      ดันคอลัมน์กลางจนแถวล้นออกนอกแผง (ตระกูล D152 — ของจริงยาวสุด 255 อักขระ) */
.e-cap-txt { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
             overflow: hidden; overflow-wrap: anywhere; }
.e-cap.is-open .e-cap-txt { display: block; -webkit-line-clamp: unset; overflow: visible; }
.e-cap-n { white-space: nowrap; color: var(--e-text-4); font-variant-numeric: tabular-nums; }
/* .e-link-btn (:992) ให้ min-height: var(--e-tap) มาแล้ว ⇒ ห้ามเขียน min-height ทับที่นี่
   padding-left: 0 เพื่อให้ขอบซ้ายของปุ่มตรงกับข้อความ (ความสูงซึ่งเป็นสิ่งที่ probe วัด ไม่เปลี่ยน) */
.e-cap-more { padding-left: 0; font-size: 11.5px; }
/* 🔴 [hidden] ต้องมี !important — .e-link-btn ตั้ง display: inline-flex ซึ่งน้ำหนักสูงกว่ากฎ
      [hidden] ตั้งต้นของเบราว์เซอร์ ⇒ ตั้ง btn.hidden = true แล้วปุ่มยังโผล่อยู่ (D142) */
.e-caps [hidden] { display: none !important; }

/* ══ 22. หัวตารางที่กดเรียงได้ — แหล่งความจริงเดียว ══════════════════════════════
   มาร์กอัปของปุ่มถูก **สร้างโดย ems-field.js** ไม่ใช่พิมพ์ในหน้า
   หน้าเว็บเติมแค่ `data-sort="text|num|date"` ให้ <th> เท่านั้น

   🔴 ทำไมให้ JS สร้างปุ่ม แทนที่จะพิมพ์ <button> ลงไปในทุกหน้า
      ของจริงมี **76 ตาราง ใน 45 ไฟล์** (สำรวจ 10 ก.ย. 2569) ⇒ พิมพ์เองแปลว่า
      เป้าแตะ · คีย์บอร์ด · aria-sort ต้องถูกเขียนให้ถูกซ้ำ ๆ ด้วย **วินัยของคนเขียน**
      ซึ่งเป็นสิ่งที่ D153 บอกให้เลี่ยง ⇒ ให้ตัวเดียวสร้าง ทุกหน้าจึงได้เหมือนกันโดยรูปทรง
   ⚠️ ผลพวงที่ต้องรู้: ตัวตรวจที่อ่าน **ซอร์ส** จะไม่เห็นปุ่มนี้เลย (ตระกูล D203/D224)
      ⇒ ข้อตรวจของหัวข้อนี้ต้องวัดในเบราว์เซอร์ หรืออ่านไฟล์ผลของ renderer เท่านั้น

   🔴 ธีมตั้ง `.e-table thead th { padding: 8px 10px }` ไว้แล้ว ⇒ ถ้าปล่อยไว้
      ปุ่มที่สูง 44px บนมือถือจะได้หัวตารางสูง 60px ⇒ ย้าย padding มาไว้ที่ปุ่มแทน
      (JS เป็นคนเติมคลาส .is-sortable ให้ th — ไม่ใช้ :has() เพื่อไม่ต้องพึ่งเวอร์ชันเบราว์เซอร์)

   🔴 D170 — 44px บังคับเฉพาะ ≤768px · บนเดสก์ท็อปหัวตารางเตี้ยกว่านั้นถือว่าถูก
      ⚠️ แต่ต้องผ่าน WCAG 2.2 AA (24×24) ทุกความกว้าง ⇒ ตั้ง min-height: 24px ไว้นอก media query
      (ค่าเดิมของ th คือ ~28px อยู่แล้ว — บรรทัดนี้จึงเป็นตาข่าย ไม่ใช่การเปลี่ยนหน้าตา)
   ══════════════════════════════════════════════════════════════════════════════ */
.e-table thead th.is-sortable { padding: 0; }

.e-table thead th .e-th-sort {
  display: inline-flex; align-items: center; gap: 5px; width: 100%;
  min-height: 24px; padding: 8px 10px;
  background: none; border: 0; border-radius: 0;
  font: inherit; letter-spacing: inherit; text-transform: inherit;
  color: inherit; text-align: inherit; cursor: pointer;
  -webkit-user-select: none; user-select: none;
}
/* คอลัมน์ตัวเลขจัดชิดขวาอยู่แล้ว (.e-t-num / .text-end) ⇒ ปุ่มต้องตามไปด้วย
   ไม่งั้นป้ายลอยไปอยู่ซ้ายในขณะที่ตัวเลขอยู่ขวา */
.e-table thead th.text-end   .e-th-sort,
.e-table thead th.e-t-num    .e-th-sort { justify-content: flex-end; }

.e-table thead th .e-th-sort:hover   { color: var(--e-a-700); background: var(--e-a-100); }
.e-table thead th .e-th-sort:focus-visible { outline: 2px solid var(--e-a); outline-offset: -2px; }

/* ตัวชี้ทิศ — ใช้อักขระยูนิโคด ไม่พึ่งฟอนต์ไอคอนของ Bootstrap
   (หน้ารายงานบางหน้าไม่ได้โหลดฟอนต์นั้น และนี่คือรูปเดียวกับที่ reports/stock_report.php ใช้มาก่อน) */
.e-table thead th .e-th-sort::after {
  content: "\2195"; margin-left: auto; padding-left: 4px;
  color: var(--e-n-400); font-size: 11px; line-height: 1;
}
.e-table thead th.text-end .e-th-sort::after,
.e-table thead th.e-t-num  .e-th-sort::after { margin-left: 0; }

.e-table thead th[aria-sort="ascending"]  .e-th-sort { color: var(--e-a-800); }
.e-table thead th[aria-sort="descending"] .e-th-sort { color: var(--e-a-800); }
.e-table thead th[aria-sort="ascending"]  .e-th-sort::after { content: "\2191"; color: var(--e-a-800); }
.e-table thead th[aria-sort="descending"] .e-th-sort::after { content: "\2193"; color: var(--e-a-800); }

@media (max-width: 768px) {
  /* น้ำหนักตัวเลือกต้อง **เท่ากับกฎฐานข้างบนเป๊ะ** (0,3,0) แล้ววางทีหลัง
     ไม่งั้นแพ้เงียบ ๆ ทั้งที่กฎอยู่ในไฟล์แล้ว (บทเรียน D129) */
  .e-table thead th .e-th-sort { min-height: var(--e-tap); }
}

@media print {
  /* กระดาษไม่มีใครกดได้ — ซ่อนตัวชี้ทิศ แต่คงป้ายไว้ */
  .e-table thead th .e-th-sort::after { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════════════
   21b · หัวตารางที่เรียงด้วย **ลิงก์ฝั่งเซิร์ฟเวอร์** (`a.th-sort`) — แผน 12 · T4
   ──────────────────────────────────────────────────────────────────────────────
   🔴 ทำไมมี 2 ชุด ไม่ใช่ชุดเดียว — `.e-th-sort` ข้างบนคือ **ปุ่มที่ JS สร้าง** สำหรับตาราง
      ที่มีแถวครบทั้งชุดอยู่ในหน้าแล้ว · ชุดนี้คือ **ลิงก์ `?sort=&dir=` ที่ PHP พิมพ์** สำหรับ
      หน้าที่ **แบ่งหน้า** ซึ่งเรียงในเบราว์เซอร์ไม่ได้ (จะเรียงแค่ 20 แถวที่เห็น โดยไม่มีอะไรฟ้อง)
      ⇒ คนละกลไก คนละสัญญา แต่ต้องให้ผู้ใช้เห็น **หน้าตาเดียวกัน** ⇒ ใช้ตัวชี้ทิศชุดเดียวกัน

   🔴🔴 ย้ายมาจากบล็อก `<style>` ของ `work_orders/index.php` (U5.2) — ที่นั่นเขียนกำกับไว้ว่า
      *"ใช้ที่หน้านี้หน้าเดียวในทั้งระบบ (grep th-sort = 1 ไฟล์)"* ซึ่ง **หมดอายุแล้ว**
      T4 ทำให้มี 13 ไฟล์ที่ใช้คลาสนี้ ⇒ ปล่อยไว้ในหน้าคือก๊อปกฎเดิม 8 บรรทัด × 13 ไฟล์
      (ตระกูล D171 — ของจริงเคยบานไป 52 ไฟล์ / 212 จุด) ⇒ ยุบเข้าธีมตาม D171 ที่ผ่อน D4 แล้ว

   🔴🔴🔴 และเหตุผลที่ **ห้ามก๊อป CSS เดิมมาตรง ๆ**: กฎเดิมไม่มี `min-height` เลย
      มันรอดมาได้เพราะ `#woViewTable { display: none !important }` ที่ ≤768px
      ⇒ ลิงก์นั้น **ไม่เคยถูกตัววัดเป้าแตะมองเห็นที่ 390px เลยแม้แต่ครั้งเดียว** (D234 เป๊ะ)
      แต่ 8 ใน 12 หน้าของ T4 **ไม่สลับเป็นการ์ด** — ตารางยังอยู่ที่ 390px (เลื่อนในแนวนอน)
      ⇒ ก๊อปมาเฉย ๆ = FAIL เป้าแตะชุดใหม่ทันที · และมันจะดูเหมือน "บั๊กที่ T4 ทำขึ้น"
        ทั้งที่เป็นหนี้เก่าที่เพิ่งถูกส่องเห็น (D225)

   ⚠️ ใช้คลาส `.is-sortable` ร่วมกับฝั่ง JS โดยตั้งใจ — กฎ `th.is-sortable { padding: 0 }`
      ข้างบนมีอยู่แล้ว และ JS เป็นฝ่าย *เขียน* คลาสนี้เท่านั้น ไม่มีใครอ่านมันเลย
      (`grep is-sortable` = `ems-field.js:1224` จุดเดียว) ⇒ ใช้เป็นหมุด CSS ได้ ไม่ชนกลไกใด
      🔴 ห้ามเปลี่ยนไปใช้ `:has()` — บล็อกข้างบนจดเหตุผลไว้แล้วว่าเลี่ยงเพื่อไม่ต้องพึ่งเวอร์ชันเบราว์เซอร์
   ══════════════════════════════════════════════════════════════════════════════ */
.e-table thead th a.th-sort {
  display: inline-flex; align-items: center; gap: 5px; width: 100%;
  min-height: 24px; padding: 8px 10px;
  color: inherit; text-decoration: none; white-space: nowrap;
}
.e-table thead th.text-end a.th-sort,
.e-table thead th.e-t-num  a.th-sort { justify-content: flex-end; }

.e-table thead th a.th-sort:hover { color: var(--e-a-700); background: var(--e-a-100); }
.e-table thead th a.th-sort:focus-visible { outline: 2px solid var(--e-a); outline-offset: -2px; }

.e-table thead th a.th-sort .th-sort-icon { color: var(--e-text-4); }
.e-table thead th a.th-sort .th-sort-ind  { margin-left: auto; padding-left: 4px; font-size: 11px; opacity: .35; }
.e-table thead th.text-end a.th-sort .th-sort-ind,
.e-table thead th.e-t-num  a.th-sort .th-sort-ind { margin-left: 0; }
.e-table thead th a.th-sort:hover .th-sort-ind { opacity: .7; }

.e-table thead th a.th-sort.active                 { color: var(--e-a-800); }
.e-table thead th a.th-sort.active .th-sort-ind    { opacity: 1; }
.e-table thead th a.th-sort.active .th-sort-icon   { color: inherit; }

/* หัวคอลัมน์ที่เรียงไม่ได้ในหน้าเดียวกัน — ต้องได้ padding เท่ากับลิงก์ ไม่งั้นแถวหัวเบี้ยว
   (th ของมันไม่ได้คลาส .is-sortable จึงยังมี padding ของธีมอยู่ ⇒ ไม่ต้องทำอะไรเพิ่ม
    บรรทัดนี้จัดเฉพาะไอคอนนำหน้าให้สีเดียวกับฝั่งที่กดได้) */
.e-table thead th > .th-sort-icon { color: var(--e-text-4); }

@media (max-width: 768px) {
  /* 🔴 น้ำหนักตัวเลือกต้องเท่ากับกฎฐานข้างบนเป๊ะ (0,3,1) แล้ววางทีหลัง — D129
        เขียนเป็น `.th-sort` เฉย ๆ (0,1,0) จะแพ้กฎฐานเงียบ ๆ ทั้งที่อยู่ในไฟล์แล้ว */
  .e-table thead th a.th-sort { min-height: var(--e-tap); }
}

@media print {
  .e-table thead th a.th-sort .th-sort-ind { display: none; }
}

/* ---------- 22. TABLE — ความกว้างคอลัมน์ที่ไม่ขยับเวลาเรียง (แผน 12 · T6) -----
   🔴 ทำไมต้องมี: ธีมนี้ไม่เคยตั้ง `table-layout` เลย ⇒ เป็นค่าปริยาย `auto`
      ซึ่งแปลตามนิยามว่า "ให้เนื้อหาที่อยู่ในตารางตอนนั้นเป็นคนกำหนดความกว้าง"
      ⇒ หน้าที่เรียงที่ SQL กดหัวคอลัมน์แล้ว **โหลดหน้าใหม่ได้แถวคนละชุด** ⇒ เนื้อหาเปลี่ยน
        ⇒ เบราว์เซอร์คำนวณความกว้างใหม่ทุกครั้ง ⇒ ตารางกระโดดต่อหน้าผู้ใช้
      วัดจริงบน production 11 ก.ย. 2569 (ผู้ใช้รายงานเอง):
        · `stock/index.php` กด "มูลค่ารวม" ⇒ ชื่อวัสดุหด **42.7px** · หมวดหมู่ขยาย 25.5px · ขยับ 6/6 คอลัมน์
        · `purchase/index.php` กด "ผู้ขอ" ⇒ 7.9px · `work_orders/index.php` กด "กำหนดส่ง" ⇒ 5.4px

   ⚠️ **เป็น opt-in ด้วยคลาส ไม่ใช่กฎเหมาเข่งกับ `.e-table`** — ตารางที่เรียง *ฝั่งเบราว์เซอร์*
      (73 ใบจาก T0–T3) วัดแล้ว **ไม่ขยับเลยสักพิกเซล** เพราะมันสลับลำดับ *แถวชุดเดิม*
      ⇒ เนื้อหาที่ป้อนให้ auto layout เหมือนเดิมทุกตัวอักษร ⇒ **ไม่มีเหตุให้ไปแตะ**
      (และการแตะจะเป็นการเปลี่ยนหน้าตา 73 ใบโดยไม่มีใครขอ · D235)

   ⚠️ กติกาการใช้ — **ทุกคอลัมน์ประกาศความกว้าง ยกเว้นคอลัมน์ข้อความยาว 1 คอลัมน์**
      คอลัมน์ที่ไม่ประกาศจะได้พื้นที่ที่เหลือไปเองทั้งหมด (ตามสเปกของ table-layout: fixed)
      🔴 ถ้าปล่อยไม่ประกาศ **มากกว่า 1 คอลัมน์** พวกมันจะแบ่งที่เหลือ **เท่า ๆ กัน**
         ซึ่งไม่ใช่สิ่งที่ใครต้องการเลย — ตรวจด้วย `verify_tbl_fixed.php`
   ---------------------------------------------------------------------------- */
.e-table-fixed { table-layout: fixed; }

/* เนื้อหาที่ยาวเกินช่องต้องถูก "ตัด" ไม่ใช่ล้นไปทับคอลัมน์ถัดไป
   🔴 จำเป็นคู่กับ fixed เสมอ — fixed ไม่ขยายคอลัมน์ให้ ⇒ ไม่ตัด = ตัวหนังสือซ้อนกันจนอ่านไม่ออก */
.e-table-fixed thead th,
.e-table-fixed tbody td { overflow: hidden; }

/* ตัดด้วย … — ต้องมี `white-space: nowrap` คู่เสมอ ไม่งั้น text-overflow ไม่ทำงาน
   ⚠️ ไม่กระทบกล่องซ้อนบรรทัด `.e-td-stack` เพราะมันเป็น flex column
      ⇒ ลูกแต่ละตัวยังขึ้นบรรทัดของตัวเองเหมือนเดิม nowrap คุมแค่ "ในบรรทัด" */
.e-table-fixed tbody td { white-space: nowrap; text-overflow: ellipsis; }

/* 🔴 ellipsis ทำงานเฉพาะกับกล่องที่ "มีข้อความอยู่ในตัวมันเอง"
      ตั้งที่ `td` อย่างเดียวจึงไม่มีผลกับข้อความที่อยู่ลึกเข้าไปอีกชั้น (ซึ่งคือรูปแบบที่ใช้จริง
      ในหน้ารายการเกือบทุกหน้า: `<td><span class="e-td-stack"><strong>ชื่อ</strong>…`)
      ⇒ ต้องลงกฎที่ "ลูกของ .e-td-stack" ด้วย ไม่งั้นชื่อยาว ๆ จะถูกตัดแบบไม่มี … ให้เห็น */
.e-table-fixed tbody td > .e-td-stack { max-width: 100%; }
.e-table-fixed tbody td > .e-td-stack > * {
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* แถวปุ่มในเซลล์จัดการต้องไม่ถูก nowrap บีบจนปุ่มซ้อนกัน — คืนค่าให้เฉพาะกล่องปุ่ม */
.e-table-fixed tbody td .e-row-wrap { white-space: normal; }

/* ความกว้างมาตรฐานเป็น % — ใช้ % ไม่ใช่ px เพราะตารางยืดตามจอ
   (px จะแข็งจนตารางล้นที่จอแคบ แล้วต้องเลื่อนแนวนอนทั้งที่ไม่จำเป็น)
   ⚠️ ค่าพวกนี้ **ถอดมาจากความกว้างที่เรนเดอร์อยู่จริงบน production** ของหน้าที่มีข้อมูลเยอะ
      (audit_logs 50 แถว · email_logs 50 · purchase/stock/work_orders 20)
      ไม่ได้คิดขึ้นใหม่ — เพื่อให้หน้าตาหลังแก้ "เหมือนเดิม" ไม่ใช่ "ใหม่" (D235)
   🔴 หน้าที่มี 1–2 แถววันนี้ (cm · ss · suppliers) **ห้ามถอดความกว้างจากของตัวเอง**
      เพราะนั่นคือความกว้างที่มาจากข้อมูลที่แทบไม่มี ⇒ ล็อกไว้แล้วจะแคบเกินไปวันที่ข้อมูลโต
      (D152/D237) ⇒ ให้ใช้มาตราส่วนตามชนิดคอลัมน์ข้างล่างนี้แทน */
.e-w-5  { width: 5%; }
.e-w-6  { width: 6%; }
.e-w-7  { width: 7%; }
.e-w-8  { width: 8%; }
.e-w-9  { width: 9%; }
.e-w-10 { width: 10%; }
.e-w-11 { width: 11%; }
.e-w-12 { width: 12%; }
.e-w-13 { width: 13%; }
.e-w-14 { width: 14%; }
.e-w-15 { width: 15%; }
.e-w-16 { width: 16%; }
.e-w-18 { width: 18%; }
.e-w-20 { width: 20%; }


/* ══ 23. แผงพับได้ — มาร์กอัปมาจาก emsAccOpen()/emsAccClose() (includes/functions.php) ══
   (21 ก.ย. 2569 · ผู้ใช้ขอ · เริ่มที่ purchase/view.php แล้วยกมาเป็นของกลาง)

   🔴 กางด้วย grid-template-rows 0fr→1fr **ไม่ใช่ max-height**
      max-height ต้องเดา "ค่าสูงสุด" ไว้ล่วงหน้า (app.css:145 เดาไว้ 600px) ⇒ วันที่เนื้อหาสูงเกิน
      ค่านั้น มันจะ **ตัดทิ้งเงียบ ๆ ไม่มี error** · ประวัติในระบบนี้ยาวได้ถึง 100 แถว
      (equipment/view.php ตั้ง LIMIT 100) ⇒ ตัวเลขที่เดาไว้จะผิดวันหนึ่งแน่นอน
      ส่วน 1fr กางไปที่ความสูง **จริง** ของเนื้อหา จึงไม่มีตัวเลขให้เดาและไม่มีวันตัด
   ⚠️ เบราว์เซอร์ที่คำนวณหน่วย fr ระหว่างทางไม่ได้ (Safari เก่ากว่า 17.2) ได้ "กางทันที
      ไม่มีอนิเมชัน" ไม่ใช่ "กางไม่ออก" ⇒ เสื่อมลงปลอดภัย (หลักเดียวกับ D270/D344)
   ⚠️ และถ้าอัปไฟล์นี้ไม่ทันหน้าที่ประกาศ .e-acc จะได้แผง **กางค้าง** = พฤติกรรมก่อนมีฟีเจอร์
      ไม่ใช่แผงที่เปิดไม่ออก ⇒ ลำดับการอัปไม่ใช่เรื่องคอขาดบาดตาย (ต่างจาก D253)
   ⚠️ ตัวสลับพับ/กางเป็น **นิพจน์เดียวใน onclick** ที่ helper พิมพ์ให้ ไม่ใช่ฟังก์ชันใน ems-field.js
      โดยตั้งใจ — จะได้ไม่มีกิ่ง "ของกลางยังมาไม่ถึง" ให้พลาด (D344 ทั้ง 2 ข้อ) */

/* 🔴 ชั้นที่ถูก clip ต้อง **ไม่มี padding ของตัวเอง** — เคยยุบรวมกับ .e-panel-bd แล้วพัง:
      min-height:0 เอาออกแค่ "ขั้นต่ำของกล่องเนื้อหา" ส่วน padding อยู่ *นอก* กล่องนั้น
      ⇒ ตอนพับยังสูงเท่า padding บน+ล่าง (14+14 = 28px) เป็นแถบว่างใต้หัวแผง
   ⚠️ วัดในเบราว์เซอร์จริงแล้ว: ย้าย overflow ไปไว้ที่กล่องกริดแทน **ไม่ช่วย** ยัง 28px เท่าเดิม
   ⚠️ ตาคนมองข้ามง่ายมาก — จับได้จากการวัดความสูงเท่านั้น ⇒ ห้ามยุบ 2 ชั้นนี้กลับเป็นชั้นเดียว */
.e-acc-wrap { display: grid; grid-template-rows: 0fr; visibility: hidden;
              transition: grid-template-rows .2s ease, visibility 0s linear .2s; }
.e-acc.is-open .e-acc-wrap { grid-template-rows: 1fr; visibility: visible;
              transition: grid-template-rows .2s ease, visibility 0s linear 0s; }
.e-acc-in { overflow: hidden; min-height: 0; }

/* หัวแผงที่กดได้ — กฎ .e-panel-hd (:397) เขียนไว้สำหรับกล่องธรรมดา จึงประกาศเฉพาะสิ่งที่กล่องไม่มี
   🔴 พอเปลี่ยนเป็นปุ่ม ค่าตั้งต้นของเบราว์เซอร์โผล่มาทั้งชุด **โดยไม่มีอะไรฟ้อง**
      (วัดจริงด้วย positive control 21 ก.ย. 2569): ขอบ 2px 3 ด้าน · กว้าง 224px แทนเต็มแผง
      · สีข้อความกลับไปเป็นสีปุ่มของระบบ · iOS ใส่มุมโค้งและไล่เฉดสีให้เอง
   🔴 44px ทั้ง 2 ความกว้าง ไม่ใช่เฉพาะมือถือ — probe ทุกโมดูลกวาดปุ่มในเขตเนื้อหาด้วยเกณฑ์ 44px
      ที่ทั้ง 1200 และ 390px และ **ไม่มีข้อยกเว้นสำหรับปุ่มทั่วไป** ⇒ ยกเว้นเพื่อให้ผ่าน = D228
   ⚠️ สูงกว่าหัวแผงธรรมดา 6px **โดยตั้งใจ** — มันคือหัวแผงเดียวที่กดได้ */
.e-acc-hd { width: 100%; border: 0; border-bottom: var(--e-line); text-align: left;
            color: inherit; cursor: pointer; appearance: none; -webkit-appearance: none;
            min-height: var(--e-tap); }
.e-acc-hd:focus-visible { outline-offset: -2px; }   /* แถบเต็มแผง ⇒ วงแหวนนอกจะทับขอบแผงพอดี */
.e-acc-hd:hover { background: var(--e-a-100); }
.e-acc:not(.is-open) .e-acc-hd { border-bottom: 0; }
.e-acc-ic { flex: none; font-size: 10px; color: var(--e-text-4);
            transition: transform .16s ease; }
.e-acc.is-open .e-acc-ic { transform: rotate(180deg); }

/* บรรทัดฐานของโปรเจกต์ (:1427) — อนิเมชันใหม่ต้องมีสวิตช์ปิดเสมอ
   ⚠️ ปิดเฉพาะของแผงนี้ ห้ามเขียนกฎเหมาที่ไปดับ drawer/การ์ดที่ทดสอบผ่านแล้ว */
@media (prefers-reduced-motion: reduce) {
  .e-acc-wrap, .e-acc-ic { transition: none !important; }
}
