/* ============================================================
   EXPORT DEPARTMENT — Design System
   Earth Tone Japanese Minimal — พื้นกระดาษสีครีมอุ่นๆ เส้นกรอบบางสีทราย สีเน้นหลักดินเผา/ terracotta
   (เข้าชุดกับโลโก้ตู้คอนเทนเนอร์สีส้ม) สีประจำหมวดหมู่โทนธรรมชาติ (คราม/มอส/โอลว์/ฝุ่นกุหลาบ) แทนสีสด
   จัดจ้าน ให้ความรู้สึกสงบตา ไม่แสบตา (เดิมเคยลองพื้นสว่างพาสเทลอุ่น → ธีมมืด "Dark Tech Minimal"
   ทั้งเว็บ → รอบนี้เปลี่ยนกลับมาเป็นพื้นสว่างอีกครั้งแต่คนละแนวคิด: โทนธรรมชาติแบบมินิมอลญี่ปุ่น ไม่ใช่
   พาสเทลอุ่นแบบเดิม) — ดู .so-doc/.pi-doc ท้ายไฟล์สำหรับข้อยกเว้น: เอกสาร SO/PI/INV&PL ต้อง pin เป็น
   กระดาษขาว-ตัวหนังสือเข้มเสมอไม่ว่าธีมแอปจะเป็นสีอะไร */

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&family=Noto+Sans+Thai:wght@300;400;500;600;700&family=Sarabun:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap');

:root {
  /* พื้นกระดาษครีมอุ่นๆ ไล่ระดับความสว่าง 4 ขั้นชัดเจน: bg (พื้นหลังหน้าเว็บ) < surface (การ์ด) <
     surface-alt (แผงย่อยในการ์ด) < bg-soft (แถบหัวตาราง/hover) — bg-soft ต้องต่างจาก surface ชัดเจน
     (เคยพลาดตั้งค่าเดียวกันตอนธีมมืด ทำให้หัวตารางกลืนกับพื้นตารางจนแยกไม่ออก) */
  --bg: #F6F1E7;
  --surface: #FFFFFF;
  --surface-alt: #F1EBDD;
  --bg-soft: #E8DFC9;

  --text: #33302A;
  --text-soft: #746C5E;
  --text-faint: #A79D89;

  --border: #DDD2BA;
  --border-soft: #E9E1CE;

  /* Single brand accent — ดินเผา/terracotta เข้าชุดกับโลโก้ตู้คอนเทนเนอร์สีส้ม */
  --gold: #B5623B;
  --gold-soft: rgba(181, 98, 59, 0.12);
  --gold-deep: #8F4B2C;

  /* สีประจำกล่อง stat-tile / tab แต่ละใบ — โทนธรรมชาติ (คราม/มอส/ฝุ่นกุหลาบ/โอลว์/ม่วงเถ้า) แทนสีสด
     จัดจ้าน ยังแยกแต่ละหมวดออกจากกันได้ไวแต่ไม่แสบตา */
  --sage: #7C8A63;
  --sage-soft: rgba(124, 138, 99, 0.12);
  --lavender: #93798A;
  --lavender-soft: rgba(147, 121, 138, 0.12);
  --blush: #B9877E;
  --blush-soft: rgba(185, 135, 126, 0.12);
  --sky: #5E7C8A;
  --sky-soft: rgba(94, 124, 138, 0.12);
  --honey: #BF9A45;
  --honey-soft: rgba(191, 154, 69, 0.12);
  /* navy: กล่องสรุปยอด (ดู #home-quota-summary-card ใน home.js) และเป็นสีตำแหน่งแรกในชุดไล่สี tile
     ตาม MOD หน้า Home (ดู typeColorClasses ใน home.js) — จงใจให้เป็นคนละเฉดกับ var(--gold) (ดินเผา
     แบรนด์หลัก) กันสับสนเวลาไล่สีต่อกันในชุดเดียวกัน — ครามญี่ปุ่น (ai-iro) เข้ม */
  --navy: #46697A;
  --navy-soft: rgba(70, 105, 122, 0.10);
  --rust: #C17B4F;
  --rust-soft: rgba(193, 123, 79, 0.13);

  --success: #7C8A63;
  --success-soft: rgba(124, 138, 99, 0.12);
  --warning: #BF9A45;
  --warning-soft: rgba(191, 154, 69, 0.12);
  --danger: #B0554A;
  --danger-soft: rgba(176, 85, 74, 0.12);
  --info: #5E7C8A;
  --info-soft: rgba(94, 124, 138, 0.12);

  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 14px;
  --radius-pill: 999px;

  --shadow-sm: 0 1px 2px rgba(51, 48, 42, 0.05);
  --shadow-md: 0 4px 16px rgba(51, 48, 42, 0.07);
  --shadow-lg: 0 16px 40px rgba(51, 48, 42, 0.10);

  /* ภาษาอังกฤษใช้ Poppins, ภาษาไทยใช้ Noto Sans Thai (ตามที่ผู้ใช้ระบุ — "Google Sans" ตัวจริงเป็นฟอนต์
     ภายในของ Google เอง ไม่เปิดให้ใช้งานผ่าน Google Fonts เลย จึงใช้ Noto Sans Thai แทน ซึ่งเป็นฟอนต์ไทย
     ตัวจริงที่ Google ทำเองและเปิดให้ใช้ฟรีผ่าน Google Fonts) — เบราว์เซอร์จะเลือกฟอนต์แรกในลิสต์ที่มี
     ตัวอักษรของฝั่งนั้นๆ ให้อัตโนมัติต่อตัวอักษร (Poppins ไม่มีตัวอักษรไทย จึงตกไปที่ Noto Sans Thai เอง
     เฉพาะช่วงข้อความไทย โดยไม่ต้องแยก selector ต่อภาษาเอง) */
  --font-display: 'Poppins', 'Noto Sans Thai', sans-serif;
  --font-body: 'Poppins', 'Noto Sans Thai', sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 15.5px;
  line-height: 1.65;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0;
  color: var(--text);
}

h1 { font-size: 2.1rem; }
h2 { font-size: 1.5rem; }
h3 { font-size: 1.15rem; font-weight: 600; }

p { margin: 0; }

.container {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 32px;
}

/* ---------------- Top navigation ---------------- */

/* ตรึงแถบเมนูไว้บนสุดของหน้าจอเสมอ ไม่เลื่อนตามเนื้อหาเวลา scroll (position: fixed แทน sticky
   เพราะ sticky อาจไม่ทำงานตามที่คาดในบางสถานการณ์ — fixed รับประกันผลลัพธ์เดียวกันทุกกรณี)
   #app-nav (ตัว wrapper ที่ยังอยู่ใน flow ปกติ) เผื่อพื้นที่สูงเท่ากับ .topbar จริงไว้ให้ผ่าน JS ใน
   nav.js กันเนื้อหาข้างล่างเลื่อนไปซ้อนใต้แถบเมนู */
.topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  background: rgba(246, 241, 231, 0.88);
  backdrop-filter: blur(10px) saturate(140%);
  border-bottom: 1px solid var(--border-soft);
}

.topbar-inner {
  max-width: none;
  width: 100%;
  padding: 16px clamp(24px, 3vw, 56px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  box-sizing: border-box;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* โลโก้ตู้คอนเทนเนอร์ (SVG วาดสีในตัวเองแล้ว ดู CONTAINER_LOGO_SVG ใน nav.js / login.html) — กล่องนี้
   แค่กำหนดขนาดที่วาง ไม่มีพื้นหลัง/สีตัวอักษรแบบตอนยังเป็นตัวย่อ "EX" — SVG ลูกใช้ width/height: 100%
   สืบทอดขนาดจากกล่องนี้ ปรับขนาดโลโก้แค่จุดเดียวตรงนี้พอ ไม่ต้องไปแก้ attribute ในทั้ง 2 ไฟล์ที่ฝัง SVG
   ไว้ (nav.js/login.html) — ขยายใหญ่ขึ้นมากจากเดิม (36px) ให้เด่นชัดในแถบเมนู ตามที่ขอ */
.brand-mark {
  width: 64px;
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.brand-mark svg { display: block; width: 100%; height: 100%; }

.brand-text .title {
  font-size: 1.9rem;
  font-weight: 700;
  color: var(--text);
  line-height: 1.1;
  letter-spacing: -0.01em;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--bg-soft);
  border-radius: var(--radius-pill);
  padding: 4px;
}

.nav-links a {
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  font-size: 0.86rem;
  font-weight: 500;
  color: var(--text-soft);
  transition: all 0.15s ease;
  white-space: nowrap;
}

.nav-links a:hover {
  color: var(--text);
}

.nav-links a.active {
  color: #fff;
  background: var(--gold);
}

/* CAL (cal.html) — ตัวหนังสือสี terracotta-rust เด่นกว่าลิงก์อื่นแม้ไม่ active (ต่างจากลิงก์อื่นที่เป็น
   text-soft จนกว่าจะ active ถึงเปลี่ยนเป็นพื้น gold) ตามที่ผู้ใช้ระบุ — active ของ CAL เองก็ใช้ rust แทน
   gold กันสีชนกับลิงก์อื่น */
.nav-links a.cal-link { color: var(--rust); font-weight: 700; }
.nav-links a.cal-link:hover { background: var(--rust-soft); }
.nav-links a.cal-link.active { background: var(--rust); color: #fff; }

/* Quotation (quotation.html) — เด่นแยกจากลิงก์อื่นเหมือน CAL ด้านบน แต่คนละสี — เดิมใช้ --lavender (โทน
   ม่วง) ผู้ใช้ระบุห้ามใช้สีม่วงในเว็บนี้ เปลี่ยนเป็น --sky (ฟ้าอมเทา) แทน ยังคงแยกจาก --rust ของ CAL ชัดเจน */
.nav-links a.quotation-link { color: var(--sky); font-weight: 700; }
.nav-links a.quotation-link:hover { background: var(--sky-soft); }
.nav-links a.quotation-link.active { background: var(--sky); color: #fff; }

.nav-user {
  font-size: 0.82rem;
  color: var(--text-faint);
  display: flex;
  align-items: center;
  gap: 10px;
}

/* ปุ่ม Backend (หน้าจัดการ User/Role) — เห็นเฉพาะ Owner ตามที่ผู้ใช้ระบุ ให้สีเด่นแยกจากปุ่ม Logout/
   ป้ายชื่อข้างๆ (เหมือนที่ .cal-link เด่นแยกจากลิงก์ nav อื่น) กันมองข้ามว่าเป็นแค่ปุ่มธรรมดา */
.nav-backend-btn,
.nav-backend-btn:visited {
  background: var(--navy);
  border-color: var(--navy);
  color: #fff;
  font-weight: 700;
}
.nav-backend-btn:hover { opacity: 0.88; }

.nav-user-label {
  font-weight: 600;
  color: var(--text-soft);
}

/* ---------------- Login page ---------------- */

.login-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.login-card {
  width: 100%;
  max-width: 380px;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  padding: 32px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.06);
}

.login-card .brand-mark { margin: 0 auto 16px; }
.login-card h1 { font-size: 1.2rem; text-align: center; margin: 0 0 4px; }
.login-card .lead { text-align: center; margin-bottom: 24px; }
.login-error {
  background: var(--danger-soft);
  color: var(--danger);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  font-size: 0.84rem;
  margin-bottom: 16px;
  display: none;
}
.login-error.show { display: block; }

/* ---------------- Viewer role: view-only lock ---------------- */
/* Role Viewer เข้าดูหน้า Order Management ได้อย่างเดียว แก้ไขอะไรไม่ได้เลย — ปุ่ม/ช่องกรอก
   ทั้งหมดในหน้าเทาลงและกดไม่ได้ (แทนที่จะไล่ปิดทีละปุ่มซึ่งพังง่ายเพราะหน้านี้ re-render
   เนื้อหาใหม่ตลอด ใช้ CSS ผูกกับ body.viewer-locked แทนให้ครอบคลุมของที่ยังไม่ได้ render ด้วย) */
body.viewer-locked .btn,
body.viewer-locked button,
body.viewer-locked input,
body.viewer-locked select,
body.viewer-locked textarea {
  filter: grayscale(1) opacity(0.55);
  pointer-events: none;
  cursor: not-allowed;
}

/* ปุ่ม Logout ใน nav ต้องกดได้เสมอ แม้อยู่ในโหมด Viewer */
body.viewer-locked #nav-logout-btn {
  filter: none;
  opacity: 1;
  pointer-events: auto;
  cursor: pointer;
}

/* Viewer เรียกดู "ตัวกรองมุมมอง" ได้ทุกหน้า (แค่เปลี่ยนช่วง/มุมข้อมูลที่แสดง ไม่ใช่แก้ไขข้อมูลจริง) —
   ครอบคลุมทุก class ที่ใช้เป็นปุ่ม/select กรองมุมมองล้วนๆ ทั่วทั้งแอป (ไม่ใช่ปุ่มบันทึก/แก้ไข/ลบ):
   - .month-filter-btn / .or-month-btn / .mr-month-btn: แถบเดือน (quota/arrangement/dashboard/
     financial/shipping ใช้ class เดียวกัน, Home ใช้ 2 class แยกสำหรับการ์ด Order Realtime/Monthly Report)
   - select ปี ของแต่ละหน้า (เจาะจงทีละ id เพราะเป็น element เดี่ยว ไม่ได้ใช้ class ร่วมกัน)
   - .unit-toggle-btn: ปุ่มสลับหน่วย/ตัวกรองแบบ toggle ทั่วแอป (DOZEN/PAIR, 200/205-S, QTY/AMOUNT,
     Year to Date ฯลฯ) — ทุกจุดที่ใช้ class นี้เป็นแค่ตัวกรองการแสดงผล ไม่มีจุดไหนแก้ไขข้อมูลจริง
   - .fin-customer-chip: ปุ่มเลือกลูกค้า/เลือกออเดอร์เพื่อดูรายละเอียด (quota/financial/arrangement) —
     แค่เปลี่ยนสิ่งที่แสดง ไม่ใช่แก้ไข (ปุ่มแก้ไข/บันทึกใน panel รายละเอียดที่ตามมายังล็อกตามปกติ)
   - #quota-btn-print-so (Summary — ป็อปอัป SO Preview) / #invpl-btn-print (Shipping — ป็อปอัป
     INV&PL Preview): ปุ่ม Print เรียก window.print() ตรงๆ ไม่มีการบันทึก/แก้ไขข้อมูลใดๆ เลย (ต่างจาก
     "Confirm & Save"/"Export PDF" ข้างๆ กันที่ยังล็อกตามปกติเพราะบันทึกเอกสารจริง) — Operator ที่ตั้งเป็น
     Viewer ของหน้านั้นต้องพิมพ์เอกสารที่เปิดออกมาดูอยู่แล้วได้ ไม่ใช่ปุ่มที่ควรถูกล็อก
   (body.viewer-locked button ด้านบนปิดปุ่มทุกตัวไว้เป็นค่าเริ่มต้น เลยต้องเจาะจงยกเว้นเป็นจุดๆ ไป) */
body.viewer-locked .month-filter-btn,
body.viewer-locked .or-month-btn,
body.viewer-locked .mr-month-btn,
body.viewer-locked .unit-toggle-btn,
body.viewer-locked .fin-customer-chip,
body.viewer-locked #quota-btn-print-so,
body.viewer-locked #invpl-btn-print,
body.viewer-locked #quota-year,
body.viewer-locked #arr-year,
body.viewer-locked #filter-year,
body.viewer-locked #fin-filter-year,
body.viewer-locked #or-year,
body.viewer-locked #mr-year {
  filter: none;
  opacity: 1;
  pointer-events: auto;
  cursor: pointer;
}

/* ---------------- Page shell ---------------- */

/* Desktop/Laptop (Tier A) ใช้พื้นที่เต็มจอเสมอ (ไม่จำกัด max-width แคบๆ เหมือนเดิมที่เหลือขอบว่าง 2 ข้าง
   บนจอกว้าง) — clamp() ให้ padding ข้างขยับตามสัดส่วนจอแบบนุ่มนวล (รองรับข้อ 2: ย่อหน้าต่าง desktop
   แคบลงแล้วต้องปรับสัดส่วนต่อเนื่อง ไม่ใช่กระโดดเป็นขั้นบันไดที่ breakpoint เดียว) — จอ ultrawide/4K
   (≥2400px) มีเพดานแยกไว้กันตารางข้อมูลถี่ๆ (ตัวเลข/ราคา) โปร่งจนอ่านยากเกินไป ดูด้านล่างสุดของไฟล์
   Tier B/C ปรับ padding ลงอีกทีผ่าน media query (Tier A/B/C) ไม่แตะ max-width เพราะ 100% อยู่แล้ว */
.page {
  max-width: none;
  width: 100%;
  box-sizing: border-box;
  padding: 14px clamp(24px, 3vw, 56px) 80px;
}

.page-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 32px;
  flex-wrap: wrap;
}

.eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold);
  font-weight: 600;
  margin-bottom: 6px;
  display: block;
}

.page-header p.lead {
  color: var(--text-soft);
  margin-top: 8px;
  max-width: 560px;
}

/* ---------------- Sticky page header (Arrangement/Summary/Financial/Shipping/CAL/Database) ----------------
   ตรึงส่วนหัวหน้า (page-header เฉยๆ หรือ page-header + การ์ดตัวกรองแรกสุด แล้วแต่หน้า ตามที่ผู้ใช้ระบุ)
   ไว้ใต้แถบเมนูเสมอระหว่างเลื่อนดูเนื้อหายาวๆ ด้านล่าง — top ถูกเซ็ตเป็น inline style จาก
   syncStickyHeaderTop() ใน nav.js เสมอ (เหตุผลเดียวกับ #quota-grids ด้านล่าง: ความสูงจริงของ .topbar
   ไม่คงที่ ขึ้นกับ breakpoint — ดู .topbar-inner flex-wrap) ค่า 0 ที่นี่เป็นแค่ fallback ก่อน JS ตั้งค่าจริง
   — background ทึบสีเดียวกับพื้นหลังหน้าเว็บกันเนื้อหาที่เลื่อนผ่านด้านล่างทะลุเห็น z-index ต่ำกว่า
   .topbar (50) เสมอ กันบังเมนูตอนเลื่อนขึ้นสุด */
.sticky-page-header {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--bg);
}

/* ---------------- Cards ---------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 26px;
}

.card + .card { margin-top: 20px; }

.card-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20px;
  flex-wrap: wrap;
  gap: 12px;
}

/* ---------------- Stat tiles ---------------- */

/* auto-fit แทน repeat(4, 1fr) ตายตัว — จำนวนกล่อง MOD ในหน้า Home ไม่คงที่ (ขึ้นกับจำนวน MOD ที่มี
   ยอดขายจริงในเดือนนั้น) กริดตายตัว 4 คอลัมน์เจอ 3 กล่องแล้วจะเหลือช่องว่างข้างขวา 1 ช่อง หรือกล่อง
   สุดท้ายตกไปอยู่แถวใหม่โดดเดี่ยว ดูไม่สมมาตร — auto-fit ให้กล่องที่มีอยู่จริงยืดเต็มแถวเสมอไม่ว่าจะมี
   กี่กล่องก็ตาม */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
  margin-bottom: 28px;
}

.stat-tile {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 20px 22px;
  box-shadow: var(--shadow-sm);
  position: relative;
  overflow: hidden;
}

.stat-tile .stat-label {
  font-size: 0.8rem;
  color: var(--text-soft);
  font-weight: 500;
  margin-bottom: 8px;
}

.stat-tile .stat-value {
  font-family: var(--font-display);
  font-size: 1.9rem;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.02em;
}

.stat-tile .stat-chip {
  display: inline-block;
  margin-top: 10px;
  font-size: 0.72rem;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
}

.stat-tile.gold { border-top: 3px solid var(--gold); }
.stat-tile.sage { border-top: 3px solid var(--sage); }
.stat-tile.sky { border-top: 3px solid var(--sky); }
.stat-tile.blush { border-top: 3px solid var(--blush); }
.stat-tile.navy { border-top: 3px solid var(--navy); }
.stat-tile.rust { border-top: 3px solid var(--rust); }
.stat-tile.honey { border-top: 3px solid var(--honey); }
.stat-tile.lavender { border-top: 3px solid var(--lavender); }

/* หน้า Home (Export Sales Report) — กล่องสรุปยอดเป็นจุดเด่นหลักของหน้านี้ แยกจำนวน "คู่" กับ "บาท"
   ออกเป็นสองบล็อกชัดเจนในตัวการ์ดเดียวกันเสมอ (คนละคอลัมน์ มีป้ายกำกับ "จำนวน"/"ยอดเงิน" ของตัวเอง
   สีตัวเลขต่างกัน) กันสับสนว่าเลขไหนคือคู่ เลขไหนคือเงิน — แบ่งเป็น 2 ส่วนหัวข้อย่อยด้วย
   (.home-dashboard-section) แทนที่จะโยนกล่องทั้งหมดไว้ปนกันไม่มีหัวข้อคั่น */

.home-dashboard-section + .home-dashboard-section { margin-top: 22px; }

.home-dashboard-section-title {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--text-soft);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border-soft);
}

/* หัวข้อย่อยที่ต้องมีตัวควบคุมอื่นด้วย (เช่น dropdown filter ต่อกราฟที่ 1) — เหมือน
   .home-dashboard-section-title ทุกประการแค่จัดเป็นแถว flex ให้มีที่วางตัวควบคุมด้านขวา */
.home-dashboard-section-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border-soft);
}
.home-dashboard-section-title-row .home-dashboard-section-title {
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: none;
}

/* บล็อก "จำนวน"/"ยอดเงิน" เรียงซ้อนกันแนวตั้ง (ป้ายกำกับชิดซ้าย ตัวเลขชิดขวา คั่นด้วยเส้นบาง) แทนการ
   วางคู่กันแนวนอนแบบเดิม — วางแนวนอนแล้วตัวเลขยอดเงินหลักล้าน (เช่น "36,672,475.00 ฿") มีที่ไม่พอ
   ถูก .stat-tile { overflow: hidden; } บังตัดท้ายจนอ่านไม่ครบ เรียงซ้อนแนวตั้งให้ตัวเลขแต่ละบล็อกได้
   ความกว้างเต็มการ์ดเสมอ กันปัญหานี้ตั้งแต่ต้น (เผื่อยอดใหญ่กว่านี้อีกในอนาคตด้วย word-break สำรอง) */
#home-dashboard-card .stat-metric-row {
  display: flex;
  flex-direction: column;
  margin-top: 10px;
}

#home-dashboard-card .stat-metric-block {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
}

#home-dashboard-card .stat-metric-block + .stat-metric-block {
  border-top: 1px solid var(--border-soft);
}

#home-dashboard-card .stat-metric-caption {
  flex: none;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
}

#home-dashboard-card .stat-metric-number {
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.01em;
  text-align: right;
  word-break: break-word;
}

#home-dashboard-card .stat-metric-block-money .stat-metric-number { color: var(--success); }

#home-dashboard-card .stat-metric-unit {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-soft);
  margin-left: 4px;
}

/* หน้าที่ 2 ของ dashboard (205-S) — กล่องซ้าย (ยอดรวมตัวเดียว) กับกล่องขวา (แยกช่วงไซส์ มีตาราง
   3 คอลัมน์ข้างใน) กว้างเท่ากัน (1:1) — กล่องขวาแคบลงกว่าเดิม (เดิมสัดส่วน 1:2) เลยลด gap/ความกว้าง
   คอลัมน์ป้ายชื่อช่วงลง และให้ตัวเลขคู่/บาทตัดขึ้นบรรทัดใหม่ได้แทน nowrap (ดู .stat-segment-qty/
   .stat-segment-amount ด้านล่าง) กัน .stat-tile { overflow: hidden; } บังตัวเลขขาดหายไปเงียบๆ */
#home-dashboard-card .stat-grid-205s { grid-template-columns: 1fr 1fr; }

/* Highlight เฉพาะกล่อง "Regular+Special" ในหมวด Flipflops/Sandals/Slippers — ตาม MOD ให้เด่นกว่ากล่อง
   อื่นในกริดเดียวกัน (ขอบหนารอบกล่อง + สีตัวเลขเปลี่ยนเป็นสีแดงแทน text/success ปกติ ที่กล่องอื่นใช้) */
#home-dashboard-card .stat-tile-highlight {
  border: 2px solid var(--danger);
  border-top-width: 4px;
  box-shadow: 0 0 0 3px var(--danger-soft);
}
#home-dashboard-card .stat-tile-highlight .stat-metric-number,
#home-dashboard-card .stat-tile-highlight .stat-metric-block-money .stat-metric-number {
  color: var(--danger);
}

/* กล่อง 205-S แบ่งช่วงไซส์ — ตารางเล็กในตัวการ์ด 3 คอลัมน์ (ช่วงไซส์ / คู่ / บาท) จัดคอลัมน์ให้ตรงกัน
   ทุกแถวแทนการเขียนคั่นด้วย "·" ในบรรทัดเดียว (อ่านเทียบยอดระหว่างช่วงไซส์ได้ง่ายกว่าเดิมมาก) —
   overflow: visible (ไม่ใช้ hidden ของ .stat-tile ฐาน) กันตัวเลขที่กว้างกว่าที่คาดโดนตัดขาดไปเงียบๆ */
#home-dashboard-card .stat-tile-segmented { display: flex; flex-direction: column; overflow: visible; }

#home-dashboard-card .stat-segment-header,
#home-dashboard-card .stat-segment-row {
  display: grid;
  grid-template-columns: minmax(40px, 1fr) auto auto;
  align-items: baseline;
  gap: 10px;
  padding: 6px 0;
}

#home-dashboard-card .stat-segment-header {
  margin-top: 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border-soft);
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
#home-dashboard-card .stat-segment-header span:first-child { visibility: hidden; }
#home-dashboard-card .stat-segment-header span { text-align: right; }

#home-dashboard-card .stat-segment-row + .stat-segment-row { border-top: 1px solid var(--border-soft); }
/* ไม่ใช้ white-space: nowrap เหมือนเดิม (ช่วงไซส์อย่าง "36-38" สั้นพออยู่แล้วไม่กระทบ) — กล่องแยกตาม
   โมเดล (ดู segmentedSalesTileHtml(..., 'โมเดล') ใน home.js) ใช้ชื่อโมเดลจริงที่อาจยาวกว่าช่วงไซส์มาก
   ปล่อยให้ตัดขึ้นบรรทัดใหม่ได้กันชื่อยาวดันคอลัมน์คู่/บาทล้นออกนอกกล่อง */
#home-dashboard-card .stat-segment-label { font-size: 0.85rem; color: var(--text-soft); font-weight: 500; word-break: break-word; }
/* คู่/บาท ต้องอยู่บรรทัดเดียวกันเสมอ (nowrap) — ต่างจาก label ด้านบนที่ยอมห่อได้ เพราะตัวเลขที่ถูกตัด
   กลางคัน (เช่น "22,104," ขึ้นบรรทัดใหม่แล้วเหลือ "900.00") อ่านผิดง่ายกว่าชื่อโมเดลที่ห่อ — ยิ่งตอนนี้
   หมวด Sneakers มีกล่อง .stat-tile-segmented เรียงกันหลายกล่องในแถวเดียว (205-S แยก 2 ช่วง/3 ช่วง/
   โมเดลอื่นๆ) กล่องแคบลงกว่าเดิม เลยย่อฟอนต์ลงจาก 1.05rem (ดู .page-home override ด้านล่าง) ให้พอดี
   คอลัมน์แคบๆ โดยไม่ต้องพึ่ง word-break */
#home-dashboard-card .stat-segment-qty,
#home-dashboard-card .stat-segment-amount {
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 700;
  text-align: right;
  white-space: nowrap;
}
#home-dashboard-card .stat-segment-amount { color: var(--success); }
#home-dashboard-card .stat-segment-qty small,
#home-dashboard-card .stat-segment-amount small {
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--text-soft);
  margin-left: 3px;
}

/* กล่องยอดขายแยกตามโมเดล (ดู classifyByModel()/segmentedSalesTileHtml() ใน home.js) อาจมีเป็นสิบ
   โมเดล — เกิน 6 แถวห่อด้วย scrollbar แทนปล่อยให้กล่องสูงไม่จำกัด (เหมือนกล่องผลต่างหน้า Arrangement
   — ดู .arr-diff-box-scroll) ความสูงคำนวณจาก ~6 แถว (padding 6px + เส้นคั่น 1px ต่อแถว) */
#home-dashboard-card .stat-segment-rows-scroll {
  max-height: 210px;
  overflow-y: auto;
}

/* กล่อง "ยอดขายรวมทั้งหมด" บนสุด (moneyOnlyTileHtml) ใช้ .stat-value ตัวเดียวกับกล่องอื่นในเว็บ แต่ยอด
   รวมทั้งระบบอาจเป็นเลขหลักสิบล้านขึ้นไป กันโดน .stat-tile { overflow: hidden; } บังตัดท้ายเหมือนที่
   เคยเกิดกับยอดเงินใน .stat-metric-number (ดูคอมเมนต์บรรทัด ~451 ด้านบน) */
#home-dashboard-card .stat-value { word-break: break-word; }

/* ---------------- Home yearly/quarterly charts (สต๊อกบาร์, เลือกหน่วยคู่/บาทได้) ----------------
 * สี 3 สล็อตมาจากชุดสี categorical ที่ผ่านการตรวจ colorblind-safety แล้ว (validate_palette.js — สี
 * navy/gold/rust ที่ใช้เป็นกรอบกล่องสรุปยอดด้านบนสอบไม่ผ่าน ใช้แทนกันไม่ได้กับกราฟที่ต้องแยกแยะข้อมูล
 * จริง) กำหนดเป็น custom property สโคปแค่ 2 การ์ดนี้ (รายเดือน + รายไตรมาส ใช้ชุดสีเดียวกัน) — ตั้งชื่อ
 * เป็นตำแหน่ง (1/2/3) ไม่ใช่ความหมายตายตัว เพราะกราฟ Regular/Special กับกราฟ 205-S แยกช่วงไซส์ใช้
 * สล็อตเดียวกันคนละความหมาย */
#home-yearly-chart-card,
#home-quarterly-chart-card {
  --chart-series-1: #5E7C8A;
  --chart-series-2: #7C8A63;
  --chart-series-3: #B9877E;
}
.chart-series-1 { background: var(--chart-series-1); }
.chart-series-2 { background: var(--chart-series-2); }
.chart-series-3 { background: var(--chart-series-3); }

.chart-legend { display: flex; gap: 20px; margin-bottom: 20px; flex-wrap: wrap; }
.chart-legend-item { display: flex; align-items: center; gap: 6px; font-size: 0.78rem; color: var(--text-soft); font-weight: 500; }
.chart-legend-swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; flex: none; }

.chart-plot-wrap { display: flex; gap: 4px; }

.chart-yaxis { width: 52px; flex: none; position: relative; height: 200px; }
.chart-gridline { position: absolute; left: 0; right: 8px; transform: translateY(50%); text-align: right; }
.chart-gridline-label { font-size: 0.66rem; color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* overflow-x:auto (ไม่ใช่ hidden) — กราฟแท่งหลายเดือน/หลายซีรีส์ (เช่น 12 เดือน x 2 ปี) แต่ละแท่งมีความ
   กว้างขั้นต่ำจริง (.chart-bar-group .chart-bar-stack กำหนด 14px ตายตัว) จอแคบมากๆ ไม่พอใส่ทั้ง 12 คอลัมน์
   โดยไม่บีบจนแท่งซ้อนทับกัน — ให้เลื่อนดูได้แทนที่จะปล่อยให้ล้น/ซ้อนทับเงียบๆ (จอปกติกว้างพอไม่ขึ้น scroll) */
.chart-plot-area { flex: 1; position: relative; height: 200px; border-bottom: 1px solid var(--border-soft); overflow-x: auto; overflow-y: hidden; }
.chart-gridlines { position: absolute; inset: 0; }
.chart-gridline-rule { position: absolute; left: 0; right: 0; height: 1px; background: var(--border-soft); }

.chart-bars-row { position: absolute; inset: 0; display: flex; align-items: flex-end; padding: 0 2px; }
.chart-bar-col {
  flex: 1;
  min-width: 30px;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  position: relative;
  border-radius: var(--radius-sm);
}
.chart-bar-col:hover,
.chart-bar-col:focus-visible,
.chart-bar-col.is-hovered {
  background: var(--bg-soft);
  outline: none;
}
.chart-bar-total {
  position: absolute;
  bottom: 100%;
  margin-bottom: 4px;
  font-size: 0.66rem;
  font-weight: 700;
  color: var(--text);
  white-space: nowrap;
}
.chart-bar-stack {
  width: 22px;
  max-width: 60%;
  display: flex;
  flex-direction: column-reverse;
  gap: 2px;
}
.chart-bar-seg { width: 100%; }
.chart-bar-seg:last-child { border-radius: 4px 4px 0 0; }

.chart-bar-label {
  margin-top: 8px;
  font-size: 0.66rem;
  color: var(--text-soft);
  font-weight: 600;
}

/* กราฟแท่งกลุ่ม (Grouped bar) — หลายซีรีส์ (เช่นคนละปี) เรียงข้างกันในหมวดเดียว (เช่นไตรมาสเดียว)
   ต่างจาก .chart-bar-stack เดี่ยวด้านบนที่มีแท่งเดียวต่อหมวด — ใช้ที่การ์ด SALES BY QUARTER OVERALL/
   GROWTH (เทียบยอดหลายปี/หลายปีก่อน-หลังในหมวดเดียวกัน) */
.chart-bar-group { display: flex; align-items: flex-end; gap: 3px; height: 100%; }
.chart-bar-group .chart-bar-stack { width: 14px; max-width: none; }

/* กราฟแท่งแนวนอนสมมาตรรอบ 0 (Diverging bar) — ใช้แสดงอัตราการเติบโต % ที่มีทั้งค่าบวก/ลบปนกัน (การ์ด
   GROWTH) สีเขียว/แดงคือสี status ของธีม (var(--success)/var(--danger)) ความหมายตายตัวคือ "เติบโต/ลดลง"
   ไม่ใช่สี categorical แยกกลุ่มข้อมูล — แกนเดียว (ความยาวแท่งจากเส้นกลาง) ตามหลัก dataviz ไม่ใช้กราฟแกนคู่ */
.diverging-chart { display: flex; flex-direction: column; gap: 6px; }
.diverging-chart-row { display: grid; grid-template-columns: 92px 1fr 64px; align-items: center; gap: 10px; }
.diverging-chart-label {
  font-size: 0.74rem;
  color: var(--text-soft);
  font-weight: 600;
  text-align: right;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.diverging-chart-track { position: relative; height: 16px; background: var(--bg-soft); border-radius: var(--radius-sm); }
.diverging-chart-zero { position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; background: var(--border); }
.diverging-chart-bar { position: absolute; top: 2px; bottom: 2px; border-radius: 3px; }
.diverging-chart-bar.positive { left: 50%; background: var(--success); }
.diverging-chart-bar.negative { right: 50%; background: var(--danger); }
.diverging-chart-value { font-size: 0.74rem; font-weight: 700; font-variant-numeric: tabular-nums; }

/* ใช้ร่วมกันทุกจุดที่ต้องบอก "เพิ่มขึ้น/ลดลง" ด้วยสี (เช่น การ์ด SALES BY QUARTER A เทียบไตรมาสก่อนหน้า
   และคอลัมน์ Growth % ในตารางการ์ด GROWTH) สีเดียวกับ .diverging-chart-bar ด้านบน (status สีของธีม) */
.is-positive { color: var(--success); }
.is-negative { color: var(--danger); }
.is-flat { color: var(--warning); }

/* ตาราง Yearly Report (หน้า Home) — คอลัมน์ Growth มี Symbol ขึ้น/ลง/ทรงตัว กำกับก่อนเปอร์เซ็นต์เสมอ
   (▲ เขียวสำเร็จ/▼ แดงลด/▶ เหลืองทรงตัว ตามเกณฑ์ yrGrowthSymbol() — ห้ามใช้สีเหลืองสดจาก wireframe ตรงๆ
   ใช้ var(--warning-soft) ของธีมแทนไฮไลต์แถว TOTAL) */
.yr-growth-cell { font-weight: 700; white-space: nowrap; }
.yr-growth-icon { margin-right: 4px; }
.yr-total-row td { background: var(--warning-soft); }

/* กรอบตาราง Yearly Report — border/radius เฉยๆ ไม่มี max-height/overflow-y แบบ .db-scroll-table (ผู้ใช้
   ระบุว่าไม่ต้องมี Scroll Bar ที่จอ desktop — มีแค่ 13 แถวพอดีแสดงเต็มได้อยู่แล้ว ไม่ต้องเลื่อนแนวตั้ง) —
   overflow-x:auto (ไม่ใช่ hidden) กันจอแคบ/มือถือที่ 6 คอลัมน์ (Month+4 ปี+Growth) ยัดลงไม่พอจริงๆ ตัดคอลัมน์
   ท้ายๆ (Growth) หายไปเข้าถึงไม่ได้เลยเงียบๆ ถ้าใช้ hidden ตรงๆ — desktop ปกติพอดีอยู่แล้วจะไม่มี scrollbar
   ขึ้นให้เห็นเอง (auto โผล่เฉพาะตอนล้นจริง) */
.yr-table-wrap { border: 1px solid var(--border-soft); border-radius: var(--radius-sm); overflow-x: auto; overflow-y: hidden; }

/* กล่องสรุปเทียบปีล่าสุดกับ 3 ปีก่อนหน้า (Yearly Report) — 3 กล่องเรียงแถว ย่อเป็นคอลัมน์เดียวจอแคบผ่าน
   auto-fit ปกติ ไม่ต้องพึ่ง media query แยก */
.yr-compare-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 20px; }
.yr-compare-tile {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 14px 16px;
  text-align: center;
  box-shadow: var(--shadow-sm);
}
.yr-compare-label { font-size: 0.76rem; color: var(--text-soft); font-weight: 500; margin-bottom: 8px; }
.yr-compare-value { font-family: var(--font-display); font-size: 1.3rem; font-weight: 700; color: var(--text); }
.yr-compare-value .yr-growth-icon { margin-right: 4px; }
/* .yr-compare-value กับ .is-positive/.is-negative/.is-flat (บรรทัด 707-709) specificity เท่ากัน (คลาส
   เดี่ยวทั้งคู่) — ลำดับในไฟล์เป็นตัวตัดสิน ".yr-compare-value" มาทีหลังเลยชนะสีเทาเดิมทับสีบวก/ลบเสมอ ต้อง
   เจาะจงรวมคลาสให้ specificity สูงกว่าเหมือนที่ "table.data-table td.is-positive" ท้ายไฟล์ทำไว้กับตาราง */
.yr-compare-value.is-positive { color: var(--success); }
.yr-compare-value.is-negative { color: var(--danger); }
.yr-compare-value.is-flat { color: var(--warning); }

/* ส่วนต่างตัวเลขจริง (โผล่เฉพาะโหมด Year to Date) — วางเหนือแถว % ขนาด font เท่า .yr-compare-label
   ด้านบน (ตามที่ผู้ใช้ระบุ "Font ไม่ใหญ่ เท่ากับบรรทัดบนที่อยู่ในกล่อง") สีตามเครื่องหมายเดียวกับ % */
.yr-compare-diff { font-size: 0.76rem; font-weight: 600; margin-bottom: 4px; }
.yr-compare-diff.is-positive { color: var(--success); }
.yr-compare-diff.is-negative { color: var(--danger); }
.yr-compare-diff.is-flat { color: var(--warning); }

/* แถว TOTAL ท้ายตาราง SALES BY MONTH — พื้นเน้นแยกจากแถวข้อมูลปกติ */
.sbm-total-row td { background: var(--bg-soft); }

/* ตารางแน่น (หลายคอลัมน์ต่อแถว เช่น ตาราง 12 เดือน+TOTAL ของ SALES BY MONTH/GROWTH/SALES BY QUARTER
   OVERALL) — ย่อ padding/font ลงจาก .data-table ปกติเพื่อให้ตัวเลขพอดีตัวหนังสือมากขึ้น อ่านง่ายขึ้นโดยไม่
   ต้องเลื่อนแนวนอนเท่าเดิม */
table.data-table.table-compact { font-size: 0.76rem; }
table.data-table.table-compact thead th { padding: 7px 8px; font-size: 0.64rem; }
table.data-table.table-compact td { padding: 7px 8px; }

/* รายการออเดอร์ (การ์ด Realtime Order) — แสดงเป็นกรอบย่อยต่อออเดอร์แทนตารางแถวยาว (1 กรอบ = 1 ออเดอร์
   รวมทุกโมเดลในออเดอร์นั้นไว้ในกรอบเดียวกัน ไม่แยกแถวต่อโมเดล) ครอบด้วยกล่องเลื่อนแนวตั้งเหมือน
   .db-scroll-table กันรายการเยอะแล้วหน้ายืดยาวเกินไป — ใช้ CSS Grid (ไม่ใช่ flex-wrap) + กำหนดความสูง
   กรอบคงที่เท่ากันทุกกรอบเสมอ (ตามที่ผู้ใช้ระบุ "จัดระเบียบให้ขนาดเท่ากันด้วย") ไม่ว่าออเดอร์นั้นจะมีกี่
   โมเดล — ถ้ารายการโมเดลในกรอบมากกว่าที่แสดงพอดี ให้เลื่อนดูภายในกรอบเอง (.order-chip-models) แทนที่จะ
   ดันความสูงกรอบให้ไม่เท่ากรอบอื่น */
.order-chip-scroll { max-height: 420px; overflow-y: auto; padding: 2px; }
.order-chip-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.order-chip {
  display: flex;
  flex-direction: column;
  height: 156px;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: var(--surface);
}
.order-chip-head {
  display: flex;
  flex: none;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border-soft);
}
.order-chip-no { font-weight: 700; font-size: 0.82rem; color: var(--text); white-space: nowrap; }
.order-chip-customer {
  font-size: 0.74rem;
  color: var(--text-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: right;
}
.order-chip-models { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; }
.order-chip-model { display: flex; flex: none; justify-content: space-between; gap: 8px; font-size: 0.78rem; color: var(--text-soft); }
.order-chip-model b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }

/* กราฟย่อยแยกทีละหมวด (Small multiples) — ใช้ที่การ์ด SALES BY QUARTER OVERALL (แยกทีละปี) แทนกราฟกลุ่ม
   สีเดียวหลายซีรีส์ (ธีม earth-tone ของเว็บนี้ตรวจสอบด้วย validate_palette.js แล้วไม่ผ่านเกณฑ์แยกแยะสี
   categorical 3-4 สีพร้อมกัน — แยกเป็นกราฟย่อยสีเดียวต่อการ์ดแทน ใช้ตำแหน่ง/หัวข้อบอกความต่าง) */
.chart-facet-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 16px; }
.chart-facet-title { font-size: 0.76rem; font-weight: 700; color: var(--text); text-align: center; margin-bottom: 6px; }

/* Tooltip: hover/focus บนทั้งแท่ง (ไม่ใช่ต่อ segment) แสดงครบทั้ง 3 series ในเดือนนั้นพร้อมกัน
 * ("one tooltip, every series") ค่าตัวเลข (Strong) ขึ้นก่อนชื่อ series (secondary) เสมอ */
.chart-tooltip {
  position: fixed;
  z-index: 60;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-sm);
  padding: 9px 11px;
  font-size: 0.76rem;
  pointer-events: none;
  box-shadow: var(--shadow-md);
  min-width: 160px;
}
.chart-tooltip-title { font-weight: 700; margin-bottom: 6px; font-size: 0.72rem; opacity: 0.85; }
.chart-tooltip-row { display: flex; align-items: center; gap: 7px; padding: 2px 0; }
.chart-tooltip-key { width: 12px; height: 3px; border-radius: 2px; flex: none; }
.chart-tooltip-value { font-weight: 700; }
.chart-tooltip-label { color: rgba(255, 255, 255, 0.72); margin-left: auto; padding-left: 10px; }
.chart-tooltip-total-row { border-top: 1px solid rgba(255, 255, 255, 0.2); margin-top: 4px; padding-top: 6px; }

/* ---------------- Home page — Pie chart (Sales by Country) ----------------
   ไม่มี helper กราฟวงกลมในเว็บนี้มาก่อน — SVG ล้วน (ไม่พึ่ง library ภายนอก) คำนวณ arc จากเปอร์เซ็นต์
   สะสม ใช้โทนสีชุดเดียวกับกราฟแท่งด้านบน วนซ้ำถ้าประเทศมากกว่า 8 (แยกด้วยข้อความใน legend อยู่แล้ว ไม่
   ต้องพึ่งสีล้วนแยกแยะ) — legend แสดงจำนวนคู่ + % ตรงๆ แทนการพึ่ง tooltip ลอย (ใช้ <title> ของ SVG native
   แทนสำหรับ hover เสริม กันต้องเขียนระบบ tooltip ซ้ำกับของกราฟแท่งด้านบน) */
.pie-chart-row { display: flex; align-items: center; gap: 28px; flex-wrap: wrap; }
.pie-chart-svg { width: 200px; height: 200px; flex: none; }
.pie-slice { stroke: var(--surface); stroke-width: 1.5; }
.pie-legend { display: flex; flex-direction: column; gap: 8px; min-width: 200px; flex: 1; }
.pie-legend-item { display: flex; align-items: center; gap: 8px; font-size: 0.82rem; color: var(--text-soft); font-weight: 500; }
.pie-legend-swatch { width: 12px; height: 12px; border-radius: 3px; flex: none; }
.pie-legend-value { margin-left: auto; padding-left: 14px; font-weight: 700; color: var(--text); white-space: nowrap; }
/* รายชื่อประเทศย่อยที่ถูกรวมเป็น "อื่นๆ" ในพาย — ยังแสดงชื่อ+% ทีละประเทศใน legend ตามที่ผู้ใช้ระบุ เยื้อง
   เข้าไปใต้แถว "อื่นๆ" หลัก ไม่มี swatch สี (ใช้สีเดียวกับ "อื่นๆ" ในพายอยู่แล้ว ไม่ต้องแยกสีต่อประเทศ) */
.pie-legend-subitem { padding-left: 20px; font-size: 0.76rem; color: var(--text-faint); font-weight: 400; }
.pie-legend-subitem .pie-legend-value { font-weight: 600; color: var(--text-soft); }

/* การ์ด By Country — 3 ปีเรียงเป็นกล่องข้างกัน (.chart-facet เดิมไม่มี box style เพราะกราฟแท่งเก่าที่เคย
   ใช้ .chart-facet-grid ไม่มี legend ให้ต้องล้อมกรอบ) เพิ่ม box ให้เพราะพาย+legend ต้องการแยกจากกันชัดเจน
   กว่า — พาย/legend เรียงซ้อนแนวตั้งแทนเคียงข้างกัน (ฐาน .pie-chart-row เป็นแนวนอน ออกแบบไว้สำหรับการ์ด
   กว้างเดี่ยวๆ ไม่ใช่ 3 กล่องแคบเรียงแถว) ย่อขนาดพายลงให้พอดีกล่องแคบ */
.chart-facet {
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 16px;
  background: var(--surface);
}
.chart-facet .pie-chart-row { flex-direction: column; align-items: center; gap: 14px; }
.chart-facet .pie-chart-svg { width: 100%; max-width: 150px; height: auto; }
.chart-facet .pie-legend { min-width: 0; width: 100%; }
.country-facet-total { text-align: center; font-size: 0.78rem; color: var(--text-soft); font-weight: 600; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border-soft); }

.page-home .pie-legend-value { font-variant-numeric: tabular-nums; }

/* ---------------- Home page ----------------
   สีทั้งเว็บตอนนี้เป็น Dark Tech Minimal theme เดียวกันหมดแล้ว (ดู :root ด้านบน) หน้า Home จึงไม่ต้อง
   override สีแยกอีกต่อไป (เคยลองมาทั้งธีมมืดเรืองแสง HUD และธีม minimal สว่างเฉพาะหน้านี้ก่อนหน้านี้) —
   เดิมมี font override แยกของหน้านี้ (IBM Plex Sans Thai แทน Kanit) ตอนเปลี่ยนธีมสีรอบก่อน ตอนนี้ฟอนต์
   ทั้งแอปเปลี่ยนเป็น Poppins/Noto Sans Thai ผ่าน :root ตัวเดียวกันหมดแล้ว (ดูด้านบน) จึงตัด override นี้
   ทิ้งไป ให้หน้า Home ใช้ฟอนต์เดียวกับทั้งแอปแทน */

/* กรอบการ์ดให้มีระยะห่างหัวข้อ/เนื้อหาชัดเจนขึ้นแบบแผงควบคุมเรียบๆ */
.page-home .card-title-row {
  border-bottom: 1px solid var(--border-soft);
  padding-bottom: 14px;
}

/* ตัวเลขทุกจุด (ยอดเดียว/ยอดต่อ tile/ยอดต่อแถวตาราง/ป้ายบนแท่งกราฟ) ใช้ฟอนต์เดียวกับข้อความอื่นทั้งหน้า
   (var(--font-body) ที่สืบทอดมาจาก body อยู่แล้ว — ไม่ต้องประกาศ font-family ซ้ำตรงนี้) เปลี่ยนแค่
   font-variant-numeric ให้ตัวเลขเรียงคอลัมน์ตรงกัน (tabular figures) — ขนาดปรับ
   ให้เป็นสเกลเดียวกันทั้งหน้า: ยอดเดี่ยว (.stat-value) ใหญ่สุด > ยอดต่อ tile (.stat-metric-number) >
   ยอดต่อแถวตาราง (.stat-segment-qty/amount) เล็กสุด แทนที่จะสุ่มขนาดคนละจุดแบบเดิม */
.page-home .stat-value,
.page-home .stat-metric-number,
.page-home .stat-segment-qty,
.page-home .stat-segment-amount,
.page-home .chart-bar-total,
.page-home .chart-gridline-label,
.page-home .chart-tooltip-value {
  font-variant-numeric: tabular-nums;
}

.page-home .stat-value { font-size: 2.1rem; letter-spacing: 0.01em; }
.page-home .stat-metric-number { font-size: 1.45rem; }
/* เดิม 1.05rem — ย่อลงเพราะหมวด Sneakers ตอนนี้มีกล่อง .stat-tile-segmented เรียงกันหลายกล่องต่อแถว
   (205-S แยก 2 ช่วง/3 ช่วง/โมเดลอื่นๆ) กล่องแคบลงกว่าตอนออกแบบขนาดนี้ไว้ครั้งแรก ตัวเลขคู่/บาทที่เป็น
   nowrap แล้ว (ดู #home-dashboard-card ด้านบน) ต้องใช้ฟอนต์เล็กพอจะพอดีคอลัมน์แคบๆ ไม่งั้นจะล้นกล่อง */
.page-home .stat-segment-qty,
.page-home .stat-segment-amount { font-size: 0.85rem; }

/* ---------------- Home page — Master Mod cards (Panel B/C) ----------------
   การ์ดต่อ Master Mod (Model: 200 / Model: 205-S ฯลฯ) — ยอดรวมตัวหนาบนสุด คั่นเส้น แล้วแบ่งเป็น
   sub-column เท่าๆ กัน (Regular/Special หรือ HF/37-43/44-49) มีเส้นคั่นแนวตั้งระหว่างคอลัมน์ ตามภาพ
   ตัวอย่างที่ผู้ใช้ส่งมา — Panel B และ Panel C ใช้ component เดียวกันนี้ (ตัวเลขชุดเดียวกัน จัดกลุ่ม
   ต่างกันแค่ segments ที่ส่งเข้ามา) */
.mastermod-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
  margin-bottom: 28px;
}

.mastermod-card {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 20px 22px;
  box-shadow: var(--shadow-sm);
}

.mastermod-card-title {
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 14px;
}

.mastermod-card-total { text-align: center; padding-bottom: 14px; }
.mastermod-pairs { font-family: var(--font-display); font-size: 1.6rem; font-weight: 700; color: var(--text); }
.mastermod-thb { font-size: 0.9rem; color: var(--success); font-weight: 600; margin-top: 4px; }
.mastermod-unit { font-size: 0.68em; font-weight: 500; margin-left: 3px; color: var(--text-soft); }

.mastermod-card-divider { border-top: 1px solid var(--border-soft); margin-bottom: 14px; }

.mastermod-segments { display: flex; }
.mastermod-segment { flex: 1; min-width: 0; text-align: center; padding: 0 10px; }
.mastermod-segment + .mastermod-segment { border-left: 1px solid var(--border-soft); }
.mastermod-segment-label { font-size: 0.76rem; color: var(--text-soft); font-weight: 500; margin-bottom: 6px; }
.mastermod-segment-pairs { font-family: var(--font-display); font-size: 1.1rem; font-weight: 700; color: var(--text); }
.mastermod-segment-thb { font-size: 0.76rem; color: var(--success); font-weight: 600; margin-top: 2px; }

.page-home .mastermod-pairs,
.page-home .mastermod-segment-pairs {
  font-variant-numeric: tabular-nums;
}

/* การ์ด Monthly Report (หน้า Home) — reuse .mastermod-card เดิม แค่เพิ่มเส้นขอบบนสีเน้นตามกลุ่ม (navy/rust
   ของธีม แทนสีแดง/น้ำเงินจาก wireframe ที่ผู้ใช้ส่งมา — ธีมนี้ห้ามใช้สีนอกชุด CSS var) และ .mastermod-dual
   สำหรับกล่อง 205-S ที่ต้องโชว์ 2 มุมมอง (HF/MD/LR แยกไซส์ กับ HF/37+ รวมไซส์) เคียงข้างกันในกล่องเดียว */
.mastermod-card.mr-card-200 { border-top: 3px solid var(--navy); }
.mastermod-card.mr-card-205s { border-top: 3px solid var(--rust); }

/* กล่อง 200 มีแค่ 2-3 segment แต่กล่อง 205-S ต้องโชว์ 5 segment พร้อมกัน (2 มุมมองเคียงกัน) ในพื้นที่
   เท่ากันแบบ 1fr/1fr เดิมทำให้ตัวเลข/บาทแน่นเกินจนขึ้นบรรทัดชนกัน — ลดความกว้าง 200 ให้ 205-S ได้พื้นที่
   มากขึ้นตามที่ผู้ใช้ระบุ ("ลดความกว้างของกล่อง 200") — จอแคบยุบเป็นคอลัมน์เดียวเหมือนเดิม (ดู media query
   max-width:767px) */
/* min-width:0 บนกล่อง (grid item) จำเป็นมาก — ปกติ grid item มี min-width:auto (ยึดตาม min-content ของ
   ลูกข้างในเสมอ) ทำให้กล่อง 205-S (5 segment ในแถวเดียว) ดันความกว้าง track เกิน 3fr ที่ตั้งไว้จริง ทะลุ
   ขอบเพจทั้งหน้าไปเลยตอนจอกลางๆ (เช่น 768px แท็บเล็ต) — ใส่ min-width:0 ให้ track หดตาม fr จริงๆ ได้ */
#mr-body .mastermod-grid { grid-template-columns: 2fr 3fr; }
#mr-body .mastermod-grid > .mastermod-card { min-width: 0; }

.mastermod-dual { display: flex; }
.mastermod-dual > .mastermod-segments { flex: 1; min-width: 0; }
.mastermod-dual > .mastermod-segments + .mastermod-segments { border-left: 1px solid var(--border-soft); padding-left: 14px; margin-left: 4px; }
/* มุมมองที่ 2 (HF/37+ รวมไซส์) ใช้สี font ต่างจากมุมมองที่ 1 (HF/MD/LR แยกไซส์) ให้แยกชุดข้อมูลออกจากกัน
   ได้ง่ายด้วยตา ตามที่ผู้ใช้ระบุ — ใช้ var(--sky) ของธีม ไม่ใช่สีใหม่ */
.mastermod-dual > .mastermod-segments + .mastermod-segments .mastermod-segment-pairs { color: var(--sky); }

/* การ์ด Order Realtime (หน้า Home) — กรอบสรุปต่อลูกค้า (1 กรอบ = 1 ลูกค้า) แสดง 3DG Code/ชื่อลูกค้า/Order
   No./รายการตาม PCat4/ยอดรวม — ความสูงขยายตามเนื้อหาจริง ไม่ใช้ scrollbar ภายในกรอบ (ตามที่ผู้ใช้ระบุ) —
   align-items:start กัน Grid ยืดทุกกรอบในแถวเดียวกันให้สูงเท่ากรอบที่สูงสุดอัตโนมัติ (แต่ละกรอบสูงตามเนื้อหา
   ของตัวเองจริงๆ) — ทุกช่องตัวหนังสือยังคงกำหนด nowrap+ellipsis+min-width:0 ป้องกันตกบรรทัด/ล้นกรอบเหมือนเดิม */
.or-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; align-items: start; }
.or-box {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  background: var(--surface);
  padding: 12px 14px;
  box-shadow: var(--shadow-sm);
}
.or-box-code {
  flex: none;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.98rem;
  color: var(--navy);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.or-box-customer {
  flex: none;
  font-size: 0.74rem;
  color: var(--text-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 2px;
  margin-bottom: 8px;
}
.or-box-divider { flex: none; border-top: 1px solid var(--border-soft); margin-bottom: 8px; }
/* รายการแยกเป็นชุดย่อยต่อออเดอร์ (ไม่รวมทุกออเดอร์ปนกันเป็นก้อนเดียว) — .or-box-order คั่นด้วยเส้นประบางๆ
   ระหว่างแต่ละออเดอร์ ตามที่ผู้ใช้ระบุ "แยกออเดอร์ด้วย แต่อยู่ในกล่องเดียวกัน" */
.or-box-orders { flex: none; display: flex; flex-direction: column; gap: 8px; }
.or-box-order + .or-box-order { padding-top: 8px; border-top: 1px dashed var(--border-soft); }
.or-box-orderno {
  flex: none;
  font-weight: 600;
  font-size: 0.72rem;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: 4px;
}
.or-box-lines { flex: none; display: flex; flex-direction: column; gap: 4px; }
.or-box-line { flex: none; display: flex; gap: 8px; font-size: 0.74rem; color: var(--text-soft); }
.or-box-line span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.or-box-line b { flex: none; color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.or-box-total {
  flex: none;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border-soft);
  font-size: 0.8rem;
}
.or-box-total span { color: var(--text-soft); white-space: nowrap; }
.or-box-total b { color: var(--success); font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }

/* ---------------- Buttons ---------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 20px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-family: var(--font-body);
  font-size: 0.87rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.15s ease;
  white-space: nowrap;
}

.btn-primary {
  background: var(--gold);
  color: #fff;
}
.btn-primary:hover { background: var(--gold-deep); }

.btn-secondary {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
}
.btn-secondary:hover { background: var(--bg-soft); }

.btn-ghost {
  background: transparent;
  color: var(--text-soft);
  border: 1px dashed var(--border);
}
.btn-ghost:hover { color: var(--text); border-color: var(--gold); }

.btn-danger {
  background: var(--danger-soft);
  color: var(--danger);
}
.btn-danger:hover { filter: brightness(0.96); }

/* ปุ่ม Payment ในตาราง Summary — เปลี่ยนเป็นดำ/ตัวหนังสือขาวเมื่อออเดอร์เป็น Payment Done แล้ว */
.btn-payment-done {
  background: #000;
  color: #fff;
}
.btn-payment-done:hover { filter: brightness(1.2); }

.btn-sm { padding: 7px 14px; font-size: 0.8rem; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-block { width: 100%; }

/* คงลำดับปุ่ม Edit / Issue PI / MAC5 / Finish Order / Delete ให้ตรงกันทุกแถวในตาราง Summary
   ทุกแถวต้องอยู่บรรทัดเดียว — ไม่ปล่อยให้ปุ่ม wrap ขึ้นบรรทัด 2 อีกต่อไป จึงลด padding/font
   ของปุ่มกลุ่มนี้ลงอีกนิด (ไม่กระทบ btn-sm ที่ใช้หน้าอื่น) ให้ 5 ปุ่มพอดีอยู่ในคอลัมน์เดียวเสมอ */
.order-actions {
  display: flex;
  gap: 6px;
  flex-wrap: nowrap;
  justify-content: flex-end;
}
.order-actions .btn {
  padding: 6px 10px;
  font-size: 0.76rem;
  white-space: nowrap;
}

/* ปุ่มที่ชื่อยาว (Create SO/Finish Order/Add Docs) มีชื่อย่อสำรอง (btn-label-short) ซ่อนไว้เฉย ๆ
   บนจอกว้าง — โผล่มาแทนที่ชื่อเต็มเฉพาะจอแคบ (ดู breakpoint ด้านล่าง) เพื่อให้ยังพอดี 1 แถวได้แม้
   คอลัมน์ order-actions จะแคบลงมากก็ตาม */
.btn-label-short { display: none; }

/* ปุ่มที่ข้อความสลับยาว/สั้นได้ (Create SO <-> Re-issue SO) — fix ความกว้างเท่าข้อความยาวสุดเสมอ กัน
   ปุ่ม/แถวเลื่อนขยับตอนข้อความเปลี่ยนความยาว (ตามที่ผู้ใช้ระบุ) — ลดขนาด/font ให้แคบกว่า .order-actions
   .btn ปกติอีกขั้น (ตามที่ผู้ใช้ระบุ) ต้อง scope ผ่าน .order-actions นำหน้าเพื่อให้ specificity สูงกว่า
   .order-actions .btn ไม่งั้นค่า font/padding จะโดนทับเงียบๆ — min-width ยังคุมตามชื่อยาวสุด "Re-issue SO"
   (btn-label-full) กันตัวหนังสือล้นออกนอกกรอบปุ่มตอนจอกว้างที่โชว์ชื่อเต็ม */
.order-actions .create-so-btn {
  padding: 5px 8px;
  font-size: 0.7rem;
}
.create-so-btn { min-width: 80px; }

/* ปุ่ม 3 ตัวฝั่ง Shipping (Create PI/Create INV&PL/LDG RPT) — คลาสพวกนี้ใช้เฉพาะหน้า Shipping ไม่กระทบ
   Summary/Arrangement/Order List ที่ใช้ .order-actions ร่วมกัน — ลดขนาด/font ให้แคบกว่า .order-actions
   .btn ปกติอีกขั้น (ตามที่ผู้ใช้ระบุ) ต้อง scope ผ่าน .order-actions นำหน้าด้วยเพื่อให้ specificity สูงกว่า
   .order-actions .btn (2 class เท่ากัน แต่ต้องแมตช์คลาสเฉพาะนี้ก่อน) ไม่งั้นค่า font/padsize จะโดนทับ
   เงียบๆ — min-width ปรับตามความยาวข้อความจริงที่ font ใหม่ กันตัวหนังสือล้นออกนอกกรอบปุ่ม (ยาวสุดคือ
   "Re-create INV&PL") */
.order-actions .issue-pi-btn,
.order-actions .issue-invpl-btn,
.order-actions .invpl-btn-vat7,
.order-actions .issue-ldgrpt-btn {
  padding: 5px 8px;
  font-size: 0.7rem;
}
.issue-pi-btn { min-width: 86px; }
.issue-invpl-btn, .invpl-btn-vat7 { min-width: 118px; }
.invpl-btn-vat7 { opacity: 0.45; }
.invpl-btn-vat7:hover { opacity: 0.7; }
.issue-ldgrpt-btn { min-width: 78px; }

/* ตาราง Summary — เดิมใช้ table-layout:fixed + ตัดจบด้วย … กันตัวหนังสือทับซ้อนคอลัมน์ข้างๆ แต่การตัด …
   คือ "ตัวหนังสือหาย" ตามความหมายจริง ขัดกับกติกาที่ต้องเห็นตัวหนังสือครบเสมอทุก platform (เห็นชัดตอน
   เจอชื่อลูกค้า/เลขที่ยาวผิดปกติ) — เปลี่ยนมาใช้แพทเทิร์นเดียวกับตารางอื่นทั้งเว็บแทน (.matrix-wrap/
   .db-scroll-table): ปล่อยให้คอลัมน์กว้างตามเนื้อหาจริง (table-layout: auto) แล้วให้ตัวครอบเลื่อน
   แนวนอนแทนตัดคำ (ดู #orders-table/#shipping-table ด้านล่างของไฟล์ที่ใส่ overflow-x:auto ให้ตลอด ไม่ใช่
   แค่จอแคบ) — col.col-* ด้านล่างยังคงไว้เป็นสัดส่วนเริ่มต้น/คำแนะนำความกว้าง ไม่ใช่ค่าตายตัวบังคับอีกต่อไป */
table.data-table.summary-table { table-layout: auto; font-size: 0.8rem; }
table.data-table.summary-table thead th { white-space: nowrap; font-size: 0.68rem; }

/* เลื่อนแนวนอนเสมอ ไม่ใช่แค่จอแคบ (เดิมมีเงื่อนไข @media max-width:1400px เท่านั้น) — สอดคล้องกับ
   .matrix-wrap/.db-scroll-table ที่เหลือทั้งเว็บ ตอนนี้ตารางไม่ตัดคำแล้วจึงต้องมีทางเลื่อนดูส่วนที่ล้น
   เสมอไม่ว่าจอกว้างแค่ไหน */
#orders-table, #shipping-table {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* ปัญหาที่ผู้ใช้แจ้ง: ย่อหน้าต่างเหลือ ~half จอ (เช่น 1261px) แล้วปุ่มขวาสุด (Delete) ของตาราง Summary/
   Shipping "หาย" ทั้งที่จริงๆ ยังเลื่อนดูได้ (overflow-x:auto ทำงานถูกต้อง scrollLeft ไปได้ปกติ) — สาเหตุ
   คือ scrollbar แนวนอนของ OS สมัยใหม่ (macOS/Windows overlay scrollbar) เป็นแบบซ่อนอัตโนมัติ ไม่โชว์
   จนกว่าจะ hover/ลากอยู่ ผู้ใช้เลยไม่รู้ว่ามีส่วนที่เลื่อนดูได้อยู่ ดูเหมือนตัวหนังสือ/ปุ่มหายไปเฉยๆ —
   แก้ด้วยการบังคับให้ scrollbar แนวนอนของ 2 ตารางนี้แสดงตลอดเวลาแบบ classic (ไม่ overlay) ด้วยสไตล์
   custom ด้านล่าง แทนที่จะพึ่งพฤติกรรม auto-hide ของแต่ละ OS/browser (::-webkit-scrollbar สำหรับ Chrome/
   Safari/Edge, scrollbar-width/scrollbar-color สำหรับ Firefox) — ไม่กระทบ layout/column width/breakpoint
   เดิมเลย แค่ทำให้ "มีอะไรให้เลื่อนดูอยู่นะ" มองเห็นได้ชัดเจนตลอด ไม่ต้องเดา */
#orders-table, #shipping-table {
  scrollbar-width: auto;
  scrollbar-color: var(--gold) var(--surface-alt);
  padding-bottom: 4px;
}
#orders-table::-webkit-scrollbar, #shipping-table::-webkit-scrollbar {
  height: 10px;
}
#orders-table::-webkit-scrollbar-track, #shipping-table::-webkit-scrollbar-track {
  background: var(--surface-alt);
  border-radius: 6px;
}
#orders-table::-webkit-scrollbar-thumb, #shipping-table::-webkit-scrollbar-thumb {
  background: var(--gold);
  border-radius: 6px;
}
#orders-table::-webkit-scrollbar-thumb:hover, #shipping-table::-webkit-scrollbar-thumb:hover {
  background: var(--gold-deep);
}

/* คอลัมน์ตาราง Summary (dashboard.js) — เดิมกำหนดกว้างตายตัวด้วย inline style="width:%" ตรงๆ ที่
   <col> ย้ายมาเป็นคลาสแทนเพื่อให้ breakpoint ด้านล่างสั่ง override ความกว้างได้โดยไม่ต้องใช้ !important
   (inline style ชนะ stylesheet เสมอไม่ว่า media query จะระบุอะไรก็ตาม)

   สัดส่วนนี้ปรับใหม่หลังเปลี่ยนฟอนต์แอปเป็น Poppins (กว้างกว่า Kanit เดิมต่อตัวอักษร) — วัดจริงด้วย
   canvas.measureText() แล้วพบว่า "เลขที่ออเดอร์" (คอลัมน์ตัวหนา 600 weight เช่น "SJZ-2ND-AUG-26") ตกบรรทัด
   และ badge "Payment Pending" (ยาวสุดในบรรดา Status badge) ถูกตัดขอบ เพราะ 2 คอลัมน์นี้แคบเกินไปสำหรับ
   Poppins แล้ว — ยืมพื้นที่มาจากคอลัมน์ Actions (col-actions) ที่มี slack เหลือเยอะมาก (ปุ่ม 4 ปุ่ม
   Edit/Re-issue SO/Payment/Delete รวมกันจริงกว้างแค่ ~260px จาก 35% ที่เคยให้ไว้ ~455px — เหลือเกินความ
   จำเป็นเกือบเท่าตัว) คอลัมน์อื่น (customer/qty/total/so/rev) วัดแล้วยังพอดีเหมือนเดิม ไม่ต้องแตะ */
.summary-table col.col-select { width: 4%; }
.summary-table col.col-order-no { width: 13%; }
.summary-table col.col-customer { width: 10%; }
.summary-table col.col-mastermod { width: 8%; }
.summary-table col.col-qty { width: 6%; }
.summary-table col.col-total { width: 10%; }
.summary-table col.col-so { width: 8%; }
.summary-table col.col-rev { width: 6%; }
.summary-table col.col-status { width: 16%; }
.summary-table col.col-actions { width: 27%; }
/* Shipping's table only has 5 columns (เลขที่ออเดอร์/ลูกค้า/ยอดรวม/Status/actions — ตัด Master
   Mod/จำนวน/SO/REV ออกตามที่ผู้ใช้ระบุ) — override ความกว้างทั้งหมดให้รวมกันครบ 100% เอง แทนที่จะพึ่ง
   .summary-table ด้านบน (มีคอลัมน์มากกว่า สัดส่วนรวมกันไม่ครบ 100% ถ้าใช้ปนกัน) */
.shipping-table col.col-order-no { width: 15%; }
.shipping-table col.col-customer { width: 25%; }
.shipping-table col.col-total { width: 15%; }
.shipping-table col.col-status { width: 15%; }
.shipping-table col.col-actions { width: 30%; }
table.data-table.summary-table td {
  white-space: nowrap;
}
.cell-truncate { overflow: hidden; text-overflow: ellipsis; }

/* คอลัมน์ Order No. ในตาราง Summary ต้องแสดงครบเสมอบรรทัดเดียว ห้ามตกบรรทัด (ตามที่ผู้ใช้ระบุ — เดิม
   เคยยอมให้ขึ้นบรรทัด 2 ตอนคอลัมน์แคบ แต่ผู้ใช้แจ้งว่าไม่ต้องการแบบนั้นแล้ว ให้คอลัมน์ ลูกค้า เป็นตัวที่
   หด/ตัดจบแทน — ดู .cell-truncate ด้านล่างที่เพิ่ม max-width เพื่อบังคับ ellipsis ให้จริง) สืบทอด
   white-space: nowrap จากกฎ td ทั่วไปด้านบนอยู่แล้ว ไม่ต้อง override เพิ่ม */

/* ลูกค้า (.cell-truncate) คือคอลัมน์ที่ยอมหด/ตัดจบด้วย … เมื่อพื้นที่ไม่พอ (มี title="" ให้ hover ดูชื่อ
   เต็มอยู่แล้วจาก dashboard.js) — table-layout: auto ต้องมี max-width ชัดเจนถึงจะบังคับ ellipsis ได้จริง
   ไม่งั้นคอลัมน์จะขยายตามเนื้อหาเสมอเหมือนคอลัมน์อื่น (วัดแล้วชื่อลูกค้าไทยยาวๆ ทั่วไปยังอ่านได้พอสมควร
   ที่ความกว้างนี้ก่อนโดนตัด) — เฉพาะหน้า Summary เท่านั้นตามที่ผู้ใช้ระบุ ตาราง Shipping ก็ใช้คลาส
   .summary-table ร่วมด้วย (ดู .shipping-table col.col-customer ด้านบนที่ตั้ง 25% ไว้กว้างกว่าตั้งใจ) จึง
   ต้อง :not(.shipping-table) กันไม่ให้ไปตัดคอลัมน์ลูกค้าของหน้า Shipping โดยไม่ได้ตั้งใจ */
table.data-table.summary-table:not(.shipping-table) td.cell-truncate {
  max-width: 200px;
}

/* บางหน้า (Summary, Input Order) เดิมกว้างกว่าหน้าอื่น (1420px vs 1180px) แต่ตอนนี้ .page ทั้งคู่ใช้พื้นที่
   เต็มจอเหมือนกันแล้ว (Tier A ด้านบน) — .page-wide เลยไม่มีผลต่างจาก .page เปล่าๆ อีกต่อไปที่ความกว้าง
   คงเหลือ class ไว้เฉยๆ (ไม่ลบออกจาก 6 หน้าที่ใช้อยู่) กันต้องแก้ไฟล์ HTML เพิ่มโดยไม่จำเป็น */
.page.page-wide { max-width: none; }

/* จอ ultrawide/4K จริงๆ (≥2400px — เกินความกว้างจอ laptop/desktop ปกติทั้งหมด 1366-2160px) จำกัดเพดานไว้
   กันตารางตัวเลข/ราคาโปร่งจนอ่านยาก จอทั่วไปทุกขนาดปกติยังคงยืดเต็ม 100% ตาม Tier A เหมือนเดิม */
@media screen and (min-width: 2400px) {
  .page, .page.page-wide, .topbar-inner {
    max-width: 2200px;
    margin-inline: auto;
  }
}

/* แบ่งกลุ่มออเดอร์ตามเดือนในหน้า Summary ให้เห็นชัดเจน — หัวข้อเดือนพร้อมเส้นคั่นก่อนตารางของ
   เดือนนั้น เรียงจากเดือน/ปีล่าสุดไปเก่าสุด */
.summary-month-group + .summary-month-group {
  margin-top: 28px;
}
.summary-month-header {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 14px;
  padding-bottom: 8px;
  margin-bottom: 10px;
  border-bottom: 2px solid var(--gold);
}
.summary-month-header h3 {
  color: var(--gold-deep);
}
.summary-month-count {
  margin-left: auto;
}

/* กรอบสรุปยอด (ยอดรวมจำนวนเงิน / จำนวนออเดอร์) เรียงอยู่แถวเดียวกับหัวข้อเดือน (MMM YY)
   ด้านบน — ย่อขนาดลงให้เล็ก กะทัดรัด เท่ากันทั้ง 2 กรอบ (ต่างจาก so-summary-box ที่ใหญ่กว่า ใช้ใน
   บริบทอื่นที่มีพื้นที่กว้างกว่า) */
.summary-month-stats {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
.summary-stat-box {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 4px 10px;
  box-shadow: var(--shadow-sm);
}
.summary-stat-label {
  font-size: 0.6rem;
  font-weight: 700;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
}
.summary-stat-value {
  font-size: 0.76rem;
  font-weight: 700;
  color: var(--gold-deep);
  white-space: nowrap;
}
.summary-stat-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
}
.summary-stat-chip {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--gold-deep);
  white-space: nowrap;
}

/* ---------------- Forms ---------------- */

label {
  display: block;
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--text-soft);
  margin-bottom: 6px;
}

.field { margin-bottom: 18px; }

/* Highlight กระพริบสีแดง — ใช้ชี้เป้าช่องกรอกที่ต้องการดึงความสนใจเป็นพิเศษ (เช่น "8. Carrier"
   ในหน้า Issue Invoice & Packing List) กระพริบวนไปเรื่อยๆ ไม่หยุด ไม่ผูกกับ field ใดเป็นการเฉพาะ
   ใช้ซ้ำกับ field อื่นได้แค่แปะคลาสนี้เพิ่ม */
@keyframes field-blink-red { 0%, 100% { box-shadow: 0 0 0 2px transparent; background: transparent; } 50% { box-shadow: 0 0 0 2px var(--danger); background: var(--danger-soft); } }
.field-blink-red { border-radius: var(--radius-md); padding: 6px 8px; margin: -6px -8px 12px; animation: field-blink-red 1s ease-in-out infinite; }

input[type="text"],
input[type="number"],
input[type="date"],
input[type="tel"],
select,
textarea {
  width: 100%;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  font-family: var(--font-body);
  font-size: 0.92rem;
  color: var(--text);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--gold);
  box-shadow: 0 0 0 3px var(--gold-soft);
}

/* ช่องที่ค่าเริ่มต้นมี default/auto-fill ใดๆ ก็ตาม (ดึงจากฐานข้อมูลลูกค้า/ธนาคาร, เอกสารอื่นที่ออกไปแล้ว
   เช่น PI/SO/Order, เลขที่รันอัตโนมัติ, หรือค่า default ที่ตั้งไว้ตายตัวในโค้ด) แทนที่จะเป็นช่องว่างให้
   พิมพ์กรอกเองล้วนๆ — ทั้งป้ายชื่อและตัวข้อมูลในช่องเป็นสีแดงเตือนไว้ (ให้ผู้ใช้รู้ว่าค่านี้ถูกดึง/ตั้งมา
   ให้แล้ว ควรตรวจสอบก่อนบันทึกจริง) — ใช้กับป๊อปอัป Create SO (dashboard.html), Create PI, Issue
   Invoice & Packing List (shipping.html) ดู .field-from-db ในไฟล์เหล่านั้น */
.field-from-db > label,
.field-from-db input,
.field-from-db select {
  color: var(--danger);
}

/* Free-typed number fields — no spinner arrows */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}

input::placeholder, textarea::placeholder { color: var(--text-faint); }

.form-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0 20px;
}

.hint {
  font-size: 0.78rem;
  color: var(--text-faint);
  margin-top: 4px;
}

/* ---------------- Tabs ---------------- */

.tabs {
  display: flex;
  gap: 8px;
  margin-bottom: 24px;
  flex-wrap: wrap;
}

.tab-btn {
  padding: 11px 22px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-soft);
  background: var(--surface);
  color: var(--text-soft);
  font-weight: 500;
  font-size: 0.92rem;
  cursor: pointer;
  transition: all 0.15s ease;
}

.tab-btn:hover { border-color: var(--gold); color: var(--text); }

.tab-btn.active {
  background: var(--gold-soft);
  border-color: var(--gold);
  color: var(--gold-deep);
}

/* ปุ่ม DOZEN / PAIR สลับหน่วยแสดงผลของตารางใน Order List (ไม่กระทบข้อมูลจริงหรือตาราง
   Quota/Available/Grand Total ด้านบน — เป็นแค่มุมมองการแสดงผลของ Order List เท่านั้น) */
.unit-toggle-btn {
  padding: 5px 14px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-soft);
  background: var(--surface);
  color: var(--text-soft);
  font-weight: 600;
  font-size: 0.72rem;
  cursor: pointer;
  transition: all 0.15s ease;
}
.unit-toggle-btn:hover { border-color: var(--gold); color: var(--text); }
.unit-toggle-btn.active {
  background: var(--gold-soft);
  border-color: var(--gold);
  color: var(--gold-deep);
}

/* ---------------- Tables ---------------- */

table.data-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.89rem;
}

table.data-table thead th {
  text-align: left;
  padding: 10px 12px;
  background: var(--bg-soft);
  color: var(--text-soft);
  font-weight: 600;
  font-size: 0.76rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

/* table.data-table thead th ด้านบนมี specificity สูงกว่า .text-right เฉยๆ (element+class+element+element
   ชนะ class เดี่ยว) ทำให้หัวตารางที่ใส่ class="text-right" ไว้ (เช่น คอลัมน์ปีของ Yearly Report) ไม่ขยับ
   ไปทางขวาจริง ยังชิดซ้ายอยู่เหมือนเดิม ทั้งที่ข้อมูลด้านล่างชิดขวา ดูเหมือนหัวตาราง/ตัวเลขไม่อยู่แนวเดียวกัน
   — ต้อง override เจาะจงอีกชั้นให้ specificity สูงกว่า (เหมือน td.text-danger/td.is-positive ด้านล่างไฟล์) */
table.data-table thead th.text-right { text-align: right; }
table.data-table thead th.text-center { text-align: center; }

table.data-table thead th:first-child { border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
table.data-table thead th:last-child { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }

table.data-table td {
  padding: 12px;
  border-bottom: 1px solid var(--border-soft);
  color: var(--text);
}

/* table.data-table td { color: var(--text) } ด้านบนมี specificity สูงกว่า .text-danger/.text-success
   เฉยๆ (element+class+element ชนะ class เดี่ยว) ทำให้ 2 คลาสนี้ไม่มีผลอะไรเลยเงียบๆ เวลาอยู่ในตาราง
   data-table (เจอตอนลองเปลี่ยนสี Amount/Balance ติดลบ/บวกในตาราง Dashboard Statement แล้วสีไม่ขึ้นตาม
   ทั้งที่ class ถูกใส่จริง) — ต้อง override เจาะจงอีกชั้นให้ specificity สูงกว่า */
table.data-table td.text-danger { color: var(--danger); }
table.data-table td.text-success { color: var(--success); }
table.data-table td.is-positive { color: var(--success); }
table.data-table td.is-negative { color: var(--danger); }
table.data-table td.is-flat { color: var(--warning); }

table.data-table tbody tr:last-child td { border-bottom: none; }
table.data-table tbody tr:hover { background: var(--surface-alt); }

table.data-table input[type="number"],
table.data-table input[type="text"] {
  padding: 7px 9px;
  font-size: 0.87rem;
  text-align: center;
}

.text-right { text-align: right; }
.text-center { text-align: center; }

/* ---------------- Scrollable list (หน้า Database) ----------------
 * ครอบตาราง data-table ที่มีแถวเยอะ (โมเดล/ลูกค้า/รหัสสินค้าอ้างอิง) ให้แสดงสูงสุดประมาณ 10 แถว
 * แล้วเลื่อนดูส่วนที่เหลือด้วย scrollbar แทนที่จะยืดหน้าเว็บยาวลงไปเรื่อยๆ — thead ปักหัวตาราง
 * (sticky) ไว้ด้านบนระหว่างเลื่อนด้วย เห็นชื่อคอลัมน์ตลอด */
.db-scroll-table {
  max-height: 460px;
  overflow-y: auto;
  overflow-x: auto;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
}

.db-scroll-table table.data-table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
}

/* ตาราง MASTER MOD/Size ใน CBM CAL (หน้า CAL) — reuse .db-scroll-table (thead sticky + scroll) แต่ย่อ
   max-height ลงเหลือประมาณ 5 แถวข้อมูล (แถวละ ~44px + หัวตาราง ~37px) ตามที่ผู้ใช้ระบุ แทนค่า 460px เดิม
   ที่ยาวเกินไปสำหรับพื้นที่การ์ดฝั่งซ้ายของหน้า CAL */
.cal-cbm-lines-scroll { max-height: 260px; }

/* ปุ่ม Delete แบบ toggle ที่หัวตาราง/หัวการ์ด (หน้า Database) — ปุ่ม Delete ต่อแถวถูกซ่อนไว้เป็นค่า
   เริ่มต้นเสมอ กันกดโดนพลาด ต้องกดปุ่มนี้ก่อนถึงจะโผล่ขึ้นมาให้กดจริง — active (กำลังอยู่ในโหมดลบ)
   ไฮไลต์เป็นสีแดงอ่อนกำกับไว้ให้เห็นชัดว่าตอนนี้ทุกแถวกดลบได้แล้ว */
.db-toggle-delete-model-btn.active,
#db-cust-toggle-delete-btn.active,
#db-sku-toggle-delete-btn.active {
  background: var(--danger-soft);
  border-color: var(--danger);
  color: var(--danger);
}

/* ---------------- Grid matrix (size x color) ---------------- */

.matrix-wrap {
  overflow-x: auto;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-soft);
}

table.matrix {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.87rem;
  min-width: 640px;
}

/* .matrix-wrap ปัดมุมด้วย border-radius แต่พื้นหลังสีของหัวตาราง/แถว Total (th/.matrix-total-row td)
   เป็นมุมฉากเสมอ ถ้าไม่ปัดมุมเซลล์ริมสุด 4 มุมให้ตรงกันด้วย จะเห็นช่องว่างเป็นรอยหยักที่มุมทั้งสี่
   (ขอบ/แถบสีดูขาด ไม่ต่อเนื่องกับเส้นโค้งของกรอบนอก) — ปัดมุมเซลล์หัว-ท้ายให้ตรงกับ .matrix-wrap เป๊ะ */
table.matrix thead tr:first-child th:first-child { border-top-left-radius: var(--radius-md); }
table.matrix thead tr:first-child th:last-child { border-top-right-radius: var(--radius-md); }
table.matrix tbody tr:last-child td:first-child { border-bottom-left-radius: var(--radius-md); }
table.matrix tbody tr:last-child td:last-child { border-bottom-right-radius: var(--radius-md); }

table.matrix th, table.matrix td {
  padding: 9px 8px;
  text-align: center;
  border-bottom: 1px solid var(--border-soft);
  border-right: 1px solid var(--border-soft);
}

table.matrix th {
  background: var(--bg-soft);
  color: var(--text-soft);
  font-weight: 600;
  font-size: 0.76rem;
}

table.matrix td:first-child, table.matrix th:first-child {
  text-align: left;
  font-weight: 500;
  background: var(--surface-alt);
  position: sticky;
  left: 0;
}

/* วงกลมเล็กแทนสีจริง ต่อท้ายชื่อสีในทุกตารางสี×ไซส์ (สร้างจาก colorSwatchHtml() ใน quota.js) */
.color-swatch {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  margin-left: 5px;
  vertical-align: middle;
  border: 1px solid var(--border);
  flex-shrink: 0;
}
.color-swatch-unknown {
  background: repeating-linear-gradient(45deg, var(--border-soft), var(--border-soft) 2px, var(--surface) 2px, var(--surface) 4px);
}

table.matrix tr:last-child td { border-bottom: none; }
table.matrix td:last-child, table.matrix th:last-child { border-right: none; }

table.matrix input[type="number"] {
  width: 56px;
  padding: 6px;
  text-align: center;
  border: 1px solid var(--border-soft);
}

/* Quota/Available/Grand Total (หน้า Order Management, #quota-grids) ต้องสูงเท่ากันทุกแถว
   แม้ Quota จะมี <input> อยู่ในเซลล์และอีกสองตารางเป็นตัวเลขอ่านอย่างเดียวก็ตาม — บังคับความสูง
   เซลล์ให้คงที่แทนที่จะปล่อยให้ input ดันความสูงของแถวให้มากกว่าตารางอื่น */
#quota-grids table.matrix th,
#quota-grids table.matrix td {
  height: 44px;
  box-sizing: border-box;
}
#quota-grids table.matrix input[type="number"] {
  height: 24px;
  padding: 2px 6px;
  box-sizing: border-box;
}

.text-danger { color: var(--danger); }
.text-success { color: var(--success); }

.grid-2col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin-bottom: 20px;
}

/* หน้า Database — การ์ด Shipping Options: 4 list อิสระ (Type of Shipment/Package/Delivery/Port of
   Loading) เรียงเป็น 4 คอลัมน์เท่ากัน — ห่อเหลือ 2 คอลัมน์บนจอแคบกันตัวเลือกที่มีชื่อยาวถูกบีบจนอ่านยาก */
.grid-4col {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
}
/* breakpoint ของ .grid-4col ย้ายไปรวมกับ Tier B ("Responsive: A/B/C" ด้านล่างของไฟล์) แล้ว */

.option-list-label {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-bottom: 8px;
}

.option-list-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 10px;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  margin-bottom: 6px;
  font-size: 0.86rem;
}
.option-list-row .btn { padding: 0 8px; line-height: 1.6; }

/* Quota / Available / Grand Total (หน้า Order Management) — เรียงแถวเดียว 3 คอลัมน์เท่ากัน
   ตารางเล็กลงกว่าเดิมมาก (แต่ยังอ่านได้) และแยก theme สีต่อการ์ดให้ต่างกันชัดเจน */

/* ตรึง 3 การ์ดนี้ไว้ใต้แถบเมนูตลอดเวลาที่เลื่อนดู Order List ยาวๆ ด้านล่าง — top ถูกเซ็ตเป็น inline
   style จาก JS เสมอ (ดู syncQuotaGridsStickyTop() ใน quota.js) เพราะความสูงจริงของแถบเมนู (.topbar)
   ไม่คงที่ (ขึ้นกับ breakpoint — ดู .topbar-inner flex-wrap ด้านล่าง) ค่า 0 ที่นี่เป็นแค่ fallback
   ก่อน JS ตั้งค่าจริงตอนโหลดหน้าเสร็จ — background ทึบสีเดียวกับพื้นหลังหน้าเว็บกันเนื้อหาที่เลื่อน
   ผ่านด้านล่างทะลุเห็นผ่านช่องว่าง (gap) ระหว่าง 3 การ์ด — z-index ต่ำกว่า .topbar (50) เสมอ กันบัง
   เมนูตอนเลื่อนขึ้นสุด */
#quota-grids {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--bg);
}

#quota-grids .quota-grid-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  margin-bottom: 20px;
  align-items: start;
}

#quota-grids .quota-grid-row .card,
#quota-grids .quota-grid-row .card + .card {
  border-top: 3px solid var(--border-soft);
  padding: 14px;
  margin-top: 0;
}
/* ใช้ var(--honey) แทน var(--gold) เดิม — --gold ตอนนี้คือสีฟ้าแบรนด์หลัก (ปุ่ม/focus ring/nav
   active ทั้งเว็บ) ถ้าการ์ด Quota ใช้สีเดียวกันจะกลืนกับการ์ด Grand Total (theme-sky ก็โทนฟ้า/ม่วง
   ใกล้กัน) แยกไม่ออกว่าการ์ดไหนคือการ์ดไหน — โทนอำพันแยกออกจากอีก 2 การ์ดชัดเจนกว่า */
#quota-grids .quota-grid-row .card.theme-gold { border-top-color: var(--honey); }
#quota-grids .quota-grid-row .card.theme-gold h3 { color: var(--honey); }
#quota-grids .quota-grid-row .card.theme-gold table.matrix th { background: var(--honey-soft); }

#quota-grids .quota-grid-row .card.theme-sage { border-top-color: var(--sage); }
#quota-grids .quota-grid-row .card.theme-sage h3 { color: var(--sage); }
#quota-grids .quota-grid-row .card.theme-sage table.matrix th { background: var(--sage-soft); }

#quota-grids .quota-grid-row .card.theme-sky { border-top-color: var(--sky); }
#quota-grids .quota-grid-row .card.theme-sky h3 { color: var(--sky); }
#quota-grids .quota-grid-row .card.theme-sky table.matrix th { background: var(--sky-soft); }

#quota-grids .quota-grid-row table.matrix {
  min-width: 0;
  width: 100%;
  font-size: 0.64rem;
}
/* กันตัวเลขตัดขึ้นบรรทัดใหม่ในเซลล์แคบๆ ตอนสลับหน่วยเป็น Pair (เลขใหญ่กว่า Dozen มาก) — ไม่มี
   table-layout: fixed ในสโคปนี้ (เห็นเฉพาะ .quota-order-block) แต่ยังขาด white-space: nowrap อยู่
   ตัวเลขจึงยังตัดบรรทัดได้เหมือนบั๊กเดิม แม้คอลัมน์จะ auto-size อยู่แล้ว (ดู .quota-order-block
   table.matrix ด้านล่างสำหรับบั๊กเดียวกันที่หน้า Order List/Arrangement) — ถ้าล้นความกว้าง 100%
   ของการ์ด .matrix-wrap มี overflow-x: auto รองรับให้เลื่อนดูแทนการตัดบรรทัดอยู่แล้ว */
#quota-grids .quota-grid-row table.matrix th,
#quota-grids .quota-grid-row table.matrix td {
  height: 26px;
  padding: 3px 4px;
  box-sizing: border-box;
  white-space: nowrap;
}
#quota-grids .quota-grid-row table.matrix th {
  font-size: 0.58rem;
}
/* ขยายกว้าง/สูง/ฟอนต์ขึ้นจากเดิม (30x16px, 0.6rem) ตามที่ผู้ใช้ระบุ — ตัวเลขคู่ 5 หลักขึ้นไป (เช่น
   "17,698") มองไม่ชัดในกล่องเดิม — ใช้ .quota-cell-input (class เดิมของ input) แทน [type="number"]
   เพราะเปลี่ยนเป็น type="text" แล้วเพื่อให้แสดงคอมม่าคั่นหลักพันได้ (input type=number ใส่คอมม่าไม่ได้
   ตามที่ผู้ใช้ขอ — ดู formatQuotaCellDisplay/parseQuotaCellValue ใน quota.js) */
#quota-grids .quota-grid-row table.matrix input.quota-cell-input {
  width: 52px;
  height: 22px;
  padding: 0 4px;
  font-size: 0.72rem;
  text-align: center;
  box-sizing: border-box;
}

/* แถวตารางออเดอร์แยกต่อรายการของลูกค้าคนเดียวกัน (หน้า Order Management) —
   แต่ละออเดอร์เป็นตารางของตัวเอง เรียงต่อกันไปทางขวา ไม่รวมยอดกัน */
.quota-customer-row {
  display: flex;
  gap: 14px;
  overflow-x: auto;
  padding-bottom: 4px;
}

.quota-order-block {
  flex-shrink: 0;
}

.quota-order-block .matrix-wrap { min-width: 0; }

/* ตารางออเดอร์แต่ละใบ (หน้า Input Order) ให้เล็กกว่าตาราง Quota/Available หลัก
   เพราะแสดงพร้อมกันหลายตารางต่อแถว (ไว้ 4 ตารางต่อแถวโดยไม่มี scrollbar) — เดิมล็อกความกว้างต่อเซลล์
   ไว้ตายตัวที่ 28px (table-layout: fixed) เพื่อให้ช่องไฟเท่ากันทุกตารางไม่ว่าโมเดลนั้นจะมีกี่สี/ไซส์
   ก็ตาม แต่ 28px ไม่พอสำหรับตัวเลขหลักหมื่น+คอมมา (เช่น "20,880" ตอนสลับหน่วยเป็น Pair ซึ่งเลขจะ
   ใหญ่กว่า Dozen เสมอ ~12 เท่า) ทำให้ตัวเลขตัดขึ้นบรรทัดใหม่ในเซลล์แคบๆ จนตารางดูขาด/ไม่สมบูรณ์ —
   เปลี่ยนเป็น table-layout: auto + min-width (ไม่ fixed width ตายตัว) ให้แต่ละคอลัมน์ขยายตามความกว้าง
   ตัวเลขจริงเสมอ พร้อม white-space: nowrap กันตัวเลขตัดบรรทัดโดยเด็ดขาด */
/* width: 100% (ไม่ใช่ auto) — หัวการ์ด (.quota-order-header: เลขที่ออเดอร์ + ช่องวันที่ + ปุ่ม
   Live Edit/Edit/Delete) มักกว้างกว่าตารางที่มีแค่ไม่กี่สี/ไซส์ ถ้าตารางกว้างแค่ตามเนื้อหาตัวเอง
   (width: auto) การ์ดทั้งใบ (.quota-order-block ซึ่งกว้างเท่าลูกที่กว้างสุด) จะกว้างกว่าตารางจริง
   เหลือช่องว่างขาวๆ ที่ขอบขวาของตาราง ดูเหมือนตารางขาด/ค้าง — ให้ตารางยืดเต็มความกว้างการ์ดเสมอ
   (table-layout: auto ด้านล่างจะกระจายพื้นที่ส่วนเกินไปตามคอลัมน์ให้เอง ไม่บีบเนื้อหาที่ยังมี
   min-width กำหนดไว้อยู่) */
.quota-order-block table.matrix {
  font-size: 0.62rem;
  table-layout: auto;
  width: 100%;
  min-width: 0;
}
.quota-order-block table.matrix th,
.quota-order-block table.matrix td {
  padding: 3px 4px;
  min-width: 28px;
  white-space: nowrap;
}
.quota-order-block table.matrix td:first-child,
.quota-order-block table.matrix th:first-child {
  width: 66px;
  min-width: 66px;
}
.quota-order-block table.matrix th {
  font-size: 0.58rem;
}

/* ออเดอร์ 1 ใบมีได้หลาย line (ต่าง Type/Model กัน) — แต่ละ line มีตารางของตัวเอง
   เรียงต่อกันในแนวตั้งภายในบล็อกเดียวกัน */
.quota-line-block + .quota-line-block {
  margin-top: 10px;
}

/* หัวของแต่ละ line-block (ชื่อโมเดล + ปุ่ม Copy/Clear All ของตารางนั้นโดยเฉพาะ) — ปุ่ม Live Edit/
   Save/Confirm ยังอยู่ที่ order-actions ระดับหัวการ์ดเหมือนเดิม (ใช้ร่วมกันทั้งออเดอร์) ส่วน Copy/Clear
   All ต้องอยู่ติดกับตารางของตัวเอง เพราะแต่ละ line-block อาจคนละโมเดล/สี/ไซส์กัน (ดู
   addLiveEditCopyButton/addLiveEditClearButton ใน quota-shared.js) — ปุ่มอยู่ชิดติดกับชื่อโมเดลเสมอ
   (ไม่ใช่ justify-content: space-between) เพราะ .quota-order-block ไม่บีบความกว้าง (flex-shrink: 0)
   ตารางที่มีหลายไซส์มากๆ (เช่น Sneaker 205-S) จะกว้างเกินจอ ถ้าดันปุ่มไปสุดขวาจะเลื่อนจอไม่เจอ */
.quota-line-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}
.quota-line-actions {
  display: flex;
  gap: 6px;
}
.quota-line-actions .btn {
  padding: 4px 8px;
  font-size: 0.68rem;
  white-space: nowrap;
}

/* หน้า Arrangement — แถบ Export (ต้นฉบับจากออเดอร์จริง) กับ Operation (นับของจริง เทียบ/แก้ไขได้) เป็น
   คนละแนวคิดกัน ให้สีคนละโทนตั้งแต่แถบหัวการ์ดลงไปถึงหัวตารางข้างใน แยกออกจากกันได้ไวแค่มองผ่านๆ ไม่ต้อง
   อ่านหัวข้อ — Export ฟ้า (var(--navy), โทนเดียวกับ "ข้อมูลต้นทาง/อ้างอิง"), Operation เขียว
   (var(--sage), โทนเดียวกับ "งานที่กำลังทำ/นับจริง") */
.card-arr-export { border-top: 3px solid var(--navy); }
.card-arr-export .card-title-row h3 { color: var(--navy); }
.card-arr-export table.matrix th { background: var(--info-soft); }

.card-arr-operation { border-top: 3px solid var(--sage); }
.card-arr-operation .card-title-row h3 { color: var(--sage); }
.card-arr-operation table.matrix th { background: var(--success-soft); }

/* กล่องผลต่างใต้ตาราง Operation แต่ละใบ (หน้า Arrangement) — เทียบตัวเลขที่นับกับตาราง Export ทีละ
   ช่องสี/ไซส์ (ดู diffBoxHtml()/diffRowHtml() ใน arrangement.js) — แต่ละแถวโชว์ Color+Size คู่กันตรงๆ
   พร้อมจำนวน แสดงสูงสุด 5 แถวในกรอบ ถ้าเกินให้เลื่อนดูที่เหลือด้วย scrollbar (.arr-diff-box-scroll) */
.arr-diff-box {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 6px;
  padding: 8px;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  background: var(--surface-alt);
  font-size: 0.68rem;
}

.arr-diff-box-scroll {
  max-height: 132px;
  overflow-y: auto;
  box-sizing: border-box;
}

.arr-diff-row-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  font-weight: 600;
  padding: 1px 0;
}

.arr-diff-row-label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.arr-diff-row-size {
  color: var(--text-soft);
}

/* เดิมใช้เฉดเขียว/แดงแยกจาก var(--success)/var(--danger) เพราะธีมพื้นสว่างเดิมสอบ contrast ตัวหนังสือ
   ขนาดเล็กไม่ผ่าน — ธีมมืดตอนนี้ var(--success)/var(--danger) (โทนสว่าง) ตัดกับพื้น --surface-alt เข้ม
   ได้ดีอยู่แล้ว ใช้ค่าเดียวกับที่อื่นในเว็บตรงๆ ได้เลยไม่ต้องแยกเฉดเอง */
.arr-diff-up { color: var(--success); }
.arr-diff-down { color: var(--danger); }

/* ช่องกรอกตอนกด Live Edit ในตาราง Order List/Arrangement — เดิมใช้ width: 100% ซึ่งไม่มีผลกับ
   table-layout: auto (คำนวณความกว้างคอลัมน์จาก preferred width ของเนื้อหา ซึ่ง <input> เปล่าไม่มี width
   ตายตัวจะรายงานความกว้างมาตรฐานของเบราว์เซอร์ ~170px) ทำให้ตารางบวมทันทีที่กด Live Edit — ทีแรกลองล็อก
   ไว้แคบสุดๆ (19px พอดีกับ min-width 28px ของ td) แต่กลายเป็นแคบเกินจนตัวเลขหลักเยอะๆ (เช่น 20880) มองไม่
   เห็นครบ จึงเปลี่ยนมาใช้ field-sizing: content (รองรับใน Chromium/Edge ตั้งแต่ปี 2024 เป็นต้นมา) ให้ช่อง
   กว้างพอดีกับความยาวของตัวเลขที่กรอกจริงในแต่ละช่องเอง (ช่องว่าง/ตัวเลขน้อยหลักแคบ ช่องเลขเยอะหลักกว้าง
   ขึ้นเอง แต่ไม่เกิน max-width) — width: 32px ด้านล่างเป็น fallback สำหรับเบราว์เซอร์ที่ยังไม่รองรับ
   field-sizing เท่านั้น เบราว์เซอร์ที่รองรับต้องตั้ง width: auto ทับใน @supports ด้านล่างด้วย ไม่งั้น
   field-sizing: content จะไม่มีผลอะไรเลยถ้ายังมี width เป็นค่าตายตัวอยู่ (ทดสอบแล้วว่าถ้าใส่ width: 32px
   ค้างไว้พร้อมกับ field-sizing:content ช่องจะไม่ขยายตามเนื้อหาเลย ต้องเป็น width:auto เท่านั้นถึงจะทำงาน)
   — selector ต้องระบุ table.matrix td นำหน้าด้วย เพราะ specificity ของ table.matrix input[type="number"]
   ด้านบน (0,2,2) สูงกว่า .live-edit-cell-input เฉยๆ (0,1,0) ถ้าเขียนแค่ class เดียวจะโดนค่า width: 56px
   ของ selector นั้นทับ ไม่มีผลอะไรเลย */
table.matrix td input.live-edit-cell-input {
  width: 32px;
  min-width: 22px;
  max-width: 64px;
  padding: 1px 2px;
  font-size: 0.62rem;
  text-align: center;
  border: 1px solid var(--border-soft);
  border-radius: 4px;
  box-sizing: border-box;
}
@supports (field-sizing: content) {
  table.matrix td input.live-edit-cell-input {
    width: auto;
    field-sizing: content;
  }
}

/* ตารางกรอกจำนวนใน popup + Add Order ให้เล็กลงกว่าเดิม (ประหยัดพื้นที่ modal) */
#quota-order-matrix table.matrix {
  font-size: 0.72rem;
}
#quota-order-matrix table.matrix th,
#quota-order-matrix table.matrix td {
  padding: 5px 6px;
}
#quota-order-matrix table.matrix th {
  font-size: 0.64rem;
}
#quota-order-matrix table.matrix input[type="number"] {
  width: 40px;
  padding: 3px;
  font-size: 0.7rem;
}

/* รายการที่กด "+ Add" เข้าตะกร้าไว้ใน popup (ยังไม่บันทึกจนกว่าจะกด Confirm) */
.quota-cart-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 12px 0;
}
.quota-cart-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--bg-soft);
  border-radius: var(--radius-md);
  padding: 8px 12px;
}
.quota-cart-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 0.82rem;
}

/* กดชื่อโมเดลในแถบตะกร้าเพื่อดึงจำนวน/ราคากลับมาแก้ไขในตารางด้านบน */
.quota-cart-line-title {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
  text-align: left;
}
.quota-cart-line-title:hover {
  color: var(--gold-deep);
  text-decoration: underline;
}

.quota-order-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}

.order-no-label { padding: 2px 8px; border-radius: var(--radius-sm); }
/* หน้า Arrangement: ตาราง Operation กด Confirm แล้วตัวเลขที่กรอกตรงกับออเดอร์จริงเป๊ะทุกช่อง —
   ไฮไลต์เลขที่ออเดอร์ในหน้า Order Management เป็นเขียวพาสเทล (ดู DB.isArrangementConfirmed) */
.order-no-arranged { background: var(--success-soft); color: var(--success); }

/* ออเดอร์ที่ระบุวันที่ Arrangement ไว้แล้วเลยวันนี้มาแล้ว (ดู isOverdueDate() ใน status.js) — ไฮไลต์
   เลขที่ออเดอร์เป็นสีแดงเตือน เฉพาะออเดอร์ที่ยังไม่ถูก Confirm (.order-no-arranged ชนะเสมอถ้าชนกัน —
   งานเสร็จแล้วไม่ต้องเตือนซ้ำว่าเลยกำหนด) */
.order-no-overdue { background: var(--danger-soft); color: var(--danger); }

/* ช่องวันที่ใต้เลขที่ออเดอร์ — แก้ไขได้ที่หน้า Order Management > Order List เท่านั้น (input[type=date]
   เปิดปฏิทินของเบราว์เซอร์เอง) ส่วนหน้า Arrangement > Export/Operation แสดงผลอย่างเดียว (text) อ่านค่า
   จากออเดอร์จริงสดๆ ทุกครั้งที่ render จึงตามการแก้ไขที่ต้นทางเสมอ (ดู DB.saveOrderDate) */
.order-no-col { display: flex; flex-direction: column; gap: 4px; }
/* ให้สูงเท่ากับปุ่มข้างๆ ใน .order-actions (.order-actions .btn สูง 24px จาก padding 6px 10px +
   font 0.76rem) — ตัด padding/font ของ input[type=date] ลงจนสูงเท่ากัน (browser เติม padding ภายใน
   ให้เองอีกชั้นเสมอ ตัดจาก CSS อย่างเดียวไม่พอ ต้องกำหนด height ตรงๆ ด้วย) — width: fit-content เดิม
   ใช้ไม่ได้จริง (ทดสอบแล้วบน Chromium/มือถือ) เพราะ input[type=date] มีความกว้างขั้นต่ำของตัวเองตาม
   segment วัน/เดือน/ปี + ไอคอนปฏิทิน ที่เบราว์เซอร์แต่ละแพลตฟอร์มบังคับเอง ไม่ยอมหดตาม fit-content/
   font-size เล็กๆ ทำให้ล้นกรอบ .order-no-col บนบางแพลตฟอร์ม (มือถือ/เบราว์เซอร์อื่น) — ตั้ง width ตรงๆ
   (ไม่ใช่ fit-content) + ลดขนาดไอคอนปฏิทินของ WebKit ลงแทน ถึงจะพอดีกรอบจริง (ทดสอบแล้วยังอ่านวันที่
   ครบทุกหลักไม่โดนตัด) — selector ต้องเป็น "input.order-date-input" ไม่ใช่ ".order-date-input" เฉยๆ
   เพราะ input[type="date"] (บรรทัดด้านบน) มี specificity สูงกว่า .class เดี่ยวๆ (attribute selector
   นับเท่า class) ถ้าใช้แค่ .order-date-input จะแพ้ทับ ทำให้ width/font-size ไม่ effect เลย */
input.order-date-input {
  height: 24px;
  box-sizing: border-box;
  font-size: 0.62rem;
  padding: 0 2px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-soft);
  background: var(--surface);
  width: 92px;
  min-width: 0;
}
.order-date-input::-webkit-calendar-picker-indicator {
  padding: 0;
  margin-left: 2px;
  width: 12px;
  height: 12px;
}
.order-date-display { font-size: 0.66rem; padding: 0 8px; }

/* กล่องสรุปยอดรวมท้าย popup Create SO — ตัวเลขเด่นชัดเจน อ่านง่าย เป็นระเบียบ (แทนแถบข้อความ
   ยาวเดิมแบบ notice-banner) */
.so-summary-row {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 24px;
}
.so-summary-box {
  flex: 1;
  min-width: 180px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  box-shadow: var(--shadow-sm);
}
.so-summary-label {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--text-soft);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.so-summary-value {
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--gold-deep);
}

.matrix-total-row td {
  background: var(--gold-soft);
  font-weight: 600;
  color: var(--gold-deep);
}

/* ---------------- Monthly sales bar chart (Home) ---------------- */

.month-bar-chart {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.month-bar-row {
  display: flex;
  align-items: center;
  gap: 14px;
}

.month-bar-label {
  width: 38px;
  flex-shrink: 0;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--text-soft);
}

.month-bar-track {
  flex: 1;
  height: 20px;
  background: var(--bg-soft);
  border-radius: 2px;
  overflow: hidden;
}

.month-bar-fill {
  height: 100%;
  min-width: 2px;
  background: var(--gold);
  border-radius: 0 4px 4px 0;
}

.month-bar-value {
  width: 130px;
  flex-shrink: 0;
  text-align: right;
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--text-soft);
  font-variant-numeric: tabular-nums;
}

/* ---------------- Badges / status ---------------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}

.badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.badge-order-issued { background: var(--warning-soft); color: var(--warning); }
.badge-so-issued { background: var(--danger-soft); color: var(--danger); }
.badge-payment-done { background: var(--warning-soft); color: var(--warning); }
.badge-shipment-done { background: var(--success-soft); color: var(--success); }

/* ---------------- Modal ---------------- */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(3px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 200;
  padding: 24px;
}

.modal-overlay.hidden { display: none; }
.hidden { display: none; }

.modal {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  width: 100%;
  max-width: 640px;
  max-height: 88vh;
  overflow-y: auto;
  padding: 30px;
}

.modal-wide { max-width: 920px; }
.modal-xwide { max-width: 1440px; }

.modal-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 22px;
}

.modal-close {
  background: none;
  border: none;
  font-size: 1.3rem;
  color: var(--text-faint);
  cursor: pointer;
  line-height: 1;
  padding: 4px;
}
.modal-close:hover { color: var(--text); }

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--border-soft);
}

/* ปุ่ม "SO เดิม" ในโมดัล Create SO (เปิดดู SO ที่เคยสร้างไว้แล้วของออเดอร์นี้ ถ้ามี) — margin-right:auto
   ดันตัวเองไปชิดขอบซ้ายสุดของแถว ในขณะที่ปุ่มยกเลิก/Preview SO ยังชิดขวาเหมือนเดิม (justify-content:
   flex-end ด้านบน) แทนที่จะต้องเปลี่ยน layout ทั้งแถวเป็น space-between ซึ่งจะดันปุ่มอื่นห่างกันเกินไป
   ตอนไม่มีปุ่มนี้โผล่ (ออเดอร์ที่ยังไม่เคยสร้าง SO) */
#quota-btn-view-existing-so { margin-right: auto; }

/* ---------------- Payment modal (ป็อปอัป Payment หน้า Summary) ---------------- */

.payment-doc-row {
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 14px 16px;
}
.payment-doc-row + .payment-doc-row { margin-top: 12px; }

.payment-doc-row-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.payment-doc-label {
  font-weight: 600;
  font-size: 0.92rem;
}

.payment-doc-empty {
  font-size: 0.82rem;
  margin-top: 8px;
}

/* Drop Zone ต่อแถว (SO/PI/Statement เท่านั้น — Payment Slip ไม่มีกล่องนี้) รูปแบบเดียวกับ .fin-dropzone
   หน้า Financial แต่ย่อขนาดลงเพราะอยู่ในการ์ดเล็กต่อแถว ไม่ใช่เต็มความกว้างหน้า */
.payment-dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border: 2px dashed var(--border);
  border-radius: var(--radius-md);
  padding: 14px;
  margin-top: 10px;
  text-align: center;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.payment-dropzone.dragover { border-color: var(--gold); background: var(--gold-soft); }
.payment-dropzone-btn {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  background: var(--gold);
  color: #fff;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}
.payment-dropzone-btn:hover { background: var(--gold-deep); }
.payment-dropzone span { font-size: 0.76rem; }

/* ไฟล์ที่อัปโหลดแล้วต่อประเภท (SO/PI/Payment Slip/Statement) — ชื่อไฟล์ชิดซ้าย ปุ่ม Remove ชิดขวา
   เรียงซ้อนกันแนวตั้งทีละไฟล์ (Payment Slip อาจมีหลายไฟล์) */
.payment-file-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0;
  border-top: 1px solid var(--border-soft);
  margin-top: 8px;
}
.payment-file-row a { min-width: 0; }

/* ---------------- Toast ---------------- */

.toast-stack {
  position: fixed;
  bottom: 28px;
  right: 28px;
  z-index: 300;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.toast {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 13px 20px;
  border-radius: var(--radius-md);
  font-size: 0.86rem;
  box-shadow: var(--shadow-lg);
  animation: toast-in 0.2s ease;
  display: flex;
  align-items: center;
  gap: 10px;
}

.toast.success { background: var(--success-soft); border-color: var(--success); color: var(--success); }
.toast.error { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ---------------- Empty state ---------------- */

.empty-state {
  text-align: center;
  padding: 60px 20px;
  color: var(--text-faint);
}

.empty-state .icon {
  font-size: 2.4rem;
  margin-bottom: 12px;
  opacity: 0.6;
}

/* ---------------- Utility ---------------- */

.flex { display: flex; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.flex-wrap { flex-wrap: wrap; }
.gap-8 { gap: 8px; }
.gap-12 { gap: 12px; }
.gap-16 { gap: 16px; }
.mt-8 { margin-top: 8px; }
.mt-16 { margin-top: 16px; }
.mt-24 { margin-top: 24px; }
.mb-8 { margin-bottom: 8px; }
.mb-16 { margin-bottom: 16px; }
.text-soft { color: var(--text-soft); }
.text-faint { color: var(--text-faint); }
.text-gold { color: var(--gold-deep); }
.fw-600 { font-weight: 600; }

.divider {
  height: 1px;
  background: var(--border-soft);
  margin: 24px 0;
}

.notice-banner.notice-danger {
  background: var(--danger-soft);
  border-color: rgba(217, 106, 106, 0.35);
  color: var(--danger);
}

.notice-banner {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  background: var(--honey-soft);
  border: 1px solid rgba(217, 166, 108, 0.35);
  border-radius: var(--radius-md);
  padding: 14px 18px;
  font-size: 0.85rem;
  color: var(--honey);
  margin-bottom: 24px;
}

/* คำใบ้ Available ในป๊อปอัป + Add Order — แยกชื่อสี/ไซส์ (บรรทัดเล็กด้านบน) ออกจากตัวเลข
   Available จริง (จากตาราง Available — 200 เป๊ะๆ) และ Preview (คงเหลือถ้าบันทึกรายการนี้จริง)
   คนละบรรทัด ให้อ่านง่าย ไม่ต้องไล่อ่านประโยคยาวบรรทัดเดียว */
#quota-available-hint { flex-direction: column; gap: 3px; }
.quota-hint-label {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  opacity: 0.85;
}
.quota-hint-avail, .quota-hint-preview { font-size: 0.85rem; }
.quota-hint-qty {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--gold-deep);
}
/* สีของแต่ละตัวเลขขึ้นกับเครื่องหมายของค่านั้นๆ เอง ไม่ใช่ผูกกับ container เดียวกันทั้งกล่อง
   (ดู qtyClass() ใน quota.js) — Available กับ Preview จึงอาจคนละสีกันได้ในเวลาเดียวกัน */
.quota-hint-qty-danger { color: var(--danger); }

/* ---------------- Footer ---------------- */

.footer {
  text-align: center;
  padding: 32px;
  color: var(--text-faint);
  font-size: 0.8rem;
}

/* หมายเหตุ: breakpoint หลักของทั้งเว็บ (Tier A/B/C) ย้ายไปอยู่ท้ายไฟล์แล้ว (ดู "Responsive: A (Desktop)
 * / B (Tablet) / C (Smartphone)" ท้ายสุดของไฟล์) — ต้องอยู่หลัง selector ทุกตัวที่ต้อง override เสมอ
 * (เช่น .cal-columns/.grid-4col ที่ base rule อยู่ไกลลงไปข้างล่างของไฟล์) ไม่งั้น cascade จะให้ base
 * rule ที่มาทีหลังในไฟล์ชนะ media query ที่มาก่อนหน้าไปเงียบๆ (specificity เท่ากัน ตัดสินด้วยลำดับไฟล์
 * ล้วนๆ) — เจอบั๊กนี้จริงตอนทดสอบ .cal-columns ไม่ยอมยุบเป็น 1 คอลัมน์ที่ Tier B/C ทั้งที่ media query
 * ก็ถูกต้องแล้ว */

/* เอกสาร SO/PI/INV&PL (so-view.html/pi-view.html/invpl-view.html) ล็อกขนาดเท่า A4 ตายตัวไว้
   (width: 210mm, height: 297mm, overflow: hidden) เพื่อความแม่นยำตอน Print/Save PDF — ถ้าปล่อยให้
   max-width:100% หดความกว้างตามจอมือถือ/iPad แคบๆ โดยความสูงยังตายตัวเท่าเดิม จะทำให้สัดส่วนเพี้ยน
   และเนื้อหาโดน overflow:hidden ตัดออกไป จึงคงขนาดจริงไว้ (max-width: none) แล้วให้ตัวครอบเลื่อนแนวนอน/
   แนวตั้งแทน (ดูเหมือนเปิดเอกสาร PDF จริงบนมือถือ — pinch-zoom/เลื่อนดู ไม่ใช่ reflow เนื้อหา) */
@media screen and (max-width: 900px) {
  .so-doc-page { overflow-x: auto; }
  .so-doc-page .so-doc,
  .so-doc-page .invpl-doc-v2 { max-width: none; }
}

/* ---------------- Print (SO document) ---------------- */

@media print {
  :root {
    --bg: #ffffff;
    --surface: #ffffff;
    --surface-alt: #ffffff;
    --bg-soft: #F3F4F6;
    --text: #2D2420;
    --text-soft: #5B6472;
    --text-faint: #98A1AD;
    --border: #E2E5EA;
    --border-soft: #EBEDF1;
    --gold-deep: #A8546D;
  }
  .topbar, .no-print { display: none !important; }
  /* #app-nav มี inline height ที่ตั้งไว้ผ่าน JS ให้เท่ากับ .topbar จริง (กันเนื้อหาเลื่อนไปซ้อนใต้
     แถบเมนูที่ตอนนี้เป็น position:fixed) — ต้องล้างกลับเป็น 0 ตอนพิมพ์ ไม่งั้นจะเหลือที่ว่างเปล่า
     ด้านบนของทุกหน้าที่พิมพ์ออกมา ทั้งที่ .topbar เองถูกซ่อนไปแล้วด้านบน */
  #app-nav { height: 0 !important; }
  body { background: #fff; }
  .page { padding: 0; }
}

/* ---------------- SO document (shared: so-view.html + Export Order preview) ---------------- */

/* เอกสาร SO/PI/INV&PL (.so-doc ใช้ทั้ง so-view.html และ invpl-view.html, .pi-doc ใช้ทั้ง pi-view.html
   และ preview ใน shipping.html) ต้องเป็นกระดาษขาว-ตัวหนังสือเข้มเสมอ ไม่ว่าธีมแอปตอนนี้จะมืดแค่ไหน
   (จำลองกระดาษจริงที่จะพิมพ์ออกมา ไม่ใช่ UI ของแอป) — pin ตัวแปรสีใหม่ทับเฉพาะในกรอบนี้แทนการไล่แก้
   ทุก selector ย่อยที่อ้าง var(--text-soft)/var(--gold-deep)/ฯลฯ อยู่แล้วด้านล่าง (ใช้ค่าเดียวกับที่
   @media print ด้านบนรีเซ็ตไว้ตอนพิมพ์จริงอยู่แล้ว — เอกสารบนจอกับที่พิมพ์ออกมาจึงหน้าตาตรงกัน) */
/* PI ย้ายไปใช้ธีมเดียวกับ .invpl-doc แล้ว (ดูด้านล่าง) จึงเหลือแค่ .so-doc ที่ยังใช้สีน้ำเงินตามโลโก้
   CHANG.png เป็น --gold-deep ของตัวเอง */
.so-doc {
  --bg: #ffffff;
  --surface: #ffffff;
  --surface-alt: #ffffff;
  --bg-soft: #F4F6FA;
  --text: #2A2A2A;
  --text-soft: #5B6472;
  --text-faint: #98A1AD;
  --border: #DCE1E8;
  --border-soft: #EAEDF2;
  --gold-deep: #20409A;
  --doc-border: #C9D0DA;
}

/* ขนาดตัวอักษรกลาง ใช้ตัวเดียวกันทุกส่วน/ทุกเอกสาร (SO/PI/INV&PL) ไม่ให้แต่ละส่วนขนาดต่างกันเอง
   — .so-meta/.so-totals (PI/INV&PL ยังใช้อยู่) กับ .so-meta-table/.so-totals-table (SO ใหม่)
   ต้องใช้ค่าเดียวกันเป๊ะๆ */
/* px ตายตัว (ไม่ใช่ rem) โดยตั้งใจ — เอกสารนี้ต้องมีขนาดตัวอักษรเป๊ะๆ เท่าเดิมเสมอไม่ว่าจะเปิดจาก
   iPad/มือถือ (ที่ตอนนี้ปรับ html { font-size } ให้เล็กลงเพื่อความพอดีของ UI ส่วนอื่นแล้ว) เพราะหน้านี้
   ต้องพิมพ์/Save PDF ให้ตรงกับที่ preview เป๊ะ — ถ้าใช้ rem ค่านี้จะโดนขนาดตัวอักษรของ UI ที่ปรับ
   ต่อ breakpoint ด้านล่างกระทบไปด้วย ทั้งที่ไม่เกี่ยวข้องกันเลย */
:root {
  --so-doc-font: 11.5px;
  --so-grid-font: 10.88px;
}

/* เอกสาร SO ต้องพอดี A4 แผ่นเดียวเป๊ะๆ ทั้งตอน Print/Save PDF จากเบราว์เซอร์ และตอน Save PDF ผ่าน
   Google Apps Script (ฝั่ง Apps Script ไม่รัน <script> ใดๆ เลย แก้ด้วย JS วัดขนาดจึงใช้ไม่ได้ — ต้อง
   ล็อกด้วย CSS ล้วนๆ ให้พฤติกรรมตรงกันทั้งสองทาง) — height ตายตัวเท่า A4 + overflow: hidden แทน
   min-height เดิมที่ปล่อยให้เนื้อหายาวเกินไหลออกไปหน้า 2 ได้ */
.so-doc {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  width: 210mm;
  height: 297mm;
  max-width: 100%;
  margin: 0 auto;
  padding: 12mm;
  box-sizing: border-box;
  font-family: 'Sarabun', sans-serif;
  font-size: var(--so-doc-font);
  line-height: 1.5;
  color: #2A2A2A;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* เบราว์เซอร์ปิดการพิมพ์ background-color/background-image เป็นค่าเริ่มต้นเสมอ (ต้องกดติ๊ก
     "Background graphics" เองใน print dialog ถึงจะเห็น) ไม่ว่า @media print ด้านล่างจะตั้งสีอะไรไว้
     ก็ตาม — ทั้งพื้นหัวตาราง (.so-grid thead, var(--bg-soft)) และแถบสีทึบของ PI (.pi-band, ตัวหนังสือ
     ขาวบนพื้นสีทึบ) ถ้าไม่บังคับด้วย print-color-adjust:exact ตัวหนังสือขาวจะกลายเป็นขาวบนกระดาษขาว
     มองไม่เห็นเลยตอนพิมพ์จริง ทั้งที่ Preview บนจอเห็นปกติ (เบราว์เซอร์ไม่ปิดสีตอนแสดงผลบนจอ) */
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}
.so-doc-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; }
.so-doc .company { font-weight: 700; font-size: 18.4px; color: var(--gold-deep); }
/* หัวบริษัท 2 บรรทัด (ไทย/อังกฤษ) เฉพาะเอกสาร SO — ตาม template จริง มีเส้นใต้เฉพาะบรรทัดภาษาไทย */
.so-doc .company .company-th { font-size: 15.2px; font-weight: 700; text-decoration: underline; letter-spacing: 0; }
.so-doc .company .company-en { font-size: 12.48px; font-weight: 500; color: var(--text-soft); text-decoration: none; }
.so-doc .so-no-topright { font-size: var(--so-doc-font); white-space: nowrap; }
.so-doc h2 { text-align: center; font-size: 20.8px; letter-spacing: 0.12em; margin: 6px 0 16px; font-weight: 600; }

.so-meta { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px 28px; margin-bottom: 20px; font-size: var(--so-doc-font); }
.so-meta .full { grid-column: 1 / -1; }
.so-meta div span.k { color: var(--text-faint); display: inline-block; width: 120px; }

/* ตารางข้อมูลหัว SO แบบมีเส้นกรอบบาง (แทน .so-meta ไม่มีเส้น) ตาม template จริง — เฉพาะเอกสาร SO
   เท่านั้น (PI/INV&PL ยังใช้ .so-meta เดิม ไม่ได้แก้) */
table.so-meta-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--so-doc-font);
  margin-bottom: 16px;
}
table.so-meta-table td {
  border: 1px solid var(--doc-border);
  padding: 5px 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
table.so-meta-table span.k {
  color: var(--text-soft);
  margin-right: 8px;
}

.so-grid-wrap { overflow-x: auto; margin-bottom: 16px; }

table.so-grid {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--so-grid-font);
  table-layout: fixed;
}
/* คอลัมน์ ขนาด/Size ไม่กำหนดความกว้างตายตัว — ปล่อยให้ table-layout: fixed หารพื้นที่ที่เหลือ
   เท่าๆ กันทุกคอลัมน์เอง ไม่ว่าโมเดลนั้นจะมีกี่ไซส์ก็ตาม (คอลัมน์อื่นกำหนดความกว้างตายตัวไว้ กว้าง
   พอสำหรับตัวเลขหลักหมื่น-แสนไม่ให้ล้นกรอบ) */
table.so-grid col.col-type { width: 62px; }
table.so-grid col.col-color { width: 78px; } /* พอสำหรับชื่อสีทั่วไปบรรทัดเดียว เช่น "Dark Blue" — ดู
  SO_GRID_FIXED_COLS_PX ใน so-render.js ที่ต้องอัปเดตคู่กันเสมอ ชื่อสียาวผิดปกติยังห่อบรรทัดใหม่ได้
  (ดู td.color-cell ด้านล่าง) กันตัดข้อความเหมือนที่เคยเกิด ไม่ใช่ nowrap ตายตัว */
table.so-grid col.col-totalpairs { width: 62px; }
table.so-grid col.col-grandtotal { width: 62px; }
table.so-grid col.col-price { width: 68px; }
table.so-grid col.col-amount { width: 92px; }
table.so-grid th, table.so-grid td {
  border: 1px solid var(--doc-border);
  /* em-relative (not px) so padding shrinks along with the per-table
     font-size that so-render.js sets inline for models with many sizes —
     see soGridFontSizePx() there. */
  padding: 0.28em 0.46em;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
table.so-grid thead th { background: var(--bg-soft); font-weight: 600; white-space: normal; line-height: 1.25; }
/* Model names can run long, especially price-group lines like "205-S (28-36)"
   — wrap onto multiple lines within the fixed column width instead of
   ellipsis-truncating, so the label always stays fully readable. */
table.so-grid td.label-cell { text-align: center; font-weight: 600; vertical-align: middle; background: var(--bg-soft); white-space: normal; overflow: visible; text-overflow: clip; word-break: break-word; }
/* สีอาจมีชื่อยาวกว่าคอลัมน์แคบๆ นี้ (เช่น "Dark Blue") — ห่อขึ้นบรรทัดใหม่แทนโดน ellipsis
   ตัดจนอ่านไม่ครบเหมือนที่เคยเกิด (เดิมสืบทอด white-space: nowrap/ellipsis จาก th/td ฐานด้านบน) —
   เหมือน td.label-cell ทุกประการ */
table.so-grid td.color-cell { text-align: left; white-space: normal; overflow: visible; text-overflow: clip; word-break: break-word; }
table.so-grid td.summary-cell { vertical-align: middle; font-weight: 600; }
/* Remark ต่อรายการ (line.remark เช่น "Free of Charge"/"Sample") — แถวเพิ่มท้ายตารางของแต่ละ line
   ตัวหนังสือสีแดง คร่อมอยู่ใต้คอลัมน์ Unit Price + Amount เท่านั้น (เซลล์ว่างก่อนหน้าไม่มีเส้นขอบซ้าย
   ให้ดูเหมือนลอยอยู่ใต้ 2 คอลัมน์นั้นจริงๆ ไม่ใช่ทั้งแถว) */
table.so-grid tr.so-remark-row td { border-top: none; }
table.so-grid tr.so-remark-row td:first-child { border-right: none; }
table.so-grid td.so-remark-cell { color: #C0392B; font-weight: 600; }

.so-totals { display: flex; justify-content: flex-end; margin: 14px 0; }
.so-totals table { border-collapse: collapse; font-size: var(--so-doc-font); width: 100%; max-width: 320px; }
.so-totals td { padding: 5px 12px; border: 1px solid var(--border); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.so-totals td.k { color: var(--text-soft); }
.so-totals td.v { text-align: right; font-weight: 600; }
.so-totals tr.grand td { font-weight: 700; color: var(--gold-deep); }

/* ---------------- PI / Sales Contract document (shared: pi-view.html + Shipping "Create PI"
   preview) ---------------- Same visual theme as .invpl-doc (Inter, warm cream/beige, plain-text
   field labels — no more navy bands) per the user's explicit request to make PI look like a
   consistent set with Invoice & Packing List. Field layout/grouping (Seller/Buyer boxes, item
   table columns, bank box) is unchanged — only the color palette, font, and label style moved. */
.pi-doc {
  --bg: #FFFFFF;
  --surface: #FFFFFF;
  --surface-alt: #FAFAF9;
  --bg-soft: #F1F1EF;
  --text: #24231F;
  --text-soft: #6B6A63;
  --text-faint: #9B9A91;
  --border: #D6D4CB;
  --border-soft: #E7E5DC;
  --gold-deep: #8F4B2C;

  background: var(--surface);
  border: 1px solid var(--border-soft);
  width: 210mm;
  min-height: 297mm;
  max-width: 100%;
  margin: 0 auto;
  padding: 14mm;
  box-sizing: border-box;
  font-family: 'Inter', 'Helvetica Neue', Arial, sans-serif;
  font-size: var(--so-doc-font);
  line-height: 1.5;
  color: var(--text);
  display: flex;
  flex-direction: column;
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}

.pi-doc h2 {
  text-align: right;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.04em;
  margin: 0 0 10px;
  color: var(--text);
}
.pi-doc table.pi-header-table { width: 100%; border-collapse: collapse; margin-bottom: 8px; table-layout: fixed; }
.pi-doc table.pi-header-table td { border: 1px solid var(--border); padding: 5px 9px; vertical-align: top; font-size: var(--so-doc-font); word-break: break-word; }
/* Seller box — โลโก้อยู่ในกล่องเดียวกับชื่อ/ที่อยู่ (ชิดขวาของกล่อง) ตามต้นแบบ PI NEW.pdf แทนที่จะแยก
   เป็นแถวหัวเอกสารต่างหากแบบเดิม (ดู .pi-seller-flex) */
.pi-doc .pi-seller-flex { display: flex; align-items: flex-start; justify-content: space-between; gap: 6px; }
.pi-doc .pi-logo { display: block; max-width: 46px; max-height: 36px; flex: none; margin-top: 2px; }
/* label เล็กด้านบน + value ด้านล่าง อยู่ในเซลล์เดียวกันเสมอ (ตามต้นแบบ) แทนแถบสีคาดคั่นแยกแถวแบบเดิม —
   ชื่อคลาสตั้งใจแยกจาก .invpl-label-plain/.invpl-field-value ของ invpl-render.js (คนละ namespace
   ตามสถาปัตยกรรมเดิมที่ตั้งใจกัน .pi-doc/.invpl-doc ไม่ให้กระทบกัน) แม้หน้าตาจะเหมือนกันทุกประการ */
.pi-doc .pi-label-plain { font-size: 0.8em; font-weight: 600; color: var(--text-soft); letter-spacing: 0.01em; margin-bottom: 3px; }
.pi-doc .pi-field-value { font-weight: 500; }
.pi-doc .pi-wrap-text { white-space: pre-line; word-break: break-word; overflow-wrap: break-word; }
.pi-doc .pi-buyer-line1 { font-weight: 700; }
.pi-doc .pi-seller-name { font-weight: 700; margin-bottom: 2px; }

.pi-doc table.pi-items-table { width: 100%; border-collapse: collapse; margin-bottom: 0; font-size: var(--so-doc-font); table-layout: fixed; border: 1px solid var(--border); }
.pi-doc table.pi-items-table th, .pi-doc table.pi-items-table td { padding: 5px 8px; word-break: break-word; }
.pi-doc table.pi-items-table col.pi-col-no { width: 36px; }
.pi-doc table.pi-items-table col.pi-col-remark { width: 84px; }
.pi-doc table.pi-items-table col.pi-col-qty { width: 64px; }
.pi-doc table.pi-items-table col.pi-col-price { width: 88px; }
.pi-doc table.pi-items-table col.pi-col-amount { width: 104px; }
/* คอลัมน์รูปสินค้า — เฉพาะเอกสาร Quotation (quotation-render.js) เท่านั้น ไม่มีใน PI/INV&PL เดิม
   เพิ่มเป็นคอลัมน์ .pi-col-* ชุดเดียวกันเพื่อ Theme ตรงกันเป๊ะตามที่ผู้ใช้ระบุ */
.pi-doc table.pi-items-table col.pi-col-image { width: 64px; }
.pi-doc table.pi-items-table td.pi-col-image-cell { text-align: center; padding: 4px; }
.pi-doc table.pi-items-table td.pi-col-image-cell img { max-width: 56px; max-height: 56px; object-fit: contain; display: block; margin: 0 auto; }
/* QTY/Unit Price/Amount เป็นตัวเลขล้วน ห้ามตัดขึ้นบรรทัดใหม่กลางตัวเลขเด็ดขาด (เคยเกิด "4,865,400.00"
   โดน word-break: break-word ตัดขึ้นบรรทัดใหม่ไปอีก "0" คือสาเหตุที่ผู้ใช้เห็น "ตัวเลขตก") คอลัมน์กว้างพอ
   สำหรับยอดเงินหลักล้านอยู่แล้ว (ดู col.pi-col-amount ด้านบน) แต่ใส่ nowrap กันไว้เหมือน
   .invpl-items-table (เดิม PI ไม่มี nowrap ต่างจาก INV&PL — เป็นจุดที่หลุด) */
.pi-doc table.pi-items-table tbody td:nth-child(4),
.pi-doc table.pi-items-table tbody td:nth-child(5),
.pi-doc table.pi-items-table tbody td:nth-child(6) { white-space: nowrap; }
.pi-doc table.pi-items-table thead td { background: var(--bg-soft); color: var(--text-soft); font-weight: 700; text-align: center; line-height: 1.3; white-space: nowrap; border-bottom: 1px solid var(--border); }
.pi-doc table.pi-items-table thead td.pi-col-sub { font-weight: 400; font-size: 0.9em; }
.pi-doc table.pi-items-table tbody tr:not(:last-child) td { border-bottom: 1px solid var(--border); }
.pi-doc table.pi-items-table tbody td { vertical-align: top; min-height: 2.6em; }
.pi-doc table.pi-items-table tbody td.text-center { text-align: center; }
.pi-doc table.pi-items-table tbody td.text-right { text-align: right; }
/* คอลัมน์ตัวเลขขวาสุด (Unit Price/Amount) — เพิ่ม padding ขวาให้ตัวเลขห่างจากขอบเซลล์/ขอบกระดาษ
   มากกว่าเซลล์อื่น กันเลขตกขอบตอนพิมพ์ (เคยเกิดกับ Amount THB ตัวเลขชิดขอบขวาสุดจนตกขอบกระดาษ) */
.pi-doc table.pi-items-table tbody td.text-right { padding-right: 12px; }
.pi-doc table.pi-items-table tfoot td { background: var(--bg-soft); color: var(--text); font-weight: 700; text-align: center; border-top: 1px solid var(--border); }

.pi-doc table.pi-bank-table { margin-top: 14px; margin-bottom: 0; }
.pi-doc .pi-footer-note { text-align: center; font-size: var(--so-doc-font); color: var(--text-soft); margin-top: 10px; }

/* ป๊อปอัป "Issue INV&PL" (หน้า Shipping) — กรอกข้อมูล + ตาราง Order/Remain/Shortage/Summary ในนี้
   ก่อน กด Preview เพื่อไปดูเอกสารจริงในโมดัลถัดไป (เหมือนขั้นตอน Create SO/PI) */
.invpl-grid-groups-compact { max-height: 40vh; overflow-y: auto; padding-right: 4px; }
.invpl-group-block { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 6px 8px; margin-bottom: 8px; }
.invpl-group-block h4 { margin: 0 0 3px; font-size: 0.78rem; }
.invpl-group-block .invpl-subtable-label { font-size: 0.6rem; font-weight: 700; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.03em; margin: 4px 0 2px; }
.invpl-group-block .invpl-subtable-hint { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--text-faint); font-size: 0.9em; margin-left: 2px; }
/* label "Final" เน้นว่าเป็นตัวที่ใช้ในเอกสารจริง */
.invpl-group-block .invpl-subtable-label-final { color: var(--gold-deep); }
/* input ในตาราง Remain/Shortage — ขยายเต็ม cell + ตัวเลขมองเห็นครบ (ป้องกันถูก overflow:hidden ของ
   .so-grid td ตัด) ใช้ padding น้อยและ min-width:0 ให้ browser ย่อ input ได้ตาม cell — ต้อง override
   font-size เอง เพราะไม่งั้นสืบทอด 0.92rem จาก input ทั่วไปทั้งแอป (ใหญ่เกินไปสำหรับกล่องแคบๆ นี้
   ตัวเลขที่พิมพ์เลยล้นออกนอกกล่องมองไม่เห็น) */
.invpl-group-block input[type="number"] {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
  padding: 2px 4px;
  text-align: center;
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
}
/* ย่อขนาดตาราง Order/Remain/Shortage/Summary ให้เล็กกะทัดรัดกว่าตาราง Quota/Available ปกติ
   (ตัวการ์ดเดียวมี 4 ตารางซ้อนกัน ถ้าใหญ่เท่าเดิมจะกินพื้นที่มากเกินไปในโมดัลเดียว) — สาเหตุที่เดิม
   ย่อ font/padding แล้วยังใหญ่อยู่: table.matrix มี min-width: 640px ตายตัว (บรรทัด ~1064) บังคับให้
   ทุกตารางกว้างอย่างน้อย 640px ไม่ว่า font จะเล็กแค่ไหน — ต้อง override min-width เป็น 0 + table-layout:
   auto (แบบเดียวกับ .quota-order-block ที่หน้า Input Order ทำไว้แล้ว) ตารางถึงจะยุบเล็กตามเนื้อหาจริง */
.invpl-group-block .matrix-wrap { min-width: 0; overflow-x: auto; }
.invpl-group-block table.matrix { font-size: 0.56rem; table-layout: auto; width: 100%; min-width: 0; }
.invpl-group-block table.matrix th,
.invpl-group-block table.matrix td { padding: 1px 3px; min-width: 46px; white-space: nowrap; }
.invpl-group-block table.matrix td:first-child,
.invpl-group-block table.matrix th:first-child { width: 52px; min-width: 52px; }
.invpl-group-block table.matrix input[type="number"] { width: 40px; padding: 1px 2px; }

/* Dashboard สรุปเล็กๆ ท้ายชุดกรอกข้อมูล Issue INV&PL (A. จำนวนโมเดล / B. Mapping with Database —
   Sack/Carton ต่อโมเดล) — กล่อง A แคบ กล่อง B กว้างกว่าแสดงรายการต่อโมเดล ดู renderInvplDashboardSummary
   ใน shipping.js — ย่อกะทัดรัด: value เล็กลง (1.05rem→0.9rem), A/value อยู่บรรทัดเดียวกันแนวนอน,
   B จำกัดความสูงมี scroll ถ้าโมเดลเยอะ, ชื่อโมเดลยาวตัดด้วย ellipsis กัน badge หน่วยตกไปบรรทัดล่าง
   (ที่ผู้ใช้เรียกว่า "ล้นบรรทัด") */
.invpl-dashboard { display: flex; gap: 8px; margin-top: 8px; flex-wrap: wrap; align-items: flex-start; }
.invpl-dash-box { background: var(--surface-alt); border: 1px solid var(--border-soft); border-radius: var(--radius-md); padding: 6px 10px; flex: 1; min-width: 130px; }
.invpl-dash-box-wide { flex: 2; }
.invpl-dash-label { font-size: 0.58rem; font-weight: 700; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: 2px; }
.invpl-dash-value { font-family: var(--font-display); font-size: 0.9rem; font-weight: 700; color: var(--gold-deep); }
.invpl-dash-rows { max-height: 84px; overflow-y: auto; }
.invpl-dash-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 1.5px 0; font-size: 0.68rem; border-top: 1px solid var(--border-soft); }
.invpl-dash-row:first-of-type { border-top: none; }
.invpl-dash-row span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.invpl-dash-unit { flex: none; font-size: 0.6rem; font-weight: 700; padding: 1px 6px; border-radius: var(--radius-pill); white-space: nowrap; }
.invpl-dash-unit-sack { background: var(--sage-soft); color: var(--sage); }
.invpl-dash-unit-carton { background: var(--sky-soft); color: var(--sky); }
.invpl-dash-unit-none { background: var(--danger-soft); color: var(--danger); }

/* โมดัล Preview ของ Issue INV&PL — พิมพ์ได้ตรงจากโมดัลนี้เลย (ปุ่ม Print) ซ่อนทุกอย่างอื่นบนหน้า
   จอตอนพิมพ์ ให้เหลือแค่เนื้อหาเอกสารในโมดัลนี้ล้วนๆ ไม่มี header/ปุ่ม/backdrop ติดไปด้วย
   จำกัดด้วย body:has(#modal-invpl-preview) ไม่งั้น rule นี้ทำงานบนทุกหน้า รวมถึง pi-view.html /
   so-view.html / invpl-view.html ที่ .so-doc-page (ลูกตรงของ body) ไม่ใช่ #modal-invpl-preview จึงถูก
   display:none ทั้งกรอบ → สั่งพิมพ์แล้วกระดาษว่างเปล่าทั้งที่หน้าเว็บมีข้อมูลครบ */
@media print {
  body:has(#modal-invpl-preview) > *:not(#modal-invpl-preview) { display: none !important; }
  body:has(#modal-invpl-preview) #modal-invpl-preview { position: static; background: none; padding: 0; display: block !important; }
  body:has(#modal-invpl-preview) #modal-invpl-preview .modal { box-shadow: none; border: none; max-width: 100%; max-height: none; overflow: visible; padding: 0; margin: 0; }
  body:has(#modal-invpl-preview) #modal-invpl-preview .modal-header,
  body:has(#modal-invpl-preview) #modal-invpl-preview .modal-actions { display: none; }
  body:has(#modal-invpl-preview) #modal-invpl-preview .so-preview-scroll { overflow: visible; }
}

/* โมดัล "ตรวจสอบ Sales Order ก่อนบันทึก" (dashboard.html) — ปุ่ม Print เดียวกับที่ Issue INV&PL มีอยู่
   แล้ว (ดูด้านบน) จำกัดด้วย body:has(#modal-quota-so-preview) ด้วยเหตุผลเดียวกันเป๊ะ — กันไม่ให้ rule
   นี้ไปทำงานบนหน้าอื่นที่ไม่มีโมดัลนี้อยู่ในหน้า (เช่น so-view.html) แล้วซ่อนเอกสารทั้งหน้าไปด้วย */
@media print {
  body:has(#modal-quota-so-preview) > *:not(#modal-quota-so-preview) { display: none !important; }
  body:has(#modal-quota-so-preview) #modal-quota-so-preview { position: static; background: none; padding: 0; display: block !important; }
  body:has(#modal-quota-so-preview) #modal-quota-so-preview .modal { box-shadow: none; border: none; max-width: 100%; max-height: none; overflow: visible; padding: 0; margin: 0; }
  body:has(#modal-quota-so-preview) #modal-quota-so-preview .modal-header,
  body:has(#modal-quota-so-preview) #modal-quota-so-preview .modal-actions,
  body:has(#modal-quota-so-preview) #modal-quota-so-preview .notice-banner { display: none; }
  body:has(#modal-quota-so-preview) #modal-quota-so-preview .so-preview-scroll { overflow: visible; }
}
.so-signatures { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin-top: 60px; text-align: center; font-size: 13.12px; }
.so-signatures .line { border-top: 1px solid var(--text-faint); margin-top: 40px; padding-top: 6px; }

.so-notes { font-size: var(--so-doc-font); margin-top: 16px; margin-bottom: 24px; }
.so-notes ol { margin: 6px 0 0; padding-left: 20px; }
.so-notes li { border-bottom: 1px dashed var(--border-soft); padding: 4px 0; min-height: 1.3em; }
.so-notes p { margin: 6px 0 0; color: var(--text-soft); white-space: pre-wrap; }

/* แถบล่างสุดของเอกสาร SO — หมายเหตุ (กว้าง, ซ้าย) + กล่องสรุปยอดรวม (แคบ, ขวา) วางคู่กัน ตาม
   template จริง — margin-top:auto ผลักให้อยู่ล่างสุดของหน้าเสมอ (.so-doc เป็น flex column สูง
   เท่า A4 อย่างน้อย) ไม่ว่ารายการสินค้าด้านบนจะสั้นหรือยาวแค่ไหน */
.so-footer { display: flex; gap: 20px; margin-top: auto; padding-top: 20px; align-items: stretch; }
.so-notes-box { flex: 1; display: flex; flex-direction: column; }
.so-notes-label { font-size: var(--so-doc-font); font-weight: 600; margin-bottom: 6px; }
.so-notes-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--doc-border);
  border-radius: 2px;
  padding: 2px 10px;
}
/* 5 บรรทัดตายตัวให้กรอกด้วยมือ/พิมพ์ (มาจาก so.remark ในป๊อปอัป Create SO) — มีเส้นใต้แต่ละบรรทัด
   เหมือนแบบฟอร์มกระดาษจริง แม้ข้อความจะสั้นกว่า 5 บรรทัดก็ยังเห็นเส้นครบ */
.so-notes-line {
  flex: 1;
  min-height: 18px;
  line-height: 18px;
  border-bottom: 1px dashed var(--border-soft);
  font-size: var(--so-doc-font);
  color: var(--text-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.so-notes-line:last-child { border-bottom: none; }
table.so-totals-table {
  width: 260px;
  flex-shrink: 0;
  border-collapse: collapse;
  font-size: var(--so-doc-font);
}
table.so-totals-table td { border: 1px solid var(--doc-border); padding: 5px 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
table.so-totals-table td.k { color: var(--text-soft); }
table.so-totals-table td.v { text-align: right; font-weight: 600; }
table.so-totals-table tr.grand td { font-weight: 700; color: var(--gold-deep); }

@media print {
  /* @page margin 16mm ทุกด้าน (ทางการ สบายตา) — กินพื้นที่ไปบน-ล่างรวม 32mm จาก A4 สูง 297mm เหลือ
     พื้นที่พิมพ์จริง 265mm; กว้าง 210mm − 32mm = 178mm. เดิมใช้ 12mm (เหลือ 273mm สูง / 186mm กว้าง)
     แต่ผู้ใช้พบว่าเนื้อหาชิดขอบกระดาษเกินไป ตัวเลข Amount ตกขอบ — เพิ่ม margin เป็น 16mm แล้วคำนวณ
     ความสูงเอกสารใหม่ให้พอดีพื้นที่พิมพ์จริงด้านล่าง */
  @page { size: A4; margin: 16mm 14mm; }
  /* .so-doc ต้องสูงเท่าพื้นที่พิมพ์จริง (297 − 32 = 265mm) ไม่ใช่ 297mm เต็มแผ่น ไม่งั้นจะเกิน
     ขอบเขตที่ @page เว้นไว้ ไหลไปสร้างหน้าที่ 2 ทั้งที่เนื้อหาจริงพอดีแผ่นเดียว ยังคง overflow: hidden
     ไว้ (สืบทอดจาก .so-doc) กันเนื้อหาที่ยาวเกินจริงๆ ไหลออกไปหน้า 2
     padding: 2mm แทน 0 — ให้มีช่องว่างหายใจระหว่างขอบกระดาษ (16mm ของ @page) กับเนื้อหา รวมเป็นขอบ
     จริง ~18mm สบายตา ทางการ ตัวเลข Amount ไม่ตกขอบอีก */
  .so-doc { width: auto; height: 265mm; max-width: 100%; padding: 2mm; border: none; }
  /* .pi-doc ไม่ล็อกความสูงตายตัวเหมือน .so-doc (ปล่อยไหลหลายหน้าได้ถ้าเนื้อหายาว เช่นออเดอร์ที่มี
     หลายรายการสินค้า) แค่รีเซ็ต width/padding/border ให้พอดีกระดาษเหมือนกัน */
  .pi-doc { width: auto; min-height: 0; max-width: 100%; padding: 2mm; border: none; }
  /* .invpl-doc-v2 — รีเซ็ตให้พอดีกระดาษเหมือนกัน (ล็อก A4 เดียวเหมือน .so-doc) */
  .invpl-doc-v2 { width: auto; min-height: 265mm; max-width: 100%; padding: 2mm; border: none; }
  /* Chrome bug ที่รู้จักกันดี: display:flex ผสมกับความสูงตายตัวเป็นหน่วยจริง (mm) เรนเดอร์เป็นหน้าเปล่า
     ในตัวอย่างก่อนพิมพ์/พิมพ์จริง ทั้งที่ preview บนจอปกติเห็นข้อมูลครบ (เจอจริงกับ .pi-doc ผ่าน print
     dialog จริงของผู้ใช้ — หน้ากระดาษว่างเปล่าทั้งที่หน้าเว็บข้างหลัง dialog มีข้อมูลครบ) — ลูกของทั้ง 3
     เอกสารเป็น block-level ทั้งหมดอยู่แล้ว (h2/table/div) เปลี่ยนเป็น display:block เฉพาะตอนพิมพ์จึงหน้าตา
     เหมือนเดิมทุกอย่าง แค่เลี่ยง flex formatting context ที่ทำให้เกิดบั๊กนี้ */
  .so-doc, .pi-doc, .invpl-doc-v2 { display: block; }
  /* .so-footer ปกติใช้ margin-top:auto ผลักลงล่างสุดของ flex column parent — ใช้ไม่ได้แล้วหลัง parent
     เปลี่ยนเป็น block ด้านบน (margin-top:auto กลายเป็น 0 เฉยๆ ใน block layout) ต้องปักหมุดด้วย
     position:absolute แทนเพื่อให้ยังติดขอบล่างสุดของหน้าเป๊ะเหมือนเดิม */
  .so-doc, .invpl-doc-v2 { position: relative; }
  .so-doc .so-footer, .invpl-doc-v2 .invpl-v2-note { position: absolute; left: 0; right: 0; bottom: 0; margin-top: 0; }
}

/* ---------------- Export Order: SO preview modal ---------------- */

.so-preview-scroll {
  max-height: 60vh;
  overflow-y: auto;
  overflow-x: auto;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 12px;
  margin-bottom: 18px;
  background: var(--bg-soft);
}

/* .so-doc/.pi-doc ปกติกว้าง 210mm ตายตัว แต่มี max-width:100% ไว้เผื่อจอแคบ (ดู .so-doc-page ด้านบน
   สำหรับหน้า print จริง) — ป็อปอัป Preview นี้ (.modal-wide กว้างสุด 920px ลบ padding ของ
   modal-overlay/modal/so-preview-scroll ไปอีก ~132px) แคบกว่า 210mm (~794px) อยู่แล้วแทบทุกจอ ไม่ใช่
   แค่จอมือถือ — ถ้าปล่อยให้ max-width:100% บีบความกว้างแต่ height ยังตายตัวเป็น mm เท่าเดิม เอกสารที่
   เห็นใน Preview จะเพี้ยนสัดส่วนไปจากที่พิมพ์จริงเสมอ (แคบ/สูงผิดสัดส่วน) ทั้งที่ตอนพิมพ์จริงจาก
   so-view.html/pi-view.html ถูกต้องเป๊ะ (@media print กำหนด width:auto ชนกับขนาด @page จริงแทน) —
   ล็อกให้ Preview แสดงที่ขนาดจริงเสมอเหมือนกัน แล้วให้ so-preview-scroll เลื่อนแนวนอนแทนถ้าไม่พอ
   (เหมือน .so-doc-page ด้านบนทำไว้กับหน้า print จริง) กัน Preview กับที่พิมพ์จริงหน้าตาไม่ตรงกัน */
.so-preview-scroll .so-doc,
.so-preview-scroll .pi-doc {
  max-width: none;
}

/* ===========================================================================
   Invoice & Packing List — v2 (clean rewrite, matches INV PL NEW.pdf 1:1)
   แยกจาก .invpl-doc (v1) อย่างสมบูรณ์จนกว่าจะทดสอบครบแล้วลบ v1 ทิ้ง — ใช้ Inter
   (sans-serif minimal สบายตา), class .num ติดบน cell ตัวเลขโดยตรง (ไม่ใช้ nth-child)
   จึงไม่มีทางพังเรื่อง alignment แม้จะมี colspan/rowspan กี่ชั้น
   =========================================================================== */
.invpl-doc-v2 {
  --v2-bg: #FFFFFF;
  --v2-text: #1F1E1B;
  --v2-text-soft: #6B6A63;
  --v2-border: #C9C7BD;
  --v2-border-soft: #E5E3DA;
  --v2-bg-soft: #F4F3EF;

  background: var(--v2-bg);
  width: 210mm;
  min-height: 297mm;
  max-width: 100%;
  margin: 0 auto;
  padding: 14mm 15mm;
  box-sizing: border-box;
  font-family: 'Inter', 'Helvetica Neue', Arial, sans-serif;
  font-size: 10px;
  line-height: 1.4;
  color: var(--v2-text);
  display: flex;
  flex-direction: column;
  position: relative;
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}
/* Packing List หลายหน้า */
.invpl-doc-v2 + .invpl-doc-v2 { margin-top: 16px; }
@media print {
  .invpl-doc-v2 + .invpl-doc-v2 { page-break-before: always; margin-top: 0; }
}
.invpl-v2-page-no { position: absolute; right: 12px; bottom: 8px; font-size: 8px; color: var(--v2-text-soft); }

/* ---- Title ---- */
.invpl-v2-title {
  text-align: right;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.14em;
  margin: 0 0 10px;
  color: var(--v2-text);
}

/* ---- Header block (table ตรงต้นแบบ PDF: Seller ซ้าย rowspan, 2 คอลัมน์ขวา) ---- */
.invpl-doc-v2 table.invpl-v2-h-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
  margin-bottom: 10px;
}
.invpl-doc-v2 table.invpl-v2-h-table td {
  border: 1px solid var(--v2-border);
  padding: 5px 8px;
  vertical-align: top;
  width: 25%;
}
/* ต้องมี table.invpl-v2-h-table td นำหน้าด้วย ไม่งั้น specificity แพ้กฎ width:25% ด้านบน (td ธรรมดา
   ใน table.invpl-v2-h-table td ชนะ td.invpl-v2-h-seller เฉยๆ เพราะมี type selector "table" เพิ่มมา) —
   ผลคือ Seller เคยเหลือแค่ ~33% แทนที่จะเป็น 50% จริง ทำให้พื้นที่ไม่พอสำหรับโลโก้ ข้อความเลยทับกัน */
.invpl-doc-v2 table.invpl-v2-h-table td.invpl-v2-h-seller {
  width: 50%;
}
/* Seller name/address (ซ้าย) + โลโก้ (ขวา) วางคู่กันแบบ flex แทน position:absolute เดิม — กันโลโก้
   ทับซ้อนกับชื่อบริษัท (ที่ยาวเต็มบรรทัดพอดี) เพราะ flex แบ่งพื้นที่ให้ข้อความจริงตามที่เหลือเสมอ */
.invpl-doc-v2 .invpl-v2-seller-flex { display: flex; align-items: flex-start; justify-content: space-between; gap: 6px; }
.invpl-doc-v2 .invpl-v2-logo { display: block; max-width: 36px; max-height: 30px; flex: none; margin-top: 2px; }
.invpl-doc-v2 .invpl-v2-h-label {
  font-size: 7.5px;
  font-weight: 600;
  color: var(--v2-text-soft);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: 2px;
}
/* .invpl-v2-h-value (Notify Party/Consignee ฯลฯ) เป็นข้อความที่อยู่/ชื่อบริษัทจริง ตั้งใจให้ตกบรรทัด
   ได้ตามปกติ (ต่างจากตารางสินค้า/Packing List ที่ห้ามตกบรรทัดเด็ดขาดด้านบน) — override white-space:
   nowrap ที่สืบทอดมาจาก td ทั่วไปกลับเป็น normal เฉพาะ field กลุ่มนี้ */
.invpl-doc-v2 .invpl-v2-h-value { font-weight: 500; white-space: normal; overflow: visible; text-overflow: clip; word-break: break-word; }
/* ชื่อบริษัท/ที่อยู่ผู้ขาย — ข้อความหัวจดหมายจริง ปล่อยตกบรรทัดได้ตามปกติถ้ายาวเกิน (เหมือนป้ายหัวจดหมาย
   ทั่วไป ไม่ใช่ตารางข้อมูลที่ห้ามตกบรรทัด) override nowrap ที่สืบทอดมาจาก td ทั่วไปกลับเป็น normal */
.invpl-doc-v2 .invpl-v2-seller-flex > div { white-space: normal; overflow: visible; text-overflow: clip; word-break: break-word; min-width: 0; }
.invpl-doc-v2 .invpl-v2-seller-name { font-weight: 700; margin: 2px 0 2px; font-size: 11px; white-space: nowrap; }
.invpl-doc-v2 .invpl-v2-h-mark { font-weight: 500; margin-top: 2px; white-space: normal; overflow: visible; text-overflow: clip; word-break: break-word; }
/* Notify Party / Consignee — ซ้าย-ขวา แถวเดียวกัน สูงใกล้เคียง Seller (label + ชื่อ + ที่อยู่หลายบรรทัด)
   ใช้ min-height ไม่ใช่ height ตายตัว — กันช่องว่าง "แหว่ง" ถ้าเนื้อหาสั้นกว่า แต่ยังขยายได้ถ้าที่อยู่ยาว
   Seller สูง ~3 แถวขวา (label+ชื่อ+4 บรรทัดที่อยู่ ≈ 5em) — Notify/Consignee ตั้งให้ใกล้เคียง */
.invpl-doc-v2 td.invpl-v2-h-tall { min-height: 4.8em; height: 4.8em; }

/* ---- Shared table base (items + packing list) ---- */
.invpl-doc-v2 table { width: 100%; border-collapse: collapse; table-layout: fixed; font-variant-numeric: tabular-nums; }
/* ข้อกำหนดที่ 1: ตัวเลข/ตัวหนังสือห้ามตกบรรทัดเด็ดขาด — white-space:nowrap (สืบทอดลง div ลูกด้วย)
   + overflow/text-overflow เผื่อกรณีข้อมูลจริงยาวเกินความกว้างคอลัมน์ (ตัดด้วย … แทนการตกบรรทัด/ทับซ้อน
   กับคอลัมน์ข้างเคียง — แพทเทิร์นเดียวกับ .so-doc table.so-meta-table ที่ใช้อยู่แล้วในเอกสาร SO) เซลล์ที่
   มีหลายบรรทัดตั้งใจ (เช่น Product Description: ชื่อ+Size+Color คนละ <div>) ยังซ้อนกันได้ตามปกติ เพราะ
   นี่คือหลาย block คนละกล่อง ไม่ใช่ข้อความเดียวที่ตกบรรทัด */
.invpl-doc-v2 th, .invpl-doc-v2 td {
  border: 1px solid var(--v2-border);
  padding: 4px 6px;
  line-height: 1.3;
  vertical-align: top;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.invpl-doc-v2 td > div, .invpl-doc-v2 th > div {
  overflow: hidden;
  text-overflow: ellipsis;
}
/* class .num ติดบน cell โดยตรง → ชิดขวา (nowrap สืบทอดมาจาก th/td ด้านบนอยู่แล้ว) */
.invpl-doc-v2 td.num, .invpl-doc-v2 th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* ---- Invoice items table ---- */
.invpl-doc-v2 table.invpl-v2-items { margin-bottom: 0; }
.invpl-doc-v2 table.invpl-v2-items col.col-no { width: 32px; }
.invpl-doc-v2 table.invpl-v2-items col.col-remark { width: 72px; }
.invpl-doc-v2 table.invpl-v2-items col.col-qty { width: 60px; }
.invpl-doc-v2 table.invpl-v2-items col.col-price { width: 78px; }
.invpl-doc-v2 table.invpl-v2-items col.col-amount { width: 98px; }
.invpl-doc-v2 table.invpl-v2-items thead th {
  background: var(--v2-bg-soft);
  color: var(--v2-text-soft);
  font-weight: 700;
  text-align: center;
}
.invpl-doc-v2 table.invpl-v2-items th .sub {
  display: block;
  font-weight: 400;
  font-size: 7.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.invpl-doc-v2 table.invpl-v2-items tbody td { vertical-align: top; }
/* แถว Total ท้ายตารางสินค้า Invoice (ตามต้นแบบ: label "Total" + จำนวนรวม + "THB") */
.invpl-doc-v2 table.invpl-v2-items tr.invpl-v2-items-total td {
  font-weight: 700;
  background: var(--v2-bg-soft);
  border-top: 1px solid var(--v2-border);
}

/* ---- Packing List table ---- */
.invpl-doc-v2 table.invpl-v2-pl { font-size: 9px; }
.invpl-doc-v2 table.invpl-v2-pl col.col-product { width: 15%; }
.invpl-doc-v2 table.invpl-v2-pl col.col-model { width: 8%; }
.invpl-doc-v2 table.invpl-v2-pl col.col-size { width: 6%; }
.invpl-doc-v2 table.invpl-v2-pl col.col-color { width: 9%; }
.invpl-doc-v2 table.invpl-v2-pl col.col-qty { width: 7%; }
.invpl-doc-v2 table.invpl-v2-pl col.col-dimension { width: 14%; }
.invpl-doc-v2 table.invpl-v2-pl col.col-vol { width: 8%; }
.invpl-doc-v2 table.invpl-v2-pl col.col-weight { width: 7%; }
.invpl-doc-v2 table.invpl-v2-pl col.col-nw { width: 13%; }
.invpl-doc-v2 table.invpl-v2-pl col.col-gw { width: 13%; }
.invpl-doc-v2 table.invpl-v2-pl thead th,
.invpl-doc-v2 table.invpl-v2-pl tr.invpl-v2-pl-colhead td {
  background: var(--v2-bg-soft);
  color: var(--v2-text-soft);
  font-weight: 700;
  text-align: center;
}
.invpl-doc-v2 table.invpl-v2-pl tr.invpl-v2-pl-subtotal td {
  background: var(--v2-bg);
  font-weight: 700;
}
.invpl-doc-v2 table.invpl-v2-pl tr.invpl-v2-pl-grandtotal td {
  background: var(--v2-bg-soft);
  font-weight: 700;
}
/* ลดขนาด font ถ้า Packing List มีแถวเยอะ (ข้อกำหนดที่ 3 — ยืดหยุ่น) */
.invpl-doc-v2.invpl-v2-dense table.invpl-v2-pl { font-size: 8px; }
.invpl-doc-v2.invpl-v2-dense table.invpl-v2-pl td,
.invpl-doc-v2.invpl-v2-dense table.invpl-v2-pl th { padding: 2px 4px; }

/* ---- Footer (qty summary box + cost box) ---- */
.invpl-v2-footer {
  display: flex;
  gap: 16px;
  margin-top: 12px;
}
.invpl-v2-foot-col { flex: 1; }
.invpl-v2-foot-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  column-gap: 8px;
  padding: 2px 0;
}
.invpl-v2-foot-label { color: var(--v2-text-soft); }
.invpl-v2-foot-num { font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; }
.invpl-v2-foot-unit { color: var(--v2-text-soft); font-size: 9px; }
.invpl-v2-foot-strong { border-top: 1px solid var(--v2-border-soft); margin-top: 3px; padding-top: 4px; }
.invpl-v2-foot-fob { font-weight: 700; border-top: 1px solid var(--v2-border-soft); border-bottom: 1px solid var(--v2-border-soft); margin: 2px 0; padding: 3px 0; }
.invpl-v2-foot-total {
  margin-top: 5px;
  padding: 6px 4px;
  border-top: 1px solid var(--v2-border-soft);
  background: var(--v2-bg-soft);
  font-size: 14px;
  font-weight: 700;
}
.invpl-v2-foot-total .invpl-v2-foot-label { color: var(--v2-text); }
.invpl-v2-foot-total .invpl-v2-foot-unit { font-size: 9px; }

/* ---- Note ---- */
.invpl-v2-note {
  text-align: center;
  font-size: 8.5px;
  letter-spacing: 0.05em;
  color: var(--v2-text-soft);
  margin-top: auto;
  padding-top: 10px;
}

/* ปิดมุม modal ไม่ให้บีบความกว้างเอกสาร */
.so-preview-scroll .invpl-doc-v2 { max-width: none; }

/* ---------------- Financial page ---------------- */

/* ตาราง Dashboard Statement ผสมแถว Shipment (ไม่มีปุ่ม)/Payment (มีปุ่ม Edit-Delete) อยู่ในตารางเดียวกัน
   — ถ้าใช้ .btn-sm ขนาดปกติ แถว Payment จะสูงกว่าแถว Shipment ชัดเจน (ปุ่มดันความสูงแถวขึ้นไปกว่าแถว
   ที่มีแต่ตัวเลข/ขีด) ทำให้เส้นแบ่งแถวเห็นเป็นจังหวะไม่สม่ำเสมอตลอดตาราง ย่อปุ่มเฉพาะในตารางนี้ให้แถว
   สูงใกล้เคียงกับแถว Shipment ที่ไม่มีปุ่ม */
#fin-statement-table .btn-sm { padding: 3px 10px; font-size: 0.72rem; }

/* 2 คอลัมน์แรก (Payment Date/Amount) ให้ font ต่างจากคอลัมน์อื่นในตาราง ทั้งหัวข้อและข้อมูล (ตามที่ผู้ใช้
   ระบุ) — เดิมลองใช้ monospace ทั่วไป (Courier New) แต่ผู้ใช้บอกว่าไม่เข้าธีมแอป ให้ใช้ 'Sarabun' แทน
   (font เดียวกับที่เอกสาร SO/PI/INV&PL ใช้อยู่แล้ว — ดู .so-doc ด้านบน, โหลดผ่าน @import ต้นไฟล์นี้แล้ว)
   ตัดกับ font หลักของแอป ('Poppins'/'Noto Sans Thai' ที่คอลัมน์อื่นยังใช้อยู่) แบบ minimal เข้าธีม —
   tabular-nums ให้ตัวเลขกว้างเท่ากันทุกหลัก อ่านจำนวนเงิน/วันที่ง่ายขึ้น (แพทเทิร์นเดียวกับที่ .invpl-doc-v2
   table ใช้อยู่แล้ว) */
#fin-statement-table .fin-stmt-highlight-col { font-family: 'Sarabun', sans-serif; font-variant-numeric: tabular-nums; }

.fin-customer-bar { display: flex; flex-wrap: wrap; gap: 8px; }
.fin-customer-chip {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 8px 14px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.fin-customer-chip:hover { border-color: var(--gold); }
.fin-customer-chip.active { border-color: var(--gold); background: var(--gold-soft); }
.fin-chip-code { font-weight: 700; font-size: 0.8rem; color: var(--gold-deep); }
.fin-chip-name { font-size: 0.68rem; color: var(--text-soft); }

/* ปุ่มเลือกออเดอร์ในหน้า Arrangement (#arr-order-bar) ใช้ .fin-chip-code เป็นป้าย Order No. ร่วมกับ
   .order-no-label/.order-no-arranged/.order-no-overdue (badge สีเดียวกับที่ Order Management ใช้ทั่วแอป)
   — ต้องรวม 2 class ให้ specificity สูงกว่า .fin-chip-code เฉยๆ (ซึ่งมาทีหลังในไฟล์นี้) ไม่งั้นสีทอง
   ปกติจะทับสี arranged/overdue ไปเสีย */
.fin-chip-code.order-no-arranged { color: var(--success); }
.fin-chip-code.order-no-overdue { color: var(--danger); }

.arr-detail-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  align-items: start;
}
@media (max-width: 860px) {
  .arr-detail-row { grid-template-columns: 1fr; }
}

.fin-customer-header { margin-bottom: 20px; }
.fin-customer-code { font-size: 0.8rem; font-weight: 700; color: var(--gold-deep); }
.fin-customer-name { font-size: 1.3rem; font-weight: 700; }

/* กรอบ Drop file — ปุ่ม + ตรงกลาง ลากไฟล์มาวางได้ หรือกดปุ่ม + เปิด file picker */
.fin-dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border: 2px dashed var(--border);
  border-radius: var(--radius-lg);
  padding: 32px;
  margin: 16px 0 12px;
  text-align: center;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.fin-dropzone.dragover { border-color: var(--gold); background: var(--gold-soft); }
.fin-dropzone-btn {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: none;
  background: var(--gold);
  color: #fff;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
}
.fin-dropzone-btn:hover { background: var(--gold-deep); }

.fin-staged-list { display: flex; flex-direction: column; gap: 6px; }
.fin-staged-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-soft);
  background: var(--surface-alt);
  font-size: 0.85rem;
}
.fin-staged-remove-btn {
  border: none;
  background: none;
  color: var(--danger);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  flex: none;
}

/* แถวตรวจสอบหลังกด Proceed — Preview/ชื่อไฟล์/Date/Amount/สถานะ OCR ต่อไฟล์ */
.fin-review-row {
  display: grid;
  grid-template-columns: 64px 1fr 160px 160px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border-soft);
}
.fin-review-preview { width: 56px; height: 56px; display: flex; align-items: center; justify-content: center; }
.fin-review-preview img { max-width: 56px; max-height: 56px; border-radius: var(--radius-sm); object-fit: cover; }
.fin-review-filename { font-size: 0.85rem; }
.fin-review-status { font-size: 0.72rem; }

/* ---------------- หน้า CAL (cal.html) — CBM CAL / PRICE CAL ----------------
 * 2 การ์ดอิสระต่อกันซ้าย/ขวา สีคนละโทนตั้งแต่หัวการ์ดลงไป แบบเดียวกับ .card-arr-export/
 * .card-arr-operation ในหน้า Arrangement (navy = CBM/ข้อมูลอ้างอิงกายภาพ, honey = Price/มูลค่าเงิน)
 * ส่วนใหญ่ reuse component ที่มีอยู่แล้วทั้งเว็บ (.card/.card-title-row/.stat-tile/.stat-grid/
 * .data-table/.badge/.notice-banner/.modal/.tabs/.form-grid) จุดที่เพิ่มใหม่จริงๆ มีแค่ toggle chip
 * (โหมด LCL บนพาเลท/Without Pallet — ไม่มี component เดิมให้ใช้ซ้ำ) */
.cal-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  align-items: start;
}
/* breakpoint ของ .cal-columns ย้ายไปรวมกับ Tier B ("Responsive: A/B/C" ต้นไฟล์ส่วน responsive) แล้ว */

/* .card + .card { margin-top: 20px } (ต้นไฟล์ ตั้งใจไว้สำหรับการ์ดที่เรียงต่อกันแนวตั้ง) รั่วมาโดนการ์ด
   PRICE CAL ด้วย เพราะมันเป็น .card ตัวที่ 2 ถัดจาก CBM CAL ในลำดับ DOM (แม้ตำแหน่งจริงจะอยู่ข้างกันใน
   grid 2 คอลัมน์ ไม่ใช่เรียงต่อกันแนวตั้ง) ดันการ์ด PRICE CAL ต่ำกว่า CBM CAL ไป 20px ทั้งที่ควรเริ่มบรรทัด
   บนสุดพร้อมกัน (เจอบั๊กแบบเดียวกันมาก่อนแล้วที่ #quota-grids .quota-grid-row — แก้ด้วยวิธีเดียวกัน) */
.cal-columns > .card + .card {
  margin-top: 0;
}

.card-cal-cbm { border-top: 3px solid var(--navy); }
.card-cal-cbm .card-title-row h3 { color: var(--navy); }
.card-cal-cost { border-top: 3px solid var(--honey); }
.card-cal-cost .card-title-row h3 { color: var(--honey); }

.cal-section-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin: 20px 0 10px;
}
.cal-section-label::after { content: ""; flex: 1; height: 1px; background: var(--border-soft); }
.cal-section-label:first-child { margin-top: 0; }

.cal-input-block {
  background: var(--surface-alt);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 14px 16px;
  margin-bottom: 12px;
  /* กันแถบ Output/3D Render ของ CBM CAL กับ PRICE CAL อยู่คนละระดับ (เนื้อหาในบล็อก By Order/By Manual
     ของสองการ์ดยาวไม่เท่ากัน — PRICE CAL โหมด Manual มีช่อง Incoterms เพิ่มอีกช่อง) ตามที่ผู้ใช้ระบุ */
  min-height: 168px;
}
.cal-input-block:last-child { margin-bottom: 0; }
.cal-input-block-tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px; height: 20px;
  border-radius: 6px;
  background: var(--bg-soft);
  color: var(--text-soft);
  font-size: 0.7rem;
  font-weight: 700;
  margin-right: 8px;
}
.cal-input-block-title { font-size: 0.88rem; font-weight: 600; }
.cal-input-block-desc { font-size: 0.78rem; color: var(--text-soft); margin: 4px 0 10px; }

/* Toggle chip — LCL บนพาเลท/Without Pallet (ไม่มี component เดิมให้ใช้ซ้ำ ต่างจาก .fin-customer-chip
   ที่โครงสร้างเฉพาะสำหรับรายชื่อลูกค้าเกินไป) */
.cal-chip-row { display: flex; gap: 6px; flex-wrap: wrap; }
.cal-chip {
  font-size: 0.78rem;
  font-weight: 600;
  padding: 6px 13px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  color: var(--text-soft);
  background: var(--surface);
  cursor: pointer;
  user-select: none;
}
.cal-chip.on { background: var(--navy); border-color: var(--navy); color: #fff; }

/* สถานะราคาต่อ Incoterm — ต่อยอดจากระบบ .badge เดิม (ดู .badge-order-issued ฯลฯ) */
.badge-cal-needed { background: var(--gold-soft); color: var(--gold-deep); }
.badge-cal-no { background: var(--bg-soft); color: var(--text-faint); }
.badge-cal-auto { background: var(--success-soft); color: var(--success); }
.badge-cal-preset { background: var(--info-soft); color: var(--info); }

/* 3D Render (CBM CAL) — CSS 3D transform ล้วนๆ ไม่พึ่งไลบรารีภายนอก (ดู cal-3d.js) — .cal-3d-viewer
   คือ "กล้อง" (perspective) ที่รับ drag/scroll แล้วหมุน/ซูม .cal-3d-stage ซึ่งเป็นจุดยึด 3D จริง
   (transform-style:preserve-3d) ให้ .cal-3d-box แต่ละใบ (1 ใบ = 1 บล็อกคอลัมน์จาก calComputePacking)
   วางตัวเองสัมพัทธ์กันได้ */
.cal-3d-viewer {
  position: relative;
  height: 320px;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  background: linear-gradient(180deg, var(--surface-alt) 0%, var(--bg-soft) 100%);
  overflow: hidden;
  perspective: 1000px;
  cursor: grab;
  user-select: none;
}
.cal-3d-viewer.dragging { cursor: grabbing; }

.cal-3d-stage {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  transform-style: preserve-3d;
  transition: transform 0.05s linear;
}

.cal-3d-box {
  position: absolute;
  top: 0;
  left: 0;
  transform-style: preserve-3d;
}

.cal-3d-face {
  position: absolute;
  top: 0;
  left: 0;
  box-sizing: border-box;
}

.cal-3d-empty-hint {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 24px;
  color: var(--text-faint);
  font-size: 0.82rem;
  pointer-events: none;
}
.cal-3d-empty-hint.hidden { display: none; }

/* Dashboard คำอธิบายสีใต้ 3D Render — 1 รายการต่อ 1 "ประเภทกล่อง" (ดู calRenderCbmLegend ใน cal-3d.js) —
   ถ้ารายการเยอะ (โมเดล/สี/ไซส์เยอะ) ให้ย่อเป็นกล่อง scroll แทนที่จะยืดหน้าเว็บยาวลงไปเรื่อยๆ — max-height
   ประมาณ 8 แถว (แถวละ ~17.5px + gap 8px) ตามที่ผู้ใช้ระบุ align-content:flex-start กันแถวถูกยืดเว้นระยะ
   เท่าๆ กันเวลารายการน้อยกว่า max-height (พฤติกรรม default ของ flex-wrap คือ stretch) */
.cal-3d-legend {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 8px 16px;
  margin-top: 12px;
  max-height: 200px;
  overflow-y: auto;
  padding: 6px 8px;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
}
.cal-3d-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  color: var(--text-soft);
}
.cal-3d-legend-swatch {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  border: 1px solid rgba(0, 0, 0, 0.15);
  flex-shrink: 0;
}

/* ---------------- LDG RPT (Loading & Inspection Report) — หน้า Shipping ----------------
 * ใช้ .fin-dropzone/.fin-staged-list/.fin-staged-item ของหน้า Financial ซ้ำสำหรับกล่องอัปโหลดรูป
 * (ไม่สร้างสไตล์ใหม่ซ้ำซ้อน) ส่วนที่เพิ่มใหม่จริงๆ มีแค่ thumbnail ในรายการ + preview แบบทีละหน้า */
.ldgrpt-thumb { width: 32px; height: 32px; border-radius: var(--radius-sm); object-fit: cover; flex-shrink: 0; }

.ldgrpt-page-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 12px 0;
}

.ldgrpt-preview-page {
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  background: var(--bg-soft);
  padding: 24px;
  min-height: 400px;
  overflow-x: auto;
}

/* หน้ากระดาษจำลอง A4 แนวตั้งจริง (210mm × 297mm) เหมือน .so-doc/.invpl-doc-v2 — พื้นขาว-ตัวหนังสือเข้ม
   เสมอไม่ว่าธีมแอปตอนนี้จะมืดแค่ไหน (จำลองกระดาษจริงที่จะพิมพ์ออกมา ไม่ใช่ UI ของแอป) — pin ตัวแปรสีทับ
   เฉพาะในกรอบนี้ด้วยชื่อตัวแปรเดียวกับที่ table.matrix (renderGridTable) อ้างอิงอยู่แล้ว
   (--bg-soft/--border-soft/--surface-alt/--text-soft) ตารางสินค้าที่ใช้ renderGridTable ร่วมกับหน้า
   Order Management จึงได้หน้าตาแบบกระดาษเอกสารทันทีโดยไม่ต้องเขียน CSS ตารางแยกซ้ำ */
.ldgrpt-doc-page {
  --surface: #ffffff;
  --surface-alt: #ffffff;
  --bg-soft: #F4F6FA;
  --text: #2A2A2A;
  --text-soft: #5B6472;
  --text-faint: #98A1AD;
  --border: #DCE1E8;
  --border-soft: #EAEDF2;
  --gold-deep: #20409A;

  background: var(--surface);
  width: 210mm;
  min-height: 297mm;
  max-width: 100%;
  margin: 0 auto;
  padding: 14mm 15mm;
  box-sizing: border-box;
  font-family: 'Sarabun', sans-serif;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text);
  position: relative;
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}
.ldgrpt-doc-header { font-size: 1.3rem; font-weight: 700; letter-spacing: 0.04em; margin-bottom: 20px; color: var(--gold-deep); text-align: center; }
.ldgrpt-doc-fields { display: flex; flex-direction: column; gap: 6px; margin-bottom: 20px; padding-bottom: 16px; border-bottom: 1px solid var(--border-soft); }
.ldgrpt-field-row { display: flex; gap: 8px; font-size: 0.92rem; }
.ldgrpt-field-row span:first-child { min-width: 120px; font-weight: 600; color: var(--text-soft); }
.ldgrpt-doc-page .quota-line-block + .quota-line-block { margin-top: 20px; }

.ldgrpt-photo-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: repeat(3, 1fr);
  gap: 10px;
  height: 100%;
  min-height: 240mm;
}
.ldgrpt-photo-box {
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-soft);
}
.ldgrpt-photo-box img { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; }
.ldgrpt-photo-page-footer { position: absolute; right: 15mm; bottom: 10mm; font-size: 8px; color: var(--text-faint); }

/* สถิติฝั่ง CBM CAL (N.W./G.W./CBM/Dimension/CAP/Usage/Space) และ PRICE CAL (Exwork/TOTAL/Additional/
   Per-pair) — เรียงแนวตั้งทีละกล่อง แทนตาราง auto-fit หลายคอลัมน์แบบ .stat-grid ปกติ + ย่อขนาดกล่อง/
   ตัวหนังสือลง (ตามที่ผู้ใช้ระบุ) — ใช้ .stat-grid.cal-cbm-stat-grid/.cal-cost-stat-grid (2 คลาส) ไม่ใช่
   แค่คลาสเดี่ยวๆ เพื่อ specificity สูงกว่า .stat-grid เดิมแน่ๆ ไม่ต้องพึ่งลำดับประกาศในไฟล์ */
.stat-grid.cal-cbm-stat-grid,
.stat-grid.cal-cost-stat-grid { display: flex; flex-direction: column; gap: 6px; }
.cal-cbm-stat-grid .stat-tile,
.cal-cost-stat-grid .stat-tile { padding: 6px 10px; }
.cal-cbm-stat-grid .stat-tile .stat-label,
.cal-cost-stat-grid .stat-tile .stat-label { font-size: 0.64rem; margin-bottom: 2px; }
.cal-cbm-stat-grid .stat-tile .stat-value,
.cal-cost-stat-grid .stat-tile .stat-value { font-size: 0.92rem; }

/* CBM CAL — จัดกลุ่มกล่องสถิติเป็น 3 แถวตามที่ผู้ใช้ระบุ: N.W./G.W./CBM แถวเดียวกัน, Dimension แถวถัดมา
   (ของตัวเอง), CAP/Usage/Space แถวถัดมาอีกแถว — .cal-cbm-stat-grid เดิม (display:flex column) จัดการ
   แค่การเรียง "แถว" แนวตั้ง ส่วน .cal-cbm-stat-row นี้จัดกล่องภายในแถวเดียวกันให้เรียงแนวนอนแบ่งพื้นที่เท่ากัน */
.cal-cbm-stat-row { display: flex; gap: 6px; }
.cal-cbm-stat-row .stat-tile { flex: 1; min-width: 0; }

/* ---------------- Edit PDF modal (หน้า Summary > ป็อปอัป Payment > ปุ่ม Edit PDF ต่อไฟล์) ----------------
 * ดู pdf-edit.js — <embed> แสดงหน้า PDF จริงผ่าน viewer ของเบราว์เซอร์ ส่วน .pdf-edit-overlay ลอยทับ
 * ตำแหน่งเดียวกันเป๊ะ (position:absolute; inset:0) รับคลิก/ลากวางข้อความ/ไฮไลท์ — .pdf-edit-canvas ทั้ง
 * กล่องกำหนดความกว้าง/สูงเป็น px ตรงๆ จาก JS (ตามสัดส่วนหน้าจริง) ไม่ใช้ CSS ตายตัว */
.pdf-edit-toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.pdf-edit-tool-btn.active { background: var(--gold-soft); border-color: var(--gold); color: var(--gold-deep); }
.pdf-edit-page-nav { display: flex; align-items: center; gap: 8px; margin-left: auto; font-size: 0.8rem; color: var(--text-soft); }
.pdf-edit-page-nav .btn { padding: 2px 10px; }

.pdf-edit-canvas-wrap { overflow-x: auto; display: flex; justify-content: center; padding-bottom: 8px; }
.pdf-edit-canvas { position: relative; border: 1px solid var(--border-soft); border-radius: var(--radius-sm); overflow: hidden; flex-shrink: 0; }
.pdf-edit-canvas embed { position: absolute; inset: 0; width: 100%; height: 100%; border: none; }
.pdf-edit-overlay { position: absolute; inset: 0; }

.pdf-edit-ann { position: absolute; cursor: default; }
.pdf-edit-ann-text {
  color: #b30000;
  background: rgba(255, 255, 255, 0.75);
  padding: 1px 3px;
  white-space: nowrap;
  font-weight: 600;
  transform: translateY(-2px);
}
.pdf-edit-ann-highlight { background: rgba(255, 235, 59, 0.4); border: 1px dashed rgba(184, 134, 11, 0.7); box-sizing: border-box; }
.pdf-edit-ann-preview { background: rgba(255, 235, 59, 0.25); border: 1px dashed var(--gold); }

.pdf-edit-ann-del {
  display: none;
  position: absolute;
  top: -8px;
  right: -8px;
  width: 16px;
  height: 16px;
  line-height: 14px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--danger);
  background: var(--surface);
  color: var(--danger);
  font-size: 0.7rem;
  cursor: pointer;
}
.pdf-edit-ann:hover .pdf-edit-ann-del { display: block; }

/* ---------------- Responsive: A (Desktop) / B (Tablet) / C (Smartphone) ----------------
 * ตั้งใจวางไว้ท้ายสุดของไฟล์ (หลัง selector ทุกตัวที่ต้อง override รวมถึง .cal-columns/.grid-4col/
 * .stat-grid-205s ที่ base rule อยู่ก่อนหน้านี้ในไฟล์) เพราะ specificity ของ media query เท่ากับ
 * selector ปกติทุกตัวที่นี่ (ไม่มีตัวไหนมี id/ซ้อนคลาสเพิ่ม specificity) — cascade เลยตัดสินด้วยลำดับ
 * ไฟล์ล้วนๆ ถ้า override มาก่อน base rule ในไฟล์ base rule จะชนะไปเงียบๆ (เจอบั๊กนี้จริงตอนทดสอบ
 * .cal-columns ไม่ยอมยุบ 1 คอลัมน์ที่ Tier B/C) วางท้ายไฟล์เป็นวิธีที่ปลอดภัยสุด ไม่ต้องไล่เพิ่ม
 * specificity (!important/ซ้อนคลาส) ทีละจุดทุกครั้งที่มีการ์ด/หน้าใหม่มาเพิ่มทีหลัง
 *
 * สโคปด้วย "screen" ล้วน (ไม่ใช่แค่ max-width เฉยๆ) เพื่อไม่ให้กระทบเอกสาร SO/PI/INV&PL ตอน Print/Save
 * PDF ที่ล็อกขนาด A4 ไว้ตายตัว (ฟอนต์ของเอกสารนั้นแปลงเป็น px ตายตัวแล้ว ไม่ผูกกับ rem จึงไม่โดน
 * html { font-size } ด้านล่างนี้กระทบด้วย)
 *
 * มาตรฐานเดียว 3 ระดับ (เดิมกระจัดกระจาย 1024/820/640 ปนกับ .grid-4col ที่แยก breakpoint 820px ของ
 * ตัวเอง และ .cal-columns ที่แยก 880px ของตัวเอง — รวมเป็นจุดเดียวที่นี่ทั้งหมดแล้ว):
 *   Tier A (Desktop/Laptop) — ค่าเริ่มต้นทั้งไฟล์ (ไม่ต้องมี media query) มีผลตั้งแต่ ≥1024px ขึ้นไป
 *     รวม iPad แนวนอนทุกรุ่น (1024px ขึ้นไป) ด้วย — ยึดความกว้างจริงเป็นหลัก ไม่ยึดชนิดอุปกรณ์
 *   Tier B (Tablet) — ≤1023px
 *   Tier C (Smartphone) — ≤767px (ประกาศไว้หลัง Tier B ในไฟล์ เติม override เพิ่มจาก Tier B
 *     ไม่ใช่แทนที่ทั้งหมด — ตาม cascade ปกติของ CSS)
 *
 * ตัวอักษรเกือบทั้งเว็บ (h1-h3, ปุ่ม, badge, ตาราง, ฟอร์ม, stat tile ฯลฯ) กำหนดด้วยหน่วย rem ซึ่งอิงตาม
 * font-size ของ <html> เสมอ (ไม่ใช่ของ body หรือ element แม่ใดๆ) — การลด font-size ของ <html> ต่อ
 * tier จึงย่อทุกอย่างลงพร้อมกันตามสัดส่วนเดิมทุกอัตราส่วน (ตัวใหญ่/ตัวเล็กยังต่างกันเท่าเดิม) แทนที่จะ
 * ต้องไล่แก้ font-size ทีละจุดแบบเดิม */

/* Tier A — ปรับความหนาแน่นภายในตัว (1024-1400px) ไม่ใช่ tier boundary — ปุ่ม order-actions
   (Edit/Create SO/MAC5/Finish Order/Add Docs/Delete) ต้องอยู่แถวเดียวเสมอ ห้ามซ้อน ห้ามหลุดจอ แม้ตอน
   ย่อหน้าต่าง desktop แคบลง หรือดูบน iPad แนวนอน (กว้างสุด ~1366px) — ลองปล่อยให้ห่อ 2 บรรทัดมาแล้ว
   ใช้ไม่ได้จริง (ปุ่มถูกดันตกบรรทัดกลางกลุ่ม อ่าน/กดยาก) วิธีที่ได้ผลจริง: คอลัมน์ order-actions
   (col.col-actions) กว้างตายตัวเป็น px ตรงๆ (พอสำหรับ 6 ปุ่มแถวเดียวจริงๆ ตามที่วัดได้) แทนสัดส่วน %
   เดิม — ตารางเองเลื่อนแนวนอนได้ไม่จำกัดความกว้างอยู่แล้ว (#orders-table/#shipping-table ไม่ตัดคำ/ไม่
   บีบคอลัมน์อีกต่อไป ดูส่วน "Data tables — เลื่อนแนวนอนเสมอ ไม่ตัดคำ") จุดนี้แค่กันปุ่ม action ไม่ให้
   ซ้อน/หลุดจอตอนคอลัมน์ actions เองยังพอมีที่บนจอกว้างระดับนี้ */
@media screen and (max-width: 1400px) {
  .summary-table col.col-actions { width: 300px; }

  .btn-label-full { display: none; }
  .btn-label-short { display: inline; }

  .order-actions .btn {
    padding: 5px 8px;
    font-size: 0.7rem;
  }

  /* ปุ่ม Create SO/Re-issue SO สลับมาโชว์ label สั้น (btn-label-short) ที่ tier นี้แล้ว (บรรทัดด้านบน)
     แต่ .create-so-btn min-width: 80px (ค่า global ด้านบนของไฟล์) ยังคุมความกว้างไว้ตามข้อความยาว
     "Re-issue SO" เดิม เกินความจำเป็นเมื่อโชว์แค่ "Re-issue"/"SO" สั้นๆ — วัดจริงด้วย canvas.measureText()
     ที่ font 0.7rem แล้ว "Re-issue" (label สั้นที่ยาวสุดในสองแบบ) กว้าง ~50px ลด min-width ลงให้พอดี
     ข้อความสั้นแทน (ตามที่ผู้ใช้ระบุ) */
  .create-so-btn { min-width: 68px; }
}

/* ---------------- Tier B: Tablet (≤1024px) ---------------- */
/* เดิม max-width: 1023px — iPad แนวนอน (landscape) มีความกว้างจอ CSS 1024px พอดี เลยหลุดออกจาก tier นี้
   ไปใช้ขนาด Desktop เต็มๆ แทน (ป้าย/ปุ่ม/ตาราง/ช่องวันที่ใน Order List เลยดูใหญ่เกินตามที่ผู้ใช้แจ้งว่า
   "ทั้งสองแนวเลย สัดส่วนใหญ่ไป") ขยับ breakpoint ขึ้นมา 1px ให้ครอบคลุม 1024px ด้วย */
@media screen and (max-width: 1024px) {
  html { font-size: 15px; }

  .page, .page.page-wide { padding: 12px 20px 60px; }
  .container { padding: 0 20px; }

  .topbar-inner { flex-wrap: wrap; padding: 10px 20px; gap: 8px; }
  .nav-links {
    order: 3;
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  .stat-grid { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
  #quota-grids .quota-grid-row { grid-template-columns: repeat(2, 1fr); }
  .grid-4col { grid-template-columns: repeat(2, 1fr); }
  .cal-columns { grid-template-columns: 1fr; }

  /* การตรึง (sticky/pinned) เป็นฟีเจอร์เฉพาะ Desktop/Laptop เท่านั้นตามที่ผู้ใช้ระบุ — Tablet/
     Smartphone ปล่อยให้เลื่อนหลุดไปตามธรรมชาติทั้งหมด (หัวหน้าเพจ/หัวตาราง/คอลัมน์แรกของ matrix)
     ยกเว้นแถบเมนูบนสุด (.topbar) ที่ยังคง position:fixed ทุกจอเสมอ เพราะเป็น app chrome หลัก ไม่ใช่
     การตรึงเนื้อหาซ้ำแบบที่กติกานี้หมายถึง (ผู้ใช้ยืนยันแยกไว้ชัดเจน) — position:static ทำให้ top/left
     ที่ syncStickyHeaderTop()/syncNavLayout() (nav.js) ยังเขียนทับผ่าน inline style ไม่มีผลอะไรเลย
     (top/left ไม่มีผลกับ position:static ตาม spec) ไม่ต้องแก้ JS เพิ่มสำหรับจุดนี้ */
  .sticky-page-header,
  #quota-grids {
    position: static;
    top: auto;
  }
  .db-scroll-table table.data-table thead th {
    position: static;
  }
  table.matrix td:first-child,
  table.matrix th:first-child {
    position: static;
    left: auto;
  }

  /* บล็อกออเดอร์ (หน้า Order Management > Order List) ตั้งขนาดไว้สำหรับจอ Desktop — Tier B (Tablet)
     เดิมได้แค่ html font-size ที่ลดลงนิดเดียว (16px -> 15px) ป้าย/ปุ่ม/ตารางเลยยังดูใหญ่/เบียดกันบน
     iPad ทั้งแนวตั้งและแนวนอนตามที่ผู้ใช้ระบุ — ลดขนาดเฉพาะจุดเพิ่มอีกชั้นเฉพาะ tier นี้ */
  .quota-order-header { gap: 6px; margin-bottom: 4px; }
  .order-no-label { font-size: 0.72rem !important; padding: 2px 6px; }
  input.order-date-input { width: 82px; font-size: 0.58rem; }
  .order-actions { gap: 4px; }
  .order-actions .btn { padding: 4px 8px; font-size: 0.68rem; }
  .quota-order-block table.matrix { font-size: 0.58rem; }
  .quota-order-block table.matrix th,
  .quota-order-block table.matrix td { padding: 2px 3px; }
  .quota-order-block table.matrix td:first-child,
  .quota-order-block table.matrix th:first-child { width: 56px; min-width: 56px; }
}

/* ---------------- Tier C: Smartphone (≤767px) ---------------- */
@media screen and (max-width: 767px) {
  html { font-size: 14px; }

  .page, .page.page-wide { padding: 10px 14px 48px; }
  .card { padding: 18px; }

  .topbar-inner { padding: 10px 14px; }

  .stat-grid { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
  .stat-tile { padding: 14px 16px; }

  #quota-grids .quota-grid-row { grid-template-columns: 1fr; }
  #home-dashboard-card .stat-grid-205s { grid-template-columns: 1fr; }

  .form-grid { grid-template-columns: 1fr; gap: 0; }

  .modal-overlay { padding: 10px; }
  .modal { padding: 18px; max-height: 92vh; }
  .modal-header { margin-bottom: 14px; }

  .month-bar-value { width: 100px; }
  .month-bar-label { width: 32px; }

  .page-header .flex { flex-wrap: wrap; }

  /* จอแคบ — สลับ sub-column ของการ์ด Master Mod (Panel B/C) จากเรียงข้างเป็นเรียงซ้อนแนวตั้ง กันตัวเลข
     คู่/บาทถูกบีบแคบจนขึ้นบรรทัดใหม่/ล้นกล่อง เปลี่ยนเส้นคั่นจากแนวตั้งเป็นแนวนอนตามทิศทางที่เปลี่ยน */
  .mastermod-segments { flex-direction: column; gap: 10px; }
  .mastermod-segment + .mastermod-segment { border-left: none; border-top: 1px solid var(--border-soft); padding-top: 10px; }
  /* กล่อง 200/205-S การ์ด Monthly Report ยุบเป็นคอลัมน์เดียวจอแคบ (ปกติ 2fr/3fr กว้างไม่เท่ากันตาม desktop) */
  #mr-body .mastermod-grid { grid-template-columns: 1fr; }

  /* กราฟวงกลม (Sales by Country) — วางซ้อนแนวตั้งแทนเรียงข้างกัน กันวงกลม+legend ถูกบีบจนอ่านยาก */
  .pie-chart-row { flex-direction: column; align-items: stretch; }
  .pie-legend { min-width: 0; }
}
