/* ===================================================================
   base.css — โทนสี, typography, reset พื้นฐานของทั้งระบบ
   ระบบจัดการโรงแรม/หอพัก/อพาร์ทเม้นท์/รีสอร์ท
   สไตล์: "ขาวสะอาด" (Clean White) — พื้นขาว/เทาอ่อน

   ฟอนต์ทั้งหมดโหลดจากเครื่อง (local, ดูไฟล์ fonts.css) ไม่พึ่ง Google Fonts CDN
   ผู้ใช้เลือกฟอนต์ตัวอักษร/ตัวเลข และขนาดฟอนต์ได้เองที่หน้า "ตั้งค่า"
   ค่าที่เลือกจะถูกเซ็ตเป็น inline style ที่ <html> โดย js/font-prefs.js
   ตัวแปร --font-sans-user / --font-mono-user / --font-scale / --menu-font-scale ด้านล่าง
   คือค่า "ตั้งต้น" ก่อนที่ font-prefs.js จะโหลดค่าจริงของ user มาทับ
   (--font-scale ปรับขนาดตัวอักษรทั้งระบบ ส่วน --menu-font-scale ปรับแยกเฉพาะเมนู sidebar
   ด้านซ้าย เผื่อผู้ใช้อยากได้เมนูใหญ่/เล็กกว่าเนื้อหาหลัก — ปรับได้อิสระจากกัน ดูหน้า "ตั้งค่า → ธีม/สี/ฟอนต์")
   =================================================================== */

@import url('fonts.css');

:root {
  /* พื้นหลัง — ขาวสะอาด */
  --bg: #FFFFFF;
  --bg-soft: #FFFFFF;
  --panel: #FFFFFF;
  --panel-hover: #F0F3F4;
  --line: #E3E7E9;
  --line-strong: #CBD2D6;

  /* ปฏิทินการจอง (Gantt) — แยก token อิสระจาก --bg-soft โดยตั้งใจ
     เดิม header แถบหัวคอลัมน์วันที่ และลายทางทึบ "ปิดปรับปรุง" ผูกสีเดียวกับ --bg-soft
     ทำให้ปรับ "พื้นหลังรอง" ในหน้าตั้งค่าแล้วสีทั้งสองที่นี้ลากตามไปด้วยโดยไม่ตั้งใจ */
  --cal-head-bg: #FFFFFF;
  --cal-maint-stripe: #FFFFFF;

  /* แยก token อิสระจาก --panel โดยตั้งใจ — เดิม --panel ตัวเดียวคุมพื้นหลังของ
     ปุ่มทั่วไป, การ์ดสรุป, แถวห้องในปฏิทิน, sidebar พร้อมกันทั้งหมด
     ทำให้ปรับสีจุดหนึ่งแล้วอีก 3 จุดเปลี่ยนตามไปด้วยโดยไม่ตั้งใจ ตอนนี้แยกเป็นคนละตัวแปร
     ปรับแยกอิสระได้ทุกจุด ไม่กระทบกัน (ค่าเริ่มต้นยังเป็นขาวเหมือน --panel เดิมทุกตัว) */
  --btn-bg: #FFFFFF;
  --card-bg: #FFFFFF;
  --row-room-bg: #FFFFFF;
  --sidebar-bg: #1C2326;

  /* ตัวอักษรในการ์ดสรุปแบบ .card (เช่น "ห้องปิดปรับปรุง", "รับชำระแล้ว", "ค้างชำระ")
     แยก token อิสระจาก --text/--text-dim ที่ใช้ทั่วทั้งแอปโดยตั้งใจ เดิมตัวเลข/หัวข้อปกติของ
     การ์ดนี้ใช้ --text/--text-dim ตรงๆ ปรับตัวอักษรหลักของทั้งแอปแล้วการ์ดนี้ลากตามไปด้วย
     (ตัวเลขเตือน/อันตราย เช่น "3" สีส้ม, "32,200.00" สีแดง มี --stat-warn/--stat-danger แยกอยู่แล้ว) */
  --card-label: #5C6A70;
  --card-num: #1C2326;

  /* ปุ่ม (.btn) ทั้ง 5 แบบ — แยก token อิสระของตัวเองทุกจุด ไม่ใช้ร่วมกับสีหลัก/สถานะ/ตัวอักษร
     ที่ใช้ในที่อื่นของแอป (--accent, --green, --red, --text, --bg, --panel-hover, --line-strong)
     เดิมปุ่มพึ่งสีเหล่านั้นตรงๆ ปรับสีหลักหรือสีสถานะที่อื่นแล้วปุ่มลากตามไปด้วยโดยไม่ตั้งใจ
     ตอนนี้แยกพื้น/ตัวอักษร/พื้นตอนชี้เมาส์ ของปุ่มแต่ละแบบออกจากกันครบทุกจุด */

  /* ปุ่ม "ยกเลิก" — ปุ่มทั่วไปไม่มีสีเฉพาะ */
  --btn-text: #1C2326;
  --btn-hover-bg: #F0F3F4;
  --btn-hover-border: #CBD2D6;

  /* ปุ่ม "บันทึก" (หลัก) */
  --btn-primary-bg: #146C68;
  --btn-primary-text: #FFFFFF;
  --btn-primary-hover-bg: #0E4F4C;

  /* ปุ่ม "เช็คอิน" (สำเร็จ) */
  --btn-success-bg: #2E9461;
  --btn-success-text: #FFFFFF;
  --btn-success-hover-bg: #237A4F;

  /* ปุ่ม "ยกเลิกสัญญา" (outline อันตราย) — แยกพื้นหลังของตัวเองออกจาก --btn-bg แล้ว
     ค่าเริ่มต้นเป็นขาวทึบ (#FFFFFF) ไม่ใช่ transparent เพื่อให้ปรับเป็นสีอื่นได้ในหน้าตั้งค่า
     (หน้าตาเริ่มต้นยังเหมือนเดิมเพราะพื้นหน้าเว็บก็ขาวอยู่แล้ว) */
  --btn-danger-bg: #FFFFFF;
  --btn-danger-text: #C4452F;
  --btn-danger-hover-bg: #F8E9E6;

  /* ปุ่ม "ลบ" (อันตราย ตัวเต็ม) */
  --btn-danger-solid-bg: #C4452F;
  --btn-danger-solid-text: #FFFFFF;
  --btn-danger-solid-hover-bg: #A8392A;

  /* สี action หลักของแบรนด์ — ใช้กับปุ่ม, ลิงก์, แท็บ/เมนูที่เลือกอยู่, focus ring */
  --accent: #146C68;
  --accent-deep: #0E4F4C;
  --accent-dim: rgba(20, 108, 104, 0.10);

  /* สีสถานะ (ห้อง, บิล, ป้ายต่างๆ) — แยกจากสีแบรนด์ */
  --green: #2E9461;
  --green-dim: rgba(46, 148, 97, 0.12);
  --amber: #B8862F;
  --amber-dim: rgba(184, 134, 47, 0.14);
  --red: #C4452F;
  --red-dim: rgba(196, 69, 47, 0.12);

  /* ตัวอักษรบนแถบจอง/เข้าพักในปฏิทิน (Gantt) — เดิม hardcode เป็นขาวตายตัว แยก token ให้ปรับได้
     เพราะพื้นแถบ (--blue / --amber) ปรับสีเองได้ แต่ตัวอักษรปรับไม่ได้ ถ้าพื้นสว่างขึ้นตัวอักษรขาวจะอ่านไม่ออก */
  --cal-bar-checkedin-text: #FFFFFF;
  --cal-bar-reserved-text: #FFFFFF;
  --blue: #3D6E96;
  --blue-dim: rgba(61, 110, 150, 0.12);

  /* ป้าย (.badge) — แบรนด์/ชำระแล้ว/รอดำเนินการ/ค้างชำระ/ข้อมูล/ทั่วไป
     แยก token อิสระจากสีสถานะหลักของแอป (--accent, --green, --amber, --red, --blue, --bg-soft)
     โดยตั้งใจ เดิมป้ายพวกนี้ใช้สีร่วมกับสถานะห้อง/ปฏิทิน/ปุ่มหลักทั้งแอป ปรับสีจุดหนึ่งจะลากตามกันหมด
     ตอนนี้แยกพื้นกับตัวอักษรของป้ายแต่ละสีออกจากกันด้วย ปรับได้ทุกจุดอิสระไม่กระทบที่อื่น
     ค่าเริ่มต้นยังเหมือนเดิมทุกตัว (อ้างอิงจากสีสถานะ ณ ตอนตั้งค่าเริ่มต้น) */
  --badge-accent-bg: #E8F0F0;
  --badge-accent-text: #146C68;
  --badge-green-bg: #E6F2EC;
  --badge-green-text: #2E9461;
  --badge-amber-bg: #F5EEE2;
  --badge-amber-text: #B8862F;
  --badge-red-bg: #F8E9E6;
  --badge-red-text: #C4452F;
  --badge-blue-bg: #E8EEF2;
  --badge-blue-text: #3D6E96;
  --badge-neutral-bg: #FFFFFF;
  --badge-neutral-text: #5C6A70;

  /* ป้าย "ประเภทบิล" ในหน้าบิล/ใบเสร็จ (การจอง/เช็คอิน/มัดจำ/สัญญา) — แยก token อิสระจาก --badge-* ชุดบนโดยตั้งใจ
     เดิม renderRow()/openInvoiceDetailModal() ใน billing.js ใช้ class badge-blue/green/amber/accent ซ้ำกับป้ายชุดบน
     ทำให้ปรับสีป้ายชุดบนแล้วป้ายประเภทบิลลากตามไปด้วยโดยไม่ตั้งใจ (และ "การจอง"/"เช็คอิน" เคยใช้สีเดียวกันจน
     แยกไม่ออกในหน้ารายละเอียดบิล) ตอนนี้แยกครบทั้ง 4 ป้าย คนละ token */
  --invoice-booking-bg: #E8EEF2;
  --invoice-booking-text: #3D6E96;
  --invoice-checkin-bg: #E6F2EC;
  --invoice-checkin-text: #2E9461;
  --invoice-deposit-bg: #F5EEE2;
  --invoice-deposit-text: #B8862F;
  --invoice-contract-bg: #E8F0F0;
  --invoice-contract-text: #146C68;

  /* ป้าย "ประเภท" ในหน้าประวัติกิจกรรม (เช็คอิน/เช็คเอาท์/จอง/ยกเลิก/บิล/รับชำระ/ใบเสร็จ/สัญญา/อื่นๆ)
     แยก token อิสระจาก --badge-* ชุดบน (แบรนด์/ชำระแล้ว/รอดำเนินการ/ค้างชำระ/ข้อมูล/ทั่วไป) โดยตั้งใจ
     เดิม actionBadge() ใน activity-log.js ใช้ class badge-blue/green/accent/red/neutral ซ้ำกับป้ายชุดบน
     ทำให้ปรับสีป้ายชุดบนแล้ว "เช็คอิน" (เคยใช้ badge-blue) หรือ "เช็คเอาท์/รับชำระ" (เคยใช้ badge-green)
     หรือ "จอง/สัญญา" (เคยใช้ badge-accent) ลากตามไปด้วยโดยไม่ตั้งใจ ตอนนี้แยกครบทั้ง 9 ป้าย คนละ token */
  --activity-checkin-bg: #E8EEF2;
  --activity-checkin-text: #3D6E96;
  --activity-checkout-bg: #E6F2EC;
  --activity-checkout-text: #2E9461;
  --activity-reserve-bg: #E8F0F0;
  --activity-reserve-text: #146C68;
  --activity-cancel-bg: #F8E9E6;
  --activity-cancel-text: #C4452F;
  --activity-invoice-bg: #FFFFFF;
  --activity-invoice-text: #5C6A70;
  --activity-payment-bg: #E6F2EC;
  --activity-payment-text: #2E9461;
  --activity-receipt-bg: #FFFFFF;
  --activity-receipt-text: #5C6A70;
  --activity-contract-bg: #E8F0F0;
  --activity-contract-text: #146C68;
  --activity-move-bg: #EFEAF6;
  --activity-move-text: #6C4FB6;
  --activity-other-bg: #FFFFFF;
  --activity-other-text: #5C6A70;
  --activity-system-bg: #F1EFEA;
  --activity-system-text: #6B6459;

  /* ป้ายสถานะในหน้า "สัญญาเช่า" (มีผลบังคับ/สิ้นสุดแล้ว/ยกเลิก) — แยก token อิสระจาก --badge-* ชุดบน
     เดิม statusMeta ใน contracts.js ใช้ class badge-green/badge-neutral/badge-red ซ้ำกับป้ายชุดบน
     ทำให้ปรับสีป้ายชุดบนแล้วป้ายสถานะสัญญาเช่าลากตามไปด้วยโดยไม่ตั้งใจ ตอนนี้แยกออกมาคนละ token */
  --contract-active-bg: #E6F2EC;
  --contract-active-text: #2E9461;
  --contract-ended-bg: #FFFFFF;
  --contract-ended-text: #5C6A70;
  --contract-cancelled-bg: #F8E9E6;
  --contract-cancelled-text: #C4452F;

  /* ป้ายในหน้า "จัดการสาขา/ผู้ใช้" — แยก token อิสระจาก --badge-* ชุดบนโดยตั้งใจ
     เดิม admin.js ใช้ class badge-green/neutral/red/accent ซ้ำกับป้ายชุดบน ปรับสีชุดบนแล้ว
     ป้ายสถานะสาขา/role ผู้ใช้/สถานะผู้ใช้ ลากตามไปด้วยโดยไม่ตั้งใจ ตอนนี้แยกออกมาคนละ token */
  --admin-branch-active-bg: #E6F2EC;
  --admin-branch-active-text: #2E9461;
  --admin-branch-inactive-bg: #FFFFFF;
  --admin-branch-inactive-text: #5C6A70;
  --admin-role-superadmin-bg: #E8F0F0;
  --admin-role-superadmin-text: #146C68;
  --admin-role-normal-bg: #FFFFFF;
  --admin-role-normal-text: #5C6A70;
  --admin-user-active-bg: #E6F2EC;
  --admin-user-active-text: #2E9461;
  --admin-user-suspended-bg: #F8E9E6;
  --admin-user-suspended-text: #C4452F;
  --admin-access-role-bg: #E8F0F0;
  --admin-access-role-text: #146C68;

  /* ตัวเลือกแบบ segmented (.segmented-toggle/.segmented-option) — เช่น เลือกรูปแบบพิมพ์ใบเสร็จ
     (A4/สลิปความร้อน/ไม่ปริ้น), เลือกชั้น/ยูนิตของตึก, เลือกแหล่งที่มาบิล — แยก token อิสระจาก
     --accent/--panel/--bg-soft/--line ที่ใช้ทั่วแอปโดยตั้งใจ เดิมตัวเลือกที่ "กำลังเลือกอยู่"
     ใช้สีเดียวกับแท็บที่เลือก/ปุ่มบันทึกทั้งแอป ปรับสีหลักที่ไหนก็ลากตัวเลือกนี้ตามไปด้วย */
  --segmented-bg: #FFFFFF;
  --segmented-text: #5C6A70;
  --segmented-hover-bg: #E3E7E9;
  --segmented-hover-text: #1C2326;
  --segmented-active-bg: #FFFFFF;
  --segmented-active-text: #146C68;

  /* ตัวเลือก "พิมพ์เอกสารหลังสร้างสัญญา" (ไม่พิมพ์/สัญญาเช่า/ใบเสร็จ A4/ใบเสร็จย่อ) ในหน้าสร้างสัญญาเช่า
     เดิมเขียนด้วย inline style ล้วนๆ อ้างถึง var(--primary) และ var(--primary-bg) ซึ่งไม่มีอยู่จริง
     ในระบบเลย (ไม่เคยถูกประกาศที่ไหน) ทำให้ตัวอักษร/ไอคอนของตัวเลือกที่ "กำลังเลือกอยู่" ไม่มีสี
     จริงจัง ส่วนพื้นหลังที่ดูเป็นสีฟ้าคือค่า fallback ของ CSS (#f0f7ff) ที่ฮาร์ดโค้ดไว้ ไม่ใช่ token
     ที่ปรับได้ ตอนนี้สร้าง token จริงแทนทั้งหมด แยกอิสระจาก --accent/--segmented-* ของ component อื่น */
  --print-option-bg: #FFFFFF;
  --print-option-icon: #5C6A70;
  --print-option-text: #1C2326;
  --print-option-active-bg: #F0F7FF;
  --print-option-active-icon: #146C68;
  --print-option-active-text: #146C68;

  /* ชุดสี "เบนโตะ" (Bento) — พาสเทลสดใส ใช้กับ sidebar nav, stat card, ผังห้องพัก
     แยกจากสีสถานะด้านบนโดยตั้งใจ เพื่อไม่กระทบ badge/toast/ปฏิทินที่ใช้สีสถานะเดิมอยู่ */
  /* ม่วง — ใช้กับ "เมนูที่เลือก" (sidebar nav) เท่านั้น */
  --bento-purple: #534AB7;
  --bento-purple-deep: #26215C;
  /* ม่วง — ใช้กับการ์ดสถิติ "กำลังเข้าพัก" (.stat-purple) เท่านั้น แยกอิสระจากเมนูที่เลือกด้านบน
     ตั้งใจแยกเป็นชุด token ของตัวเอง ไม่ใช้ร่วมกับ --bento-purple/-deep อีกต่อไป
     เพราะเป็น 2 component คนละความหมาย (เมนู sidebar vs การ์ดสรุปห้องพัก) ที่เคยผูกสีเดียวกันโดยไม่ตั้งใจ */
  --bento-purple-stat: #534AB7;
  --bento-purple-stat-soft: #CECBF6;
  --bento-mint: #9FE1CB;
  --bento-mint-text: #04342C;
  --bento-mint-sub: #085041;
  --bento-coral: #F0997B;
  --bento-coral-text: #4A1B0C;
  --bento-coral-sub: #712B13;
  --bento-pink: #D4537E;
  --bento-pink-occupied-text: #FFFFFF;
  --bento-pink-occupied-sub: #FFFFFF;
  --bento-pink-soft: #F4C0D1;
  --bento-pink-text: #4B1528;
  --bento-pink-sub: #72243E;

  /* แถวข้อมูลเพิ่มเติมในผังห้อง (.room-tile-guest) — แสดงชื่อลูกค้า (ห้องเข้าพัก/พิงค์)
     หรือวันที่จองล่วงหน้า (ห้องว่างแต่มีจองรอไว้/มินท์) เดิมไม่มีสีของตัวเอง รับสีตาม
     -text ของห้องนั้นๆ มาตรงๆ (ผูกกับเลขห้องและป้ายสถานะ) ตอนนี้แยก token อิสระให้ปรับเองได้ */
  --room-tile-guest-mint: #04342C;
  --room-tile-guest-pink: #FFFFFF;
  --bento-amber: #FAC775;
  --bento-amber-text: #412402;
  --bento-amber-sub: #633806;
  --bento-slate: #6E8898;
  --bento-slate-text: #13202A;
  --bento-slate-sub: #2C4450;
  --bento-tangerine: #F0997B;
  --bento-tangerine-text: #4A1B0C;
  --bento-tangerine-sub: #712B13;
  /* เทาเขียวกลาง — ใช้กับการ์ดสรุปทั่วไปที่ "ไม่ใช่สถานะห้อง" (เช่น "ห้องทั้งหมด", "เช็คอินวันนี้ (ตามจอง)")
     แยกอิสระจาก --bento-mint โดยตั้งใจ เพราะ --bento-mint มีความหมายเฉพาะคือสถานะ "ห้องว่าง"
     (ต้องสีตรงกับผังห้อง .room-tile.st-available) ถ้าการ์ดสรุปทั่วไปไปใช้สีเดียวกัน
     การปรับสี "ห้องว่าง" ในหน้าตั้งค่าจะกระทบการ์ดที่ไม่เกี่ยวกับสถานะห้องไปด้วยโดยไม่ตั้งใจ */
  --bento-neutral: #8A9499;
  --bento-neutral-text: #1C2326;
  --bento-neutral-sub: #4A5458;

  /* สีตัวเลขเตือน/อันตรายของการ์ดสรุปแบบ .stat-num (เช่น ยอดค้างชำระ, จำนวนห้องปิดปรับปรุง)
     ตั้งใจแยกเป็น token อิสระของตัวเอง ไม่ใช้ร่วมกับ --bento-coral-text / --bento-pink-text
     เพราะ token เหล่านั้นผูกกับสีพื้นหลังของการ์ดเบนโตะคนละใบ (ปิดปรับปรุง / กำลังเข้าพัก)
     ถ้าใช้ร่วมกัน การปรับสีเบนโตะใบหนึ่งจะไปเปลี่ยนสีตัวเลขเตือนในการ์ดอื่นที่ไม่เกี่ยวข้องไปด้วย */
  --stat-warn: #B8460B;
  --stat-danger: #C4452F;

  /* ตัวอักษร */
  --text: #1C2326;
  --text-dim: #5C6A70;
  --text-faint: #8E9BA1;

  /* ฟอนต์ที่ user เลือกได้ (ค่าตั้งต้น — font-prefs.js จะ override ที่ <html> ตามที่ผู้ใช้ตั้งไว้) */
  --font-sans-user: 'Sarabun';
  --font-mono-user: 'IBM Plex Mono';
  --font-scale: 1;
  --menu-font-scale: 1;

  /* ฟอนต์จริงที่ใช้ทั่วระบบ — อ้างอิงตัวแปร *-user เสมอ มี fallback ปลอดภัยต่อท้าย */
  --font-sans: var(--font-sans-user), 'Sarabun', sans-serif;
  --font-serif: var(--font-sans-user), 'Sarabun', sans-serif;
  --font-mono: var(--font-mono-user), 'IBM Plex Mono', var(--font-sans-user), monospace;

  /* ระยะ/ขนาด */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 14px;
  --sidebar-w: 248px;
  --header-h: 64px;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  /* ปิดกรอบ/แถบสีฟ้าที่เบราว์เซอร์มือถือ (Chrome/Brave บน Android เป็นต้น) ขึ้นให้เองตอนแตะ element ที่แตะได้
     (input, button, div ที่มี click handler ฯลฯ) — ทั้งแอปตั้งใจไม่ใช้ effect นี้ เพราะมีสไตล์ :active/hover ของ
     ปุ่มเองอยู่แล้วในทุกที่ (เช่น .btn, .numpad-grid button) ทำให้กดแล้วเห็นเป็นกรอบสีฟ้าแทรกซ้อนดูไม่สวย */
  -webkit-tap-highlight-color: transparent;
}

html {
  -webkit-tap-highlight-color: transparent;
}

html, body {
  height: 100%;
  /* กันชนสุดท้ายไม่ให้หน้าจอเลื่อนแนวนอนได้ (กรณีมีองค์ประกอบใดกว้างเกิน viewport บนมือถือ) */
  overflow-x: hidden;
}

/* ปรับขนาดทั้งระบบ (ตัวอักษร, ปุ่ม, ระยะห่าง) ตามค่าที่ผู้ใช้ตั้งไว้ในหน้า "ตั้งค่า"
   ครอบทั้ง #app-root (แอปหลังบ้าน) และ .auth-screen (หน้า login ก่อนเข้าระบบ)
   ใช้ zoom เพราะ scale ทุกอย่างพร้อมกันโดยไม่ต้องแก้ unit เดิมในไฟล์ css อื่นเลย */
#app-root,
.auth-screen {
  zoom: var(--font-scale);
}

body {
  font-family: var(--font-sans);
  background: var(--bg);
  color: var(--text);
  font-size: 15px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-weight: 600;
  line-height: 1.35;
}

a {
  color: var(--accent);
  text-decoration: none;
}

button, input, select, textarea {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
}

button {
  cursor: pointer;
}

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

/* scrollbar เข้ากับธีมกระดาษ */
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-track {
  background: transparent;
}
::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--text-faint);
}

/* utility คลาสที่ใช้บ่อย */
.mono {
  font-family: var(--font-mono);
}

.serif {
  font-family: var(--font-serif);
}

.text-dim {
  color: var(--text-dim);
}

.text-faint {
  color: var(--text-faint);
}

.hidden {
  display: none !important;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

/* โฟกัสที่มองเห็นได้ชัดเจน (accessibility) */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ซ่อนลูกศร number input ทุกที่ */
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;
}

/* spin animation for loading states */
@keyframes spin { to { transform: rotate(360deg); } }
.spin { display: inline-block; animation: spin .7s linear infinite; }

