/* public/css/tokens.css
   ที่มา: html/M SPORTS.dc.html
   - base reset (บรรทัด 16-33 ใน <style> ของ helmet)
   - CSS custom properties (จาก root div บรรทัด 35 — ค่า light theme; ค่า dark ยกจาก syncRoot() บรรทัด 4791-4793)
   ตั้งใจให้เป็น "แหล่งเดียว" ของ design token ทั้งระบบ — หน้าใหม่ทุกหน้าอ้างตัวแปรจากที่นี่ อย่า hardcode สี/ระยะซ้ำ
*/

/* ---------- design tokens: light (ค่าเริ่มต้น) ---------- */
:root{
  /* 🔴 บอกเบราว์เซอร์ว่าหน้านี้เป็นธีมอะไร — ของที่ "เบราว์เซอร์วาดเอง" ใช้ค่านี้ตัดสินสีทั้งหมด
     (พื้นช่องกรอกที่ไม่ได้ตั้งเอง · สี placeholder · ไอคอนปฏิทิน/นาฬิกาในช่องวันที่-เวลา ·
     รายการที่ผุดจาก <select> · แถบเลื่อน) — ไม่ประกาศ = `normal` = เบราว์เซอร์วาดแบบธีมสว่างเสมอ
     แม้ตัวหนังสือจะเป็นสีของธีมมืดแล้ว (1 ต.ค. 2569 เจ้าของเจอเองที่ฟอร์ม "ไม่อนุมัติใบลา") */
  color-scheme: light;
  --bg:#f6f7fb;
  --surface:#ffffff;
  --surface2:#f1f4f9;
  --text:#0f172a;
  --muted:#64748b;
  --border:#e4e8ef;
  --brand:#4f46e5;
  --brand-soft:#eef0ff;
  --shadow:0 1px 2px rgb(15 23 42/.05), 0 6px 18px rgb(15 23 42/.05);
  --sheet-align:center;
  --sheet-radius:18px;
  /* 🔴 ฉากหลัง + ทรงของแผ่น/ลิ้นชัก — **ที่เดียวของระบบ** (เดิมฝังค่าไว้ใน 4 ไฟล์ที่สร้าง overlay
     และต่างกันเอง 3 ค่า) · ดูเหตุผลของค่าในธีมมืดที่บล็อก dark ด้านล่าง */
  --scrim:rgb(15 23 42/.42);
  --sheet-bg:var(--surface);
  --sheet-border:var(--border);
  --sheet-shadow:0 24px 60px rgb(8 12 20/.3);
}

/* ---------- design tokens: dark ----------
   สลับด้วย data-theme="dark" บน <html> (ตั้ง+จำค่าใน localStorage โดย public/js/core/theme.js) */
html[data-theme="dark"]{
  color-scheme: dark;   /* 🔴 คู่กับ color-scheme:light ข้างบน — ดูเหตุผลที่นั่น */
  /* 🔴 ธีมมืด: แผ่นที่เปิดอยู่ต้อง "ลอยอยู่เหนือทุกอย่าง" ให้เห็น — ของเดิมแยกไม่ออกเลย เพราะ
     (1) ฉากหลังใช้สี rgb(8 12 20) ซึ่ง **เกือบเท่าสีพื้นหน้าเอง (#0a0f18)** ทับแล้วแทบไม่มืดลง
         (วัดจริง: 10,15,24 → 9,14,22 = ต่างกันช่องละ 1 หน่วย ตาคนมองไม่ออก)
     (2) ตัวแผ่นใช้ `--surface` ซึ่งเป็น **สีเดียวกับการ์ดที่อยู่ข้างหลังเป๊ะ**
     (3) เงาเป็นสีดำบนพื้นดำ = มองไม่เห็น · ในธีมมืดความ "ลอย" สื่อด้วยความสว่างกับขอบ ไม่ใช่เงา
     → ฉากหลังเป็นดำจริง 62% · พื้นแผ่นยกขึ้นหนึ่งขั้นให้สว่างกว่าการ์ด · ขอบชัดขึ้น
     ⚠️ `--sheet-bg` ตั้งใจอยู่ระหว่าง `--surface` (#141b27) กับ `--surface2` (#1d2634) — ของที่อยู่
        *ข้างใน* แผ่นหลายตัวใช้ 2 ค่านั้นเป็นพื้น ถ้าแผ่นไปใช้ค่าเดียวกันมันจะกลืนกันเอง */
  --scrim:rgb(0 0 0/.62);
  --sheet-bg:#1b2433;
  --sheet-border:#3a475e;
  --sheet-shadow:0 24px 60px rgb(0 0 0/.65);
  --bg:#0a0f18;
  --surface:#141b27;
  --surface2:#1d2634;
  --text:#e9eef7;
  --muted:#93a3b8;
  --border:#27313f;
  --brand:#6a63ea;
  --brand-soft:#241f57;
  --shadow:0 1px 2px rgb(0 0 0/.5), 0 8px 22px rgb(0 0 0/.35);
}

/* sheet/modal alignment: ล่างจอมือถือ (แผ่นเลื่อนขึ้นจากล่าง), กลางจอ desktop
   (ของเดิมคำนวณจาก window.innerWidth ด้วย JS ทุก resize — ที่นี่ใช้ media query ล้วน ผลลัพธ์เหมือนกันแต่ไม่ต้องมี JS listener) */
@media (max-width:759px){
  :root{ --sheet-align:end; --sheet-radius:20px 20px 0 0; }
}

html, body{ height:100%; }
body{
  margin:0;
  background:var(--bg);
  color:var(--text);
  font-family:'Sarabun','Noto Sans Thai',sans-serif;
  font-size:14px;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
*{ box-sizing:border-box; }
button{ font:inherit; color:inherit; background:none; border:0; cursor:pointer; padding:0; text-align:left; }
button:disabled{ cursor:not-allowed; opacity:.6; }
input,select,textarea{ font:inherit; color:inherit; }
/* 🔴 ช่องกรอกต้องมีพื้นหลังของตัวเองเสมอ ห้ามปล่อยให้ตกไปใช้ค่าเริ่มต้นของเบราว์เซอร์
   (ซึ่งเป็น "สีขาว" ของธีมสว่าง) — ของเดิมตั้งแต่ไฟล์นี้เกิดมามีแต่ `color:inherit` ตัวหนังสือจึง
   เป็นสีขาวของธีมมืดบนพื้นขาวของเบราว์เซอร์ = **อ่านไม่ออกเลย** · ที่ผ่านมาไม่มีใครเจอเพราะเกือบทุก
   ช่องในระบบตั้ง `background:var(--surface)` เป็น inline style ไว้เอง (184 จาก 249 ช่องไม่ได้ตั้ง
   แต่ส่วนใหญ่ถูกบังด้วยคลาส/แผงที่ตั้งให้) เหลือรอดมาถึงจอผู้ใช้จุดเดียวคือ textarea ของแผ่น
   "ถามเหตุผล" ซึ่งใช้ร่วมกัน 11 จุดทั้งระบบ
   🔴 ใช้ `:where()` เพราะมันมี specificity = 0 — กฎนี้จึงเป็นแค่ "ค่าตั้งต้น" ที่คลาสหรือ inline
   style ของหน้าไหนก็ทับได้ตามปกติ ไม่ไปแย่งสีพื้นที่ใครตั้งใจตั้งไว้แล้วสักที่เดียว
   🔴 ไม่ครอบ checkbox/radio/range/color/file/ปุ่ม — พวกนั้นเบราว์เซอร์วาดรูปทรงของมันเอง
   การไปตั้งสีพื้นให้ทำให้หน้าตาเพี้ยนแทนที่จะช่วย */
input:where([type="text"],[type="search"],[type="tel"],[type="email"],[type="url"],[type="password"],
            [type="number"],[type="date"],[type="time"],[type="month"],[type="week"],
            [type="datetime-local"],:not([type])),
select, textarea{ background-color:var(--surface); }
/* 🔴 สีของ placeholder ต้องมาจากธีม ไม่ใช่ค่าเริ่มต้นของเบราว์เซอร์ — Chromium ฝัง #757575 ไว้ตายตัว
   **ไม่ขยับตาม `color-scheme` ด้วย** (วัดจริง: บนพื้นธีมมืดได้คอนทราสต์ 3.75 ต่ำกว่าเกณฑ์อ่านออก 4.5)
   ใช้ `--muted` ซึ่งเป็นสีข้อความรองของระบบอยู่แล้ว — ได้ค่าที่อ่านออกทั้งสองธีมและเข้าชุดกับคำอธิบาย
   ใต้ช่องกรอกที่ใช้สีเดียวกัน · `opacity:1` จำเป็นเพราะ Firefox หรี่ placeholder ให้เองโดยปริยาย */
::placeholder{ color:var(--muted); opacity:1; }
a{ color:var(--brand); text-decoration:none; }
a:hover{ color:var(--brand); text-decoration:underline; }

/* ตัวเลข/ตารางใช้ tabular-nums เสมอ (กันตัวเลขเลื่อนตอนโหลดข้อมูลใหม่) */
.num, table{ font-variant-numeric: tabular-nums; }

/* ตารางกว้าง scroll ในกล่องตัวเอง ห้าม body scroll แนวนอน */
.table-scroll{ overflow-x:auto; -webkit-overflow-scrolling:touch; }

@keyframes spin{ to{ transform:rotate(360deg); } }
@keyframes fadeUp{ from{ opacity:0; } to{ opacity:1; } }
@keyframes sheetUp{ from{ opacity:.35; } to{ opacity:1; } }
@keyframes softPulse{ 0%,100%{ opacity:1; } 50%{ opacity:.45; } }
/* drawer เมนูซ้ายบนมือถือ (view/nav-more.js — 15 ส.ค. 2569) เลื่อนเข้าจากขอบซ้ายแบบ sidebar */
@keyframes drawerIn{ from{ transform:translateX(-100%); opacity:.6; } to{ transform:translateX(0); opacity:1; } }

@media print{
  @page{ size:A4; margin:12mm; }
  /* ใบเสนอราคาใช้กระดาษเต็มแผ่น แล้วเว้นขอบในเอกสารเอง: Chrome จึงไม่แทรก URL/วันที่ในหัวท้าย */
  @page quote{ size:A4; margin:0; }
  body *{ visibility:hidden !important; }
  [data-print-area],[data-print-area] *{ visibility:visible !important; }
  [data-print-area]{ position:fixed !important; left:0 !important; top:0 !important; width:100% !important; max-height:none !important; overflow:visible !important; box-shadow:none !important; border-radius:0 !important; padding:0 !important; }
  [data-print-hide]{ display:none !important; }

  /* เอกสารจากหน้าใบงานต้องออกจากกรอบ modal เพื่อให้ A4 เต็มหน้าและตารางต่อหน้าถัดไปได้ */
  body:has([data-doc-print-root]) > :not([data-doc-print-root]){ display:none !important; }
  body:has([data-doc-print-root]){ background:#fff !important; }
  [data-doc-print-root]{ position:static !important; inset:auto !important; display:block !important; background:#fff !important; animation:none !important; }
  [data-doc-print-sheet]{ width:100% !important; max-height:none !important; overflow:visible !important; padding:0 !important; margin:0 !important; border:0 !important; border-radius:0 !important; box-shadow:none !important; animation:none !important; }
  [data-doc-print-sheet] > div:first-child{ display:none !important; }
  [data-doc-print-sheet] [data-sheetview-body],
  [data-doc-print-sheet] .workdoc-wrap{ width:100% !important; overflow:visible !important; }
  [data-doc-print-sheet] [data-print-area]{ position:static !important; width:100% !important; min-width:0 !important; min-height:270mm !important; padding:0 !important; zoom:1 !important; border:0 !important; border-radius:0 !important; }
  [data-doc-print-sheet] [data-print-area] *{ print-color-adjust:exact !important; -webkit-print-color-adjust:exact !important; }

  /* ใบเสนอราคาต้องอยู่ใน document flow จริง มิฉะนั้นหน้าที่สองว่างและตารางถูกตัด
     เพราะกฎเอกสารทั่วไปด้านบนวางเนื้อหาเป็น fixed ใน modal ที่เลื่อนได้ */
  body:has([data-quote-print-root]) > :not([data-quote-print-root]){ display:none !important; }
  body:has([data-quote-print-root]){ page:quote; background:#fff !important; }
  [data-quote-print-root]{ position:static !important; inset:auto !important; display:block !important; background:#fff !important; animation:none !important; }
  [data-quote-print-root] > [data-doc-quote-sheet]{ width:100% !important; max-height:none !important; overflow:visible !important; padding:0 !important; margin:0 !important; border:0 !important; box-shadow:none !important; animation:none !important; }
  [data-quote-print-root] > [data-doc-quote-sheet] > div:first-child{ display:none !important; }
  [data-quote-print-root] [data-sheetview-body],
  [data-quote-print-root] [data-quote-scroll]{ width:100% !important; max-width:none !important; overflow:visible !important; }
  [data-quote-print-root] [data-quote-scroll]{ display:block !important; padding:0 !important; background:#fff !important; border-radius:0 !important; }
  [data-quote-print-root] [data-doc-quote]{ position:static !important; width:100% !important; min-width:0 !important; max-width:none !important; min-height:260mm !important; padding:12mm !important; box-sizing:border-box !important; border:0 !important; border-radius:0 !important; box-shadow:none !important; zoom:1 !important; break-after:auto !important; }
  [data-quote-print-root] [data-quote-bottom]{ margin-top:auto !important; }
  [data-quote-print-root] [data-doc-quote] > div{ break-inside:avoid; }
  [data-quote-print-root] [data-quote-items]{ overflow:visible !important; break-inside:auto !important; }
  [data-quote-print-root] [data-quote-item]{ break-inside:avoid; }
  [data-quote-print-root] [data-doc-quote] *{ print-color-adjust:exact !important; -webkit-print-color-adjust:exact !important; }
}

[data-doc-print-sheet]{ width:min(900px,98vw) !important; max-height:96dvh !important; }
[data-doc-print-sheet] [data-sheetview-body]{ min-width:0; overflow:auto; }
[data-doc-work] .workdoc-paper,
[data-doc-receipt] [data-print-area]{ width:794px; min-width:794px; min-height:1123px; box-sizing:border-box; padding:46px !important; background:#fff; color:#0f172a; }
[data-doc-receipt] [data-print-area]{ margin:auto; box-shadow:0 8px 30px #0f172a18; }
.workdoc-wrap{ display:flex; flex-direction:column; align-items:center; gap:12px; }
.workdoc-paper{ box-shadow:0 8px 30px #0f172a18; display:flex; flex-direction:column; gap:22px; font-size:13px; line-height:1.5; }
.workdoc-header{ display:flex; align-items:flex-start; justify-content:space-between; gap:22px; padding-bottom:18px; border-bottom:2px solid #0f172a; }
.workdoc-brand{ display:flex; align-items:center; gap:12px; min-width:0; }
.workdoc-brand strong{ display:block; font-size:16px; }
.workdoc-brand span{ display:block; font-size:11px; color:#64748b; }
.workdoc-logo{ width:52px; height:52px; object-fit:contain; flex:none; }
.workdoc-title{ text-align:right; flex:none; }
.workdoc-title h1{ margin:0; font-size:26px; line-height:1.2; }
.workdoc-title strong{ font-size:17px; color:#4f46e5; }
.workdoc-hero{ display:grid; grid-template-columns:minmax(0,1.5fr) repeat(2,minmax(0,1fr)); gap:10px; }
.workdoc-hero > div{ padding:12px 14px; border-radius:10px; background:#eef2ff; min-width:0; }
.workdoc-hero span,.workdoc-field span{ display:block; color:#64748b; font-size:11px; font-weight:700; }
.workdoc-hero strong{ display:block; font-size:17px; overflow-wrap:anywhere; }
.workdoc-hero small{ display:block; color:#475569; }
.workdoc-section{ break-inside:avoid; }
.workdoc-section h2{ margin:0 0 10px; padding-left:9px; border-left:4px solid #6366f1; font-size:14px; }
.workdoc-grid{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:10px 14px; }
.workdoc-field strong{ display:block; overflow-wrap:anywhere; }
.workdoc-table{ width:100%; border-collapse:collapse; font-size:12px; table-layout:fixed; }
.workdoc-table th{ background:#eef2ff; color:#334155; text-align:left; font-size:11px; }
.workdoc-table th,.workdoc-table td{ padding:9px 8px; border-bottom:1px solid #e2e8f0; vertical-align:top; overflow-wrap:anywhere; }
.workdoc-table th:nth-child(1){ width:6%; }.workdoc-table th:nth-child(2){ width:29%; }
.workdoc-table th:nth-child(3){ width:9%; }.workdoc-table th:nth-child(4){ width:16%; }
.workdoc-table th:nth-child(5){ width:29%; }.workdoc-table th:nth-child(6){ width:11%; text-align:right; }
.workdoc-table td.workdoc-qty{ text-align:right; font-weight:700; }
.workdoc-table small{ display:block; color:#64748b; }
.workdoc-table tr{ break-inside:avoid; }
.workdoc-note{ white-space:pre-wrap; overflow-wrap:anywhere; margin:0; padding:12px 14px; background:#f8fafc; border-radius:9px; }
.workdoc-address{ margin:10px 0 0; overflow-wrap:anywhere; }
.workdoc-footer{ margin-top:auto; padding-top:10px; border-top:1px solid #cbd5e1; color:#64748b; font-size:11px; }
.workdoc-actions{ display:flex; width:min(794px,100%); gap:9px; }
.workdoc-actions button{ min-height:44px; border:1px solid var(--border); border-radius:10px; padding:0 18px; font-weight:700; }
.workdoc-actions button:last-child{ flex:1; background:var(--brand); color:#fff; }
@media print{
  [data-doc-print-sheet] [data-doc-work] .workdoc-paper,
  [data-doc-print-sheet] [data-doc-receipt] [data-print-area]{ min-width:0 !important; padding:0 !important; box-shadow:none !important; }
  .receipt-lines{ overflow:visible !important; break-inside:auto !important; }
  .receipt-line{ break-inside:avoid; }
  .workdoc-section:has(.workdoc-table){ break-inside:auto; }
  .workdoc-table thead{ display:table-header-group; }
}

[data-doc-quote-sheet]{ width:min(1000px,98vw) !important; max-height:96dvh !important; }
[data-doc-quote-sheet] [data-sheetview-body]{ min-width:0; }
@media screen and (max-width:759px){
  [data-doc-quote-sheet] [data-quote-scroll]{ overflow-x:visible !important; }
  [data-doc-quote]:not([data-quote-export]){ width:100% !important; min-width:0 !important; padding:14px !important; gap:14px !important; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-header]{ display:grid !important; grid-template-columns:48px minmax(0,1fr) !important; gap:9px !important; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-logo]{ width:48px !important; height:48px !important; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-meta]{ grid-column:1 / -1; text-align:left !important; display:grid; grid-template-columns:1fr 1fr; gap:1px 8px; border-top:1px solid #e4e8ef; padding-top:8px; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-meta] > div:first-child{ display:none; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-meta] > div:nth-child(2){ grid-column:1 / -1; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-customer]{ grid-template-columns:1fr !important; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-table-head]{ display:none !important; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-item]{ grid-template-columns:minmax(0,1fr) auto !important; gap:3px 8px; padding:9px 11px; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-item] > div{ padding:0 !important; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-item] > div:first-child{ grid-column:1 / -1; font-size:13.5px; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-item] > div:nth-child(3)::before{ content:'จำนวน '; color:#64748b; font-family:'Sarabun',sans-serif; font-weight:400; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-item] > div:nth-child(4){ text-align:left !important; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-item] > div:nth-child(4)::before{ content:'ราคา/ตัว '; color:#64748b; font-family:'Sarabun',sans-serif; font-weight:400; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-item] > div:nth-child(5)::before{ content:'รวม '; color:#64748b; font-family:'Sarabun',sans-serif; font-weight:400; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-totals]{ align-items:stretch !important; }
  [data-doc-quote]:not([data-quote-export]) [data-quote-totals] > div{ justify-content:space-between !important; width:100%; gap:8px !important; }
}

/* ---------- app shell: sidebar / bottom-tabs breakpoint (760px ตามที่ dc.html ใช้) ---------- */
#shell-sidebar-slot aside{ transition:width .22s cubic-bezier(.4,0,.2,1); }
@media (max-width:759px){
  #shell-sidebar-slot{ display:none; }
}
@media (min-width:760px){
  #shell-bottomtabs-slot{ display:none; }
}

/* ของใน header ที่ต้องหายไปบนมือถือเพื่อคืนที่ให้ "ชื่อหน้า" (วัดจริงที่ 320px: กล่องชื่อหน้าเหลือ
   กว้าง 12-16px = เห็นแค่ตัวอักษรเดียวแล้วจุดไข่ปลา) — ตัว attribute ถูกเติมผ่าน PATCHES.shell.
   renderHeader ใน tools/dc2vanilla.js **ไม่ใช่แก้ไฟล์ generated ด้วยมือ** และ PATCHES จะ throw
   ทันทีถ้าต้นฉบับใน dc.html เปลี่ยนไป (กันกรณีกฎ CSS นี้ค้างอยู่แต่ไม่มี element ไหนตรงแล้วเงียบ ๆ) */
@media (max-width:759px){
  [data-hide-mobile]{ display:none !important; }
}

/* คู่ตรงข้ามของ data-hide-mobile — ของที่มี "เฉพาะบนมือถือ" (ปุ่ม ☰ เปิดเมนูซ้าย · 15 ส.ค. 2569)
   ต้อง !important เพราะปุ่มระบุ display ใน inline style · ค่าตอนโชว์เป็น grid เพราะผู้ใช้ตัวเดียว
   ของกติกานี้คือปุ่มไอคอนที่จัดกลางด้วย place-items */
[data-only-mobile]{ display:none !important; }
@media (max-width:759px){
  [data-only-mobile]{ display:grid !important; }
}

/* ปุ่มทุกปุ่มอย่างน้อย 44px (กติกาที่ล็อกไว้ในแผน) — บังคับ floor ไว้เผื่อ inline style ระบุน้อยกว่า */
button, .btn{ min-height:44px; }
button.btn-inline{ min-height:0; } /* ปุ่มเล็กในตาราง/แถวที่ตั้งใจให้เล็กกว่า 44 — ใช้ class นี้ยกเว้นเป็นราย ๆ */

/* ปุ่มลิงก์ในเนื้อหา (เลขออเดอร์ที่กดเปิดได้ ฯลฯ) — ต้องดูออกว่ากดได้ ไม่ใช่ข้อความสีน้ำเงินเฉย ๆ
   (6 ก.ย. 2569 เจ้าของทัก "ปุ่มมันควรมี icon/ดูออกว่าเป็นปุ่ม") */
button.lnk{ color:var(--brand); font-weight:700; text-decoration:underline; text-decoration-thickness:1.5px;
  /* 🔴 ระยะเส้นใต้ 5px ไม่ใช่ 3px — ที่ 3px เส้นตัดผ่าน **สระล่างของไทย** (ดู/ปู/สู้) จนอ่านเป็น
     คนละตัว (ซูมดูจริงที่ 3x บนปุ่ม "ดู User Agent ฉบับเต็ม" เมื่อ 26 ก.ย. 2569 แล้วเห็นชัด)
     · ตระกูลเดียวกับบทเรียน 20 ก.ย. 2569 ที่ตัวอักษรไล่สีตัดสระอุใต้คำว่า "ชุด" ทิ้ง
     · เส้นใต้เป็นการตกแต่ง ไม่กินที่ในกล่อง ขยับลงแล้วเลย์เอาต์ไม่ขยับสักพิกเซล */
  text-underline-offset:5px; text-decoration-color:color-mix(in oklab,var(--brand) 45%,transparent); border-radius:6px; padding:0 3px; }
button.lnk:hover{ background:var(--brand-soft); text-decoration-color:var(--brand); }
/* ปุ่มเล็กในแถว/การ์ด (แก้ · เปลี่ยน · คัดลอก) — กรอบ+พื้นอ่อนให้เห็นว่าเป็นปุ่ม */
/* 8 ก.ย. 2569: `label.chip-btn` ด้วย — ปุ่มอัปโหลดไฟล์ทั้งระบบเป็น <label> ครอบ <input type=file>
   ที่ซ่อนไว้ (ปุ่มแนบสลิป · อัปโหลดรูปแบบคอ · อัปโหลดโลโก้ร้าน) ถ้า selector จับแต่ <button>
   ปุ่มพวกนี้จะกลายเป็นข้อความเปล่า ๆ ที่ไม่รู้ว่ากดได้ — บั๊กเดียวกับที่เจ้าของทักเรื่องปุ่ม "แก้" */
button.chip-btn, label.chip-btn{ display:inline-flex; align-items:center; gap:5px; min-height:32px; padding:0 10px; border-radius:8px;
  border:1px solid color-mix(in oklab,var(--brand) 35%,var(--border)); background:var(--brand-soft); color:var(--brand);
  font-size:12.5px; font-weight:600; white-space:nowrap; }
button.chip-btn:hover, label.chip-btn:hover{ border-color:var(--brand); }

/* ไอคอนหน้า "ปุ่มทำรายการ" — core/actionicons.js แทรก <span class="btn-ico"> ให้อัตโนมัติจากแผนที่
   data-act -> ไอคอน (เจ้าของสั่ง 8 ก.ย. 2569 "พวกปุ่มทำรายการ อยากให้เพิ่ม icon เข้ามาด้วย")
   ขนาดผูกกับ font-size ของปุ่มเอง (ปุ่มเล็ก 12.5px ได้ไอคอน ~12.7px · ปุ่มปกติ ~14px)
   🔴 เป็น inline-block + margin ไม่ใช่ flex — ตั้งใจ **ไม่แตะ display ของปุ่ม** เพราะปุ่มในระบบนี้มีทั้ง
   inline-flex/grid/block ที่ตั้ง style ไว้เอง เปลี่ยน display = พังทีละที่โดยไม่มีอะไรฟ้อง
   (ปุ่มที่เป็น flex และมี gap อยู่แล้ว actionicons.js จะไม่ใส่ margin ให้ ไม่งั้นห่างเป็น 2 เท่า) */
.btn-ico{ display:inline-block; width:1.02em; height:1.02em; flex:none; vertical-align:-0.13em;
  background:currentColor; mask:var(--ic) center/contain no-repeat; }

/* โลโก้ LINE บนปุ่ม "เข้าสู่ระบบด้วย LINE" (17 ก.ย. 2569 · เจ้าของทัก "ไปเอา logo มาใส่ให้หน่อย มันไม่สวย"
   — เดิมเป็นสี่เหลี่ยมเขียวเปล่า ๆ ที่ prototype วาดไว้แทนโลโก้)
   🔴 ต่างจาก .btn-ico ตรงที่ **ห้ามใช้ mask** — mask ระบายด้วย currentColor ซึ่งจะได้โลโก้สีเดียว
   โลโก้แบรนด์ต้องคงสีของตัวเอง (เขียว #06C755 + ฟองขาว) จึงเป็น background-image เต็มรูป
   ฝังเป็น data-URI เพราะ CSP script-src/img ของระบบไม่ให้ดึงจาก CDN ภายนอก (กติกาข้อ 3) */
/* ---------- ป้ายสถานะ (badge) — core/badge.js เป็นคนวาด ที่เดียวของระบบ ----------
   เจ้าของสั่ง 17 ก.ย. 2569: "สถานะ ก็อยากให้ใช้ Badge เพื่อให้แยกง่าย ๆ หน่อยนะ"
   🔴 พื้นหลัง 18% ไม่ใช่ 13% แบบ chip() เดิม — ดูจริงแล้ว 13% จางจนป้ายคนละสถานะกลมกลืนกันหมด
   เมื่อมองผ่าน ๆ · เส้นขอบสีเดียวกันจาง ๆ ช่วยตัดขอบป้ายออกจากพื้นการ์ดโดยไม่ต้องเพิ่มความเข้ม
   จนป้ายแย่งความสนใจไปจากชื่อแถว (ซึ่งเป็นสิ่งที่ต้องอ่านก่อน) */
.badge{ display:inline-flex; align-items:center; gap:5px; white-space:nowrap; vertical-align:middle;
  border-radius:999px; padding:3px 10px; font-size:11.5px; font-weight:700; line-height:1.45;
  background:color-mix(in oklab, var(--bc) 18%, var(--surface));
  color:var(--bc); border:1px solid color-mix(in oklab, var(--bc) 30%, transparent); }
.badge-dot{ width:6px; height:6px; flex:none; border-radius:50%; background:currentColor; }
.badge-solid{ background:var(--bc); color:#fff; border-color:transparent; }
/* ธีมมืด: ตัวอักษรสีเดิมมืดเกินบนพื้นเข้ม — ดันให้สว่างขึ้นแทนการเปลี่ยนสีทีละจุดในโค้ด */
html[data-theme="dark"] .badge{ color:color-mix(in oklab, var(--bc) 62%, #ffffff);
  background:color-mix(in oklab, var(--bc) 26%, var(--surface));
  border-color:color-mix(in oklab, var(--bc) 42%, transparent); }
html[data-theme="dark"] .badge-solid{ color:#0a0f18; background:color-mix(in oklab, var(--bc) 78%, #ffffff); }

.line-logo{ display:inline-block; width:20px; height:20px; flex:none; vertical-align:-0.28em;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect width='24' height='24' rx='6' fill='%2306C755'/%3E%3Cpath fill='%23fff' d='M12 4.35c-4.23 0-7.67 2.79-7.67 6.22 0 3.07 2.73 5.64 6.41 6.13.25.05.59.16.68.38.08.19.05.5.03.69l-.11.66c-.03.19-.15.76.67.42.82-.35 4.44-2.61 6.06-4.48 1.11-1.22 1.65-2.46 1.65-3.8 0-3.43-3.44-6.22-7.72-6.22z'/%3E%3C/svg%3E") center/contain no-repeat; }

/* ---------- วงกลมรูปโปรไฟล์ (sql/29 · 26 ก.ย. 2569) ----------
   เจ้าของสั่ง: "ตอนที่เชื่อมไลน์แล้ว อยากให้เอารูปโปรไฟล์ ไลน์มาแสดงให้ด้วยนะ ที่มันจะแสดงอยู่
   header 'เจ้าของร้าน' แล้วมันจะมี ตัวเอาไว้แสดงรูปอยู่" — วงกลมนั้นมี 3 ที่ (หัวจอ · ท้าย sidebar ·
   ท้าย drawer มือถือ) ทั้งสามใช้กติกาชุดนี้ร่วมกัน

   🔴 **ขนาด/สีของวงกลมยังเป็น inline style ของ view เหมือนเดิม** (32px ที่ sidebar/drawer · 26px ที่
      หัวจอ) คลาสนี้เติมแค่ 2 อย่างที่ inline ทำไม่ได้: กรอบให้รูปวางทับ + ตัดมุมรูปให้กลมตามวงกลม
      — ย้ายขนาดมาไว้ที่นี่ = มีที่มาของขนาดสองแห่ง แล้ววันที่แก้ inline จะไม่มีผลโดยไม่มีอะไรฟ้อง

   🔴 **อักษรแรกอยู่ใน DOM เสมอ แล้วเอารูปวางทับ** ไม่ใช่ "มีรูปแล้วไม่ต้องวาดอักษร" — รูปมาจาก CDN
      ของ LINE ซึ่งโหลดไม่สำเร็จได้จริง (เน็ตร้านสะดุด · ที่อยู่รูปหมดอายุ · LINE ล่ม) วันนั้นต้อง
      เหลืออักษรแรกให้เห็น ไม่ใช่วงกลมว่างเปล่า · ตัว <img> ที่โหลดไม่สำเร็จถูกถอดออกด้วย listener
      'error' ใน main.js (CSP `script-src 'self'` ห้าม onerror ใน HTML — กติกาข้อ 3) */
.avatar{ position:relative; overflow:hidden; }
.avatar-img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  /* 🔴 พื้นทึบ: รูปที่มีพื้นหลังโปร่งใสจะเห็นอักษรแรกที่อยู่ข้างหลังทะลุขึ้นมาปนกับรูป (เจอจริง
     ตอนทดสอบด้วยไฟล์ PNG โปร่งใส) — LINE เสิร์ฟรูปโปรไฟล์เป็นภาพถ่ายทึบอยู่แล้ว แต่กันไว้
     ด้วยบรรทัดเดียวถูกกว่ามานั่งหาทีหลังว่าทำไมวงกลมของบางคนมีตัวหนังสือซ้อนอยู่บนรูป */
  background:var(--brand-soft); }

/* sidebar บนจอเตี้ย (≤ 940px): เจ้าของร้านมี 18 เมนู + การ์ดโปรไฟล์ — ที่ความสูงเมนูปกติ (42px) เมนู
   2 ตัวสุดท้ายหลุดไปอยู่ใต้การ์ดโปรไฟล์โดยไม่มีสัญญาณว่าเลื่อนได้ (เห็นจริงใน walkthrough 6 ก.ย. 2569
   ที่ 1440×900) → ย่อความสูงแถวให้ 18 เมนูพอดีจอ · inline style ของ view ตั้ง min-height/padding ไว้
   จึงต้อง !important (view เป็นไฟล์ generated) */
@media (max-height:940px){
  /* sql/20 (8 ก.ย. 2569): เมนู OWNER/MANAGER เป็น 20 แถวแล้ว (เพิ่มเคลม+ใบเสนอราคา) — 36px×20 ล้นจอ 900px
     ไป ~23px ทำให้แถวสุดท้ายซ่อนใต้การ์ดโปรไฟล์ (บั๊กเดิม 7 ก.ย.) → ย่อเป็น 34px/ช่องไฟ 1px = 699px พอดี */
  #shell-sidebar-slot nav > button[data-act="n.go"]{ min-height:34px !important; padding-top:5px !important; padding-bottom:5px !important; }
  #shell-sidebar-slot nav{ gap:1px !important; }
}

/* ---------- ทุกหน้าที่มีมุมมองตาราง desktop / การ์ด mobile คู่กัน (คิวงาน T1.2, กระดานผลิต T1.3):
   สลับด้วย breakpoint เดียวกับ sidebar (759px) ล้วน ๆ — เลี่ยงพึ่ง JS คำนวณ vw (แพตเทิร์นเดียวกับ
   sidebar/bottom-tabs ด้านบน) ชื่อ class ตั้งกลาง ๆ (ไม่ผูกกับ "queue") เพราะใช้ร่วมกันหลายหน้า ---------- */
.fe-desktop-rows{ display:block; }
.fe-mobile-rows{ display:none; }
@media (max-width:759px){
  .fe-desktop-rows{ display:none; }
  .fe-mobile-rows{ display:block; }
}
/* ---------- ไฮไลต์ "แถวที่เมาส์ชี้อยู่" ทั้งระบบ (เจ้าของสั่ง 17 ก.ย. 2569:
   "เวลาเอาเมาส์ไป hover ตาราง อยากให้มีสี hover ด้วย เพื่อที่ว่าเราจะได้โฟกัสได้ถูกตาราง") ----------
   แถวในหน้าจัดการยาวข้ามจอ (ยอดค้างชำระกว้าง 820px · ตารางลงเวลากว้างเท่าจำนวนวันในเดือน) ตากวาด
   จากชื่อทางซ้ายไปหาตัวเลขทางขวาแล้วหลุดไปคนละแถวได้ง่ายมาก — ไฮไลต์ทั้งแถวคือตัวจับสายตา
   🔴 **ใช้ ::after ทับ ไม่ใช่ตั้ง background ของแถวเอง** เพราะแถวในระบบนี้ตั้งสีพื้นเป็น inline style
   กันเกือบหมด (แถวที่เลือกอยู่ = brand-soft · ของใกล้หมด = ส้มจาง · แถวที่ตีกลับ = จาง) ซึ่ง inline
   ชนะ class เสมอ — ถ้าตั้ง background จะต้องใส่ !important แล้ว**สีเดิมของแถวหายไปตอน hover**
   (แถวที่เลือกอยู่จะไม่เหลือสีบอกว่าเลือกอยู่) · ชั้นทับโปร่งจึงคงสีเดิมไว้ใต้ตัวเองได้ทั้งหมด
   🔴 ห่อใน `@media (hover:hover)` เพราะบนมือถือ :hover **ค้างหลังแตะ** — แถวที่เพิ่งกดจะไฮไลต์
   ค้างไว้เหมือนยังเลือกอยู่ทั้งที่เปลี่ยนหน้าไปแล้ว */
:root{ --row-hv:color-mix(in oklab, #0f172a 6%, transparent); }
html[data-theme="dark"]{ --row-hv:color-mix(in oklab, #ffffff 8%, transparent); }
@media (hover:hover) and (pointer:fine){
  /* ตารางจริง (`<table>`) — รายงาน · ตารางลงเวลา · สรุปค่าจ้าง · รายการสินค้าในใบงาน · ใบเสนอราคา
     ระบายที่ <td> ไม่ใช่ <tr> เพราะพื้นของ cell ทับพื้นของ row เสมอ */
  tbody tr:hover > td{ background:var(--row-hv); }
  /* แถวที่ประกอบจาก <div>/<button> (ลิสต์ในหน้าจัดการเกือบทั้งหมด) — ติด class `row-hv` ที่ตัวแถว */
  .row-hv{ position:relative; }
  .row-hv:hover::after{ content:''; position:absolute; inset:0; border-radius:inherit;
    background:var(--row-hv); pointer-events:none; }
}

.hv-drag-row:hover{ background:var(--surface2) !important; }
/* is-dragging/is-drop-target ใช้ทั้งแถวคิวงาน (T1.2) และคอลัมน์/การ์ดกระดานผลิต (T1.3) — ไม่ผูกกับ
   .hv-drag-row เพราะ drop target ของกระดานผลิตคือ "คอลัมน์" ทั้งคอลัมน์ ไม่ใช่การ์ด (เลยไม่มี class นั้น) */
.is-dragging{ opacity:.4; }
.is-drop-target{ outline:2px dashed var(--brand); outline-offset:-2px; }
/* การ์ดที่เพิ่งเปลี่ยนสถานะสำเร็จบนกระดานผลิต — กะพริบขอบ ~3 วิ ให้ผู้ใช้เห็นว่ามันไปอยู่คอลัมน์ไหน
   (เจ้าของทัก 9 ก.ย. 2569: วาดกระดานใหม่แล้วหาใบที่เพิ่งย้ายไม่เจอ) · ใช้ box-shadow ไม่ใช่ border/
   outline-width เพราะการ์ดมี box-shadow inline อยู่แล้ว และ animation ชนะ inline style ตามลำดับ
   cascade — ถ้าใช้ border จะดันขนาดการ์ดทำให้คอลัมน์ขยับทั้งแถว */
@keyframes cardJustMoved{
  0%, 60%{ box-shadow:0 0 0 3px color-mix(in oklab, var(--brand) 55%, transparent), var(--shadow); }
  100%{ box-shadow:0 0 0 0 transparent, var(--shadow); }
}
.is-just-moved{ animation:cardJustMoved 3s ease-out both; }

/* ---------- หน้าแรกผู้จัดการ + การเดินเรื่องของใบงาน (23–26 ก.ย. 2569) ---------- */
.nav-group-label { padding:14px 11px 5px; font-size:12px; font-weight:700; color:var(--muted); }
.nav-group-divider { margin:8px 6px 4px; border-top:1px solid var(--border); }
/* ปุ่มช่วยเหลือ: กล่องกดจริง 44px แต่วงกลมที่เห็น 26px; pseudo-element ตกแต่งอย่างเดียว */
.help-dot { position:relative; }
.help-dot::before { content:''; position:absolute; inset:9px; border:1px solid var(--border); border-radius:50%; pointer-events:none; }
.help-dot:focus-visible { outline:2px solid var(--brand); outline-offset:-3px; }
.order-task-nav { display:flex; flex-wrap:wrap; gap:4px; background:var(--surface2); border-radius:12px; padding:5px; }
.order-task-nav button { min-height:44px !important; }
.order-task-panels { display:flex; flex-direction:column; gap:14px; max-width:1120px; min-width:0; margin-top:14px; }
.order-detail-card { min-width:0; overflow:hidden; background:var(--surface); border:1px solid var(--border); border-radius:16px; box-shadow:var(--shadow); }
.order-detail-card .order-task-nav { border-radius:0; }
.order-detail-body { min-width:0; display:flex; flex-direction:column; }
.order-detail-body > :is(div,section) { min-width:0; border:0 !important; border-radius:0 !important; box-shadow:none !important; }
.order-detail-body .order-task-panels { max-width:none; margin-top:0; }
.order-detail-body .order-task-panels > :is(div,section) { border:0 !important; border-radius:0 !important; box-shadow:none !important; }
.order-detail-body > :not(:first-child), .order-detail-body .order-task-panels > :not(:first-child) { border-top:1px solid var(--border) !important; }
.dash-actions { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; }
@media (min-width:1400px) { .dash-actions { grid-template-columns:repeat(4,minmax(0,1fr)); } }
@media (max-width:540px) { .dash-actions { grid-template-columns:1fr; } }
.dash-action { display:flex; flex-direction:column; gap:8px; padding:15px; min-width:0; border:1px solid var(--border); border-radius:12px; text-align:left; background:var(--surface); }

.order-quick-filters { display:flex; flex-wrap:wrap; gap:8px; margin-bottom:12px; }
.order-quick-filters button { min-height:44px; padding:8px 14px; }
.order-quick-filters [aria-pressed="true"] { background:var(--brand-soft); color:var(--brand); border-color:var(--brand); }
/* ── "ใบงานที่ต้องติดตาม" บนหน้าภาพรวม (26 ก.ย. 2569 — เจ้าของสั่ง "ให้ชื่อผู้รับผิดชอบขั้นปัจจุบัน
   เด่น อ่านง่าย และแยก 'เปิดใบงาน' เป็นปุ่มที่เห็นชัดและกดได้จริงทั้งคอม/มือถือ") ────────────
   🔴 **การ์ดเป็น <article> ไม่ใช่ <button> ทั้งใบ** — ปุ่มจริงมีปุ่มเดียวคือ "เปิดใบงาน"
      (ปุ่มซ้อนปุ่มเป็น HTML ที่ใช้ไม่ได้ และทำให้คนกดโดนคนละอย่างกับที่ตั้งใจ)
   🔴 **ชื่อผู้รับผิดชอบเป็นข้อความที่ใหญ่ที่สุดในการ์ด (16px/700)** ใหญ่กว่าเลขใบและสถานะ —
      เจ้าของสั่งเอง: สิ่งที่ผู้จัดการต้องรู้ก่อนคือ "ตอนนี้ใบนี้อยู่ในมือใคร" แล้วค่อยไปตามคนนั้น
   🔴 **ปุ่มสูงอย่างน้อย 44px และมีพื้นทึบสีแบรนด์** — ของเดิมอ่านไม่ออกว่ากดได้ (บทเรียนเดิม
      7 ก.ย. 2569: "ปุ่มที่เป็นข้อความสีน้ำเงินเฉย ๆ ผ่านเทสทุกข้อแต่ผู้ใช้ไม่รู้ว่ากดได้")
   🔴 **ยุบเหลือคอลัมน์เดียวที่ ≤600px** และ overflow-wrap:anywhere เพราะชื่อทีมยาวได้ไม่จำกัด */
.urgent-order-list { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; }
.urgent-order { display:flex; flex-direction:column; gap:6px; min-width:0; padding:14px; border:1px solid var(--border); border-radius:12px; background:var(--surface); font-size:14px; overflow-wrap:anywhere; }
.urgent-order-footer { display:flex; align-items:flex-end; justify-content:space-between; flex-wrap:wrap; gap:12px; margin-top:auto; padding-top:11px; border-top:1px solid var(--border); }
.urgent-order-holder { display:flex; flex-direction:column; gap:3px; min-width:0; }
.urgent-order-holder-label { color:var(--muted); font-size:12.5px; font-weight:600; }
.urgent-order-holder-name { color:var(--text); font-size:16px; font-weight:700; line-height:1.35; overflow-wrap:anywhere; }
.urgent-order-holder-name.is-unassigned { color:#b45309; }
.urgent-order-reason { font-weight:700; }
.urgent-order-reason.is-bad { color:#dc2626; }   /* เลยกำหนดแล้ว */
.urgent-order-reason.is-warn { color:#b45309; }  /* ถึงกำหนดวันนี้ */
/* ธีมมืด: สีเดิมมืดเกินไปบนพื้นเข้ม (วัดจริง #dc2626 = 3.58 · #b45309 = 3.44 ต่ำกว่าเกณฑ์อ่านออก
   4.5) — ดันให้สว่างขึ้นแทนการเปลี่ยนสีทีละจุดในโค้ด แพทเทิร์นเดียวกับ .badge ด้านบนของไฟล์นี้ */
html[data-theme="dark"] .urgent-order-reason.is-bad { color:#f87171; }
html[data-theme="dark"] .urgent-order-reason.is-warn,
html[data-theme="dark"] .urgent-order-holder-name.is-unassigned { color:#fbbf24; }
.urgent-order-open { display:inline-flex; align-items:center; justify-content:center; gap:8px; padding:0 14px; border:1px solid var(--brand); border-radius:9px; background:var(--brand); color:#fff; font-size:14px; font-weight:700; white-space:nowrap; }
@media (max-width:600px) { .urgent-order-list { grid-template-columns:1fr; } }

/* 🔴 :hover ทุกตัวต้องอยู่ใน @media (hover:hover) and (pointer:fine) เสมอ — บนจอสัมผัส :hover
   **ค้างหลังแตะ** ปุ่มที่เพิ่งกดจะหรี่ค้างไว้จนกว่าจะไปแตะที่อื่น (กติกาเดิมตั้งแต่ทำ .row-hv
   เมื่อ 17 ก.ย. 2569 · แท็บเล็ตของแคชเชียร์กับมือถือหน้างานเป็นเครื่องหลักของร้านนี้) */
@media (hover:hover) and (pointer:fine){
  .dash-action:hover{ border-color:var(--brand); background:var(--brand-soft); }
  .urgent-order-open:hover{ filter:brightness(.92); }
}

/* 23 ก.ย. 2569 — "งานของคุณบนใบนี้" บนหน้ารายละเอียดใบงาน + แผงเหตุผลที่งานติดขัด
   🔴 จัดเป็นกริดที่ยุบเหลือคอลัมน์เดียวเองบนมือถือ — คนหน้างานเปิดจากมือถือเป็นหลัก ต้องอ่านจบ
   โดยไม่ต้องเลื่อนแนวนอนและไม่ต้องเปิดแท็บอื่น (ข้อมูลทั้งใบยังอยู่ครบในแท็บเดิม ไม่มีอะไรถูกถอด) */
.od-focus { background:var(--surface); border:1px solid var(--border); border-radius:16px;
  box-shadow:var(--shadow); padding:16px; display:flex; flex-direction:column; gap:12px; min-width:0; }
.od-focus-h { font-size:15px; font-weight:700; }
.od-focus-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(190px,100%),1fr));
  gap:12px 16px; margin:0; }
.od-focus-item { min-width:0; }
.od-focus-item dt { font-size:12px; color:var(--muted); font-weight:600; }
.od-focus-item dd { margin:0; font-size:14.5px; font-weight:600; overflow-wrap:anywhere; text-wrap:pretty; }
.od-block { border-top:1px solid var(--border); padding-top:12px; display:flex; flex-direction:column; gap:10px; }
.od-block-now { display:flex; flex-wrap:wrap; align-items:center; gap:6px 10px;
  background:color-mix(in oklab,#e11d48 10%,var(--surface)); border:1px solid color-mix(in oklab,#e11d48 30%,var(--surface));
  border-radius:12px; padding:10px 12px; }
.od-block-tag { background:#e11d48; color:#fff; border-radius:999px; padding:2px 10px; font-size:11.5px; font-weight:700; flex:none; }
.od-block-why { font-size:14px; font-weight:700; color:#be123c; overflow-wrap:anywhere; }
.od-block-meta { font-size:12.5px; color:var(--muted); width:100%; overflow-wrap:anywhere; }
.od-block-err { font-size:13px; color:#dc2626; font-weight:600; }
.od-block-form { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr)); gap:10px; }
.od-block-f { display:flex; flex-direction:column; gap:4px; font-size:12px; color:var(--muted); font-weight:600; min-width:0; }
.od-block-f select, .od-block-f input { min-height:44px; border:1px solid var(--border); background:var(--surface);
  border-radius:10px; padding:0 11px; font-size:14px; color:var(--text); width:100%; }
.od-block-note { grid-column:1 / -1; }
.od-block-btns { display:flex; flex-wrap:wrap; gap:8px; grid-column:1 / -1; }
.od-block-btns button { min-height:44px; }
.od-block-save { background:var(--brand); color:#fff; border-radius:11px; padding:0 16px; font-weight:700; font-size:14px; }
.od-block-save[disabled] { opacity:.6; }

/* ช่องเลือกที่อยู่ใช้ตัวเลือกเดียวกับระบบ; ลูกศรต้องยังเห็นหลังเลือกค่าแล้ว */
input[readonly][data-address-part] {
  cursor:pointer;
  padding-right:34px !important;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") !important;
  background-repeat:no-repeat !important;
  background-position:right 11px center !important;
  background-size:16px !important;
}
input[readonly][data-address-part]:disabled { cursor:not-allowed; opacity:.65; }

/* หมวดที่มีหลายแท็บ: บนคอมคงปุ่มเต็มชุด บนมือถือเหลือปุ่มเดียวเหนือเมนูล่าง
   เพื่อไม่ให้แท็บหลายแถวบังข้อมูลและยังเปลี่ยนหมวดได้ตอนเลื่อนลงไปแล้ว */
.mobile-tab-trigger { display:none; }
.catalog-tab-strip [data-tab-ico] { border:1px solid var(--border); box-shadow:0 1px 2px rgb(8 12 20/.06); }
.catalog-tab-strip [data-tab-ico][aria-pressed="true"] { border-color:var(--brand); }
.order-task-nav [data-tab-ico],
[data-tab-ico^="staff."], [data-tab-ico^="leaves."] { border:1px solid var(--border); }
@media (max-width:759px) {
  .desktop-tab-strip, .order-task-nav { display:none !important; }
  .mobile-tab-trigger {
    display:flex; align-items:center; justify-content:center; gap:9px;
    position:fixed; left:50%; bottom:calc(78px + env(safe-area-inset-bottom));
    z-index:39; transform:translateX(-50%);
    width:min(320px,calc(100vw - 32px)); min-height:50px; max-width:calc(100vw - 32px);
    padding:0 14px; border:1px solid var(--brand); border-radius:999px;
    background:var(--brand); color:#fff; box-shadow:0 8px 28px rgb(8 12 20/.25);
    font-size:14px; font-weight:700; line-height:1.2;
  }
  .mobile-tab-trigger-text { min-width:0; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
  #route-view:has(.mobile-tab-trigger) { padding-bottom:calc(160px + env(safe-area-inset-bottom)) !important; }
  /* ตัวกรองและปุ่มแก้รายการในตั้งค่าตัวเลือกเดิมสูงเพียง 30–34px กดพลาดง่ายบนจอสัมผัส */
  #route-view [data-act="f.go"], #route-view [data-act="catReorder"],
  #route-view [data-act="c.go"], #route-view [data-act="catToneOne"],
  #route-view [data-act="catToneTwo"], #route-view [data-act="r.edit"],
  #route-view [data-act="r.del"] { min-height:44px !important; }
}

/* ───────────────────────────────────────────────────────────────────────────
   เอฟเฟกต์ตอนลงเวลาเข้า-ออกงาน (1 ต.ค. 2569 — เจ้าของสั่งให้มีเสียง + เอฟเฟกต์)
   วาดโดย `public/js/core/feedback.js` แล้วลบตัวเองทิ้งหลังเล่นจบ
   🔴 ขยับแค่ `transform` กับ `opacity` เท่านั้น — 2 ตัวนี้เบราว์เซอร์ย้ายภาพที่วาดไว้แล้วได้เลย
   ไม่ต้องวาดใหม่ทุกเฟรม (กติกาเดียวกับอนิเมชันพื้นหลัง hero ของหน้าแรก 20 ก.ย. 2569)
   🔴 `pointer-events:none` ทั้งก้อน — เอฟเฟกต์ลอยทับปุ่มอยู่ ถ้ารับคลิกด้วยจะบังการกดครั้งถัดไป
   ─────────────────────────────────────────────────────────────────────────── */
.clock-burst{
  position:fixed; z-index:120; pointer-events:none;
  transform:translate(-50%,-50%);
  display:grid; place-items:center;
  --burst-c:#10b981;
}
.clock-burst[data-tone="warn"]{ --burst-c:#f59e0b; }
.clock-burst-ring{
  position:absolute; width:var(--burst-size,180px); height:var(--burst-size,180px);
  border-radius:50%; border:3px solid var(--burst-c);
  animation:clockBurstRing .72s cubic-bezier(.22,.9,.3,1) forwards;
}
.clock-burst-badge{
  position:relative;
  background:var(--burst-c); color:#fff;
  font-weight:700; font-size:14.5px; white-space:nowrap;
  padding:9px 15px; border-radius:999px;
  box-shadow:0 10px 26px color-mix(in oklab,var(--burst-c) 40%,transparent);
  animation:clockBurstBadge 1.05s ease-out forwards;
}
@keyframes clockBurstRing{
  from{ transform:scale(.72); opacity:.85; }
  to{ transform:scale(1.85); opacity:0; }
}
@keyframes clockBurstBadge{
  0%{ transform:translateY(8px) scale(.9); opacity:0; }
  18%{ transform:translateY(0) scale(1); opacity:1; }
  72%{ transform:translateY(0) scale(1); opacity:1; }
  100%{ transform:translateY(-14px) scale(.98); opacity:0; }
}
/* คนที่เปิด "ลดการเคลื่อนไหว" ยังต้องเห็นผลว่ากดติดแล้ว แค่ไม่ต้องมีอะไรวิ่ง */
.clock-burst[data-reduce]{ }
.clock-burst[data-reduce] .clock-burst-ring{ display:none; }
.clock-burst[data-reduce] .clock-burst-badge{ animation:none; }
@media (prefers-reduced-motion: reduce){
  .clock-burst-ring{ display:none; }
  .clock-burst-badge{ animation:none; }
}

/* ───────────────────────────────────────────────────────────────────────────
   หน้า "ลงเวลา" แท็บของตัวเอง — จอกว้างวาง 2 คอลัมน์ จอแคบซ้อนลงมา
   (1 ต.ค. 2569 เจ้าของสั่ง: "ถ้าในคอมแสดง ควรย้าย UI ไปข้าง ๆ แล้วถ้ามือถือหรือจอไม่พอค่อยย้ายลงมา")
   🔴 ใช้ `@container` ไม่ใช่ `@media` — เมนูข้างพับได้ และหน้านี้อยู่ในกล่องที่กว้างไม่เท่า viewport
   (จอ 1440 เหลือที่จริง 1,194px) · ถามความกว้างของ "ที่ที่มีจริง" จึงตรงกับที่เจ้าของสั่งว่า
   "ถ้าจอไม่พอค่อยย้ายลงมา" มากกว่าการเดาจากขนาดจอ
   🔴 `container-type:inline-size` ต้องอยู่ที่ `[data-att-me]` ซึ่งกว้างตามพ่อ (block เต็มความกว้าง)
   **ห้ามย้ายไปไว้ที่ตัวกริดเอง** — `@container` ถามขนาดตัวเองไม่ได้ และ inline-size containment
   แปลว่า "ห้ามดูเนื้อในตอนคิดความกว้าง" ซึ่งถ้าใส่ผิดที่กล่องจะยุบเหลือ 0 (บทเรียน `.f` 20 ก.ย. 2569)
   ─────────────────────────────────────────────────────────────────────────── */
[data-att-me]{ container-type:inline-size; }
.att-me-grid{ display:grid; gap:14px; grid-template-columns:minmax(0,1fr); align-items:start; }
/* คอลัมน์ซ้าย = การ์ดลงเวลา + สรุปเข้า-ออก + สัปดาห์นี้ (ย้ายมาจาก inline style ของ dc.html) */
.att-me-side{ display:flex; flex-direction:column; gap:14px; width:100%; max-width:520px; margin:0 auto; min-width:0; }
.att-me-main{ min-width:0; }
@container (min-width: 960px){
  .att-me-grid{ grid-template-columns:minmax(0,400px) minmax(0,1fr); }
  /* จอกว้างแล้วไม่ต้องจัดกลาง — ชิดซ้ายให้ตารางฝั่งขวาได้ที่เต็มที่ */
  .att-me-side{ max-width:none; margin:0; }
}

/* ───────────────────────────────────────────────────────────────────────────
   ไฮไลต์ "รายการที่ต้องโฟกัส" หลังกดมาจากแจ้งเตือน แล้วจางหายใน 3 วินาที
   (1 ต.ค. 2569 เจ้าของสั่ง — วาด/ถอดโดย `public/js/core/focusflash.js`)
   1 ต.ค. 2569 (รอบหก) เจ้าของทัก: "ไฮไลต์ทำงานละ แต่มันไม่สวย ทำไมไม่ใส่ filter เอา แล้วก็ทำ
   animation คลุมทั้งหมดเลย หรืออื่น ๆ แทน border" → จากเส้นกรอบส้มรอบการ์ด เปลี่ยนเป็น 3 ชั้น
     1. **สีส้มคลุมทับทั้งการ์ด + แสงวิ่งผ่านหนึ่งรอบ** วาดบน `::before` ที่อยู่ "เหนือทุกอย่าง"
        แล้วผสมสีด้วย `mix-blend-mode` — ธีมมืดใช้ `screen` (ทำให้สว่างขึ้นเท่านั้น = เหมือนแสงตกกระทบ)
        · ธีมสว่างใช้ `multiply` (ทำให้เข้มขึ้นเท่านั้น = เหมือนปากกาไฮไลต์) → ตัวหนังสือขาวในธีมมืด
        ยังขาวเท่าเดิม ตัวหนังสือเข้มในธีมสว่างยังเข้มเท่าเดิม **ความคมของตัวอักษรไม่ลดลง**
        🔴 **ห้ามย้ายกลับไปไว้ใต้ตัวหนังสือ (`z-index:-1`)** — ลองแล้ว: ปุ่ม/ป้ายที่มีพื้นทึบของตัวเอง
           ("ยกเลิกใบนี้" · "ไม่อนุมัติ") บังสีไว้เป็น **ก้อนมืดโผล่ทะลุสีส้มเป็นหย่อม ๆ** ดูเหมือนพัง
        🔴 **ห้ามใช้ blend ปกติ (normal)** — สีไปจับตัวหนังสือทำให้จางลง และทำให้ปุ่มสีม่วงขุ่น
     2. **แสงฟุ้งรอบการ์ด** ด้วย `filter: drop-shadow()` — โค้งตามมุมการ์ดเอง
     3. **การ์ดยกขึ้น 2px** ตอนเริ่ม แล้ววางกลับตอนจาง
   🔴 **ไม่มีชั้นไหนแตะ `box-shadow`/`border`/`padding` ของตัวการ์ดเลย** → (ก) ไม่ดันเลย์เอาต์
      (บทเรียน `.is-just-moved` ของกระดานผลิต) (ข) **ไม่ชนกับ inline style ของแถว** — บั๊กรอบห้า
      "คลาสติดแต่ไม่มีวงให้เห็น" เกิดเพราะไปแย่ง `box-shadow` ที่แถวตั้งไว้ใน inline · ส่วน
      `filter`/`transform`/`::before` ไม่มีแถวไหนตั้งเป็น inline style จึงไม่มีอะไรให้แพ้
   🔴 **ต้องใช้ `::before` ห้าม `::after`** — `.row-hv` (ไฮไลต์ตอนเอาเมาส์ชี้) จอง `::after` ของแถว
      ไว้แล้ว ใช้ตัวเดียวกันเมื่อไร 2 กฎจะผสมกันตอนเมาส์ชี้ แล้วหน้าตาเพี้ยนทั้งคู่
   🔴 การ์ดต้องเป็น stacking context ของตัวเอง (`position:relative` + `z-index:1`) — ไม่งั้น
      `mix-blend-mode` จะไปผสมกับทุกอย่างบนหน้าที่อยู่ข้างหลังการ์ด แทนที่จะผสมแค่เนื้อในการ์ด
      · `pointer-events:none` ห้ามถอด — ชั้นนี้คลุมปุ่ม "อนุมัติ" อยู่ ถอดแล้วกดปุ่มไม่ติด 3 วินาที
   🔴 สีมาจาก `--ff-c` ตัวเดียว (ส้ม = สีของ "งานรอทำ" ชุดเดียวกับป้าย "รออนุมัติ") — ผู้เรียก
      เปลี่ยนโทนได้ผ่าน `flashFocus(..., {tone})` ทุกชั้นเปลี่ยนตามพร้อมกัน
   ─────────────────────────────────────────────────────────────────────────── */
.focus-flash{ --ff-c:#f59e0b; position:relative; z-index:1;
  animation:ffLift 3s cubic-bezier(.22,.8,.3,1) forwards; }
.focus-flash::before{ content:''; position:absolute; inset:-1px; border-radius:inherit; z-index:2;
  pointer-events:none; mix-blend-mode:multiply;
  background-color:color-mix(in oklab, var(--ff-c) 16%, transparent);
  /* แถบแสงเฉียงที่วิ่งจากซ้ายไปขวาหนึ่งรอบ — ภาพกว้าง 260% แล้วเลื่อนตำแหน่งจาก 100% ไป 0% */
  background-image:linear-gradient(105deg, transparent 36%,
    color-mix(in oklab, var(--ff-c) 34%, transparent) 50%, transparent 64%);
  background-size:260% 100%; background-repeat:no-repeat; background-position:100% 0;
  /* ขอบด้านในบาง ๆ ให้การ์ดมีเส้นขอบเรืองแสง (ทับเส้นขอบเดิมของการ์ดพอดีเพราะ inset:-1px) */
  box-shadow:inset 0 0 0 1px color-mix(in oklab, var(--ff-c) 55%, transparent);
  animation:ffWash 3s ease-out forwards; }
/* ธีมมืด: screen = ทำให้สว่างขึ้นอย่างเดียว · แรงกว่าธีมสว่างเล็กน้อยเพราะพื้นมืดดูดสีไปเยอะ */
html[data-theme="dark"] .focus-flash::before{ mix-blend-mode:screen;
  background-color:color-mix(in oklab, var(--ff-c) 20%, transparent);
  background-image:linear-gradient(105deg, transparent 36%,
    color-mix(in oklab, var(--ff-c) 42%, transparent) 50%, transparent 64%); }
@keyframes ffWash{
  0%   { opacity:0; background-position:100% 0; }
  10%  { opacity:1; }
  50%  { background-position:0% 0; }
  /* ค้างเต็มสีเกือบ 2 วิก่อนจาง — จางเร็วเกินไปคนที่เพิ่งละสายตาจากกระดิ่งมาจะไม่ทันเห็น */
  65%  { opacity:1; }
  100% { opacity:0; background-position:0% 0; }
}
@keyframes ffLift{
  0%   { transform:translateY(0);    filter:drop-shadow(0 0 0 transparent); }
  12%  { transform:translateY(-2px); filter:drop-shadow(0 10px 24px color-mix(in oklab, var(--ff-c) 42%, transparent)); }
  65%  { transform:translateY(-2px); filter:drop-shadow(0 8px 20px color-mix(in oklab, var(--ff-c) 32%, transparent)); }
  100% { transform:translateY(0);    filter:drop-shadow(0 0 0 transparent); }
}
/* ลดการเคลื่อนไหว — **ไม่มีอะไรเคลื่อนที่**: ไม่ยกการ์ด ไม่มีแถบแสงวิ่ง เหลือสีทาทับ + แสงฟุ้ง
   ที่ "ค้างนิ่ง" แล้วค่อยจางตอนท้าย (การจางด้วยความทึบไม่ใช่การเคลื่อนไหว — แนวเดียวกับที่ระบบ
   ของ Apple เปลี่ยนการเลื่อนเป็นการจางเมื่อเปิดค่านี้) · JS ถอดคลาสเองเมื่อครบ 3 วิเหมือนกัน
   🔴 ห้ามสั่ง `animation:none` แล้วตั้งสไตล์เป็นกฎธรรมดา — ใช้ animation ค่านิ่งแทน ให้จังหวะ
      เข้า-ออกตรงกับทางปกติ (บทเรียนรอบห้า: ทางนี้เคยเงียบหายไปทั้งที่ทางปกติทำงาน) */
@media (prefers-reduced-motion: reduce){
  .focus-flash{ animation:ffLiftStill 3s linear forwards; }
  .focus-flash::before{ background-image:none; animation:ffWashStill 3s linear forwards; }
}
@keyframes ffWashStill{
  0%, 70% { opacity:1; }
  100%    { opacity:0; }
}
@keyframes ffLiftStill{
  0%, 70% { filter:drop-shadow(0 8px 20px color-mix(in oklab, var(--ff-c) 32%, transparent)); }
  100%    { filter:drop-shadow(0 0 0 transparent); }
}

/* Fabric → garment → size summaries keep quantities readable on small screens. */
.od-group-columns{display:grid;grid-template-columns:minmax(0,1fr) 96px 76px;gap:8px;padding:10px 12px;align-items:center}
.od-group-details.has-money .od-group-columns{grid-template-columns:minmax(0,1fr) 70px 60px 90px 100px}
.od-group-heading{font-size:12px;color:var(--muted);padding-top:8px;padding-bottom:8px}
.od-group-row{font-size:13px;border-top:1px solid var(--border)}
.od-group-columns>*{min-width:0;overflow-wrap:anywhere}
.od-group-number{text-align:right;font-variant-numeric:tabular-nums}
.od-group-mobile-label{display:none}
.pwa-update-banner{position:fixed;z-index:450;top:calc(70px + env(safe-area-inset-top,0px));right:12px;width:min(420px,calc(100vw - 24px));padding:16px;border:1px solid var(--brand);border-radius:14px;background:var(--surface);color:var(--text);box-shadow:var(--shadow)}
.pwa-update-title{display:flex;gap:8px;align-items:center;font-size:15px}
.pwa-update-banner p{font-size:13px;line-height:1.6;margin:8px 0 12px;color:var(--muted)}
.pwa-update-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}
.pwa-update-actions button{min-height:44px}
.pwa-update-actions .chip-btn.pwa-update-primary{background:var(--brand)!important;border-color:var(--brand)!important;color:#fff!important}
@media(max-width:767px){
  .od-group-heading{display:none}
  .od-group-details .od-group-row,.od-group-details.has-money .od-group-row{grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:10px 14px}
  .od-group-spec{grid-column:1 / -1}
  .od-group-row>:nth-child(even){text-align:left}
  .od-group-mobile-label{display:block;font-size:12px;font-weight:400;color:var(--muted);margin-bottom:3px}
}
