/* ==========================================================================
   Tech2Income — stylesheet หลัก
   เขียนแบบ mobile first: กฎพื้นฐานคือจอเล็ก แล้วค่อยเพิ่มด้วย @media (min-width)
   ห้ามใช้ !important — ถ้าสไตล์ไม่ติดให้เพิ่มความจำเพาะของ selector แทน
   ลำดับหัวข้อ: 1 ตัวแปร 2 พื้นฐาน 3 เลย์เอาต์ 4 ส่วนหัว 5 ปุ่ม 6 แบนเนอร์
                7 การ์ด 8 แถบพาราแลกซ์ 9 ตะกร้า/หน้าต่างซ้อน 10 ฟอร์ม
                11 ส่วนท้าย 12 หน้ารายละเอียด 13 หน้าชำระเงินกับชั้นหนังสือ
                14 การเคลื่อนไหว 15 จอใหญ่
   ========================================================================== */

/* 1 ── ตัวแปร ─────────────────────────────────────────────────────────── */
:root {
  /* สี — พื้นขาวสะอาด โครงกรมท่า ฟ้าคือทางเดิน ชมพูแดงคือการซื้อ
     ตามหลัก 60-30-10 ใน DESIGN.md ไม่มีสีอุ่นในระบบนี้เลย */
  --outer: #f1f5f9;
  --ink: #0f172a;
  /* ปุ่มลำดับสามเป็นกรมท่า ไม่ใช่ฟ้าคราม เพราะพาเลตต์นี้เป็นฟ้าทั้งชุดอยู่แล้ว
     สีเย็นจึงไม่ใช่ตัวแยกอีกต่อไป ตัวแยกคือ "ไม่มีสี" ขาวบนพื้นนี้ได้ 17.85:1 */
  --file: #0f172a;
  --file-dark: #1e293b;
  --body: #475569;
  /* เข้มกว่า Slate 500 มาตรฐานเล็กน้อย เพราะ #64748b ตกเกณฑ์บนพื้น --surface-2
     (4.34:1) ค่านี้ผ่านทุกพื้นที่มันถูกใช้ 5.51 / 5.26 / 5.03 */
  --muted: #5c6a7d;
  --soft: #94a3b8;
  --canvas: #ffffff;
  --surface: #f8fafc;
  --surface-2: #f1f5f9;
  --card: #ffffff;
  --card-soft: #f8fafc;
  --line: #e2e8f0;
  --line-soft: #f1f5f9;
  --deep: #0b1326;
  --deep-2: #1e293b;
  --deep-body: #94a3b8;
  /* ฟ้าสำหรับพื้นเข้มโดยเฉพาะ — --brand (#0369a1) บนพื้น footer ได้แค่ 3.12:1
     และ --brand-accent ได้ 4.52:1 ซึ่งยังจม เฉดนี้ได้ 8.63:1 */
  --deep-accent: #38bdf8;
  /* พื้นปุ่มหลักเป็น Sky 700 ไม่ใช่ Sky 600 เพราะ #0284c7 กับตัวอักษรขาว
     ได้แค่ 4.10:1 ตกเกณฑ์ 4.5:1 ค่านี้ได้ 5.93:1 */
  --brand: #0369a1;
  --brand-dark: #075985;
  /* Sky 600 ยังอยู่ในระบบ แต่ใช้กับของที่ไม่ใช่ตัวอักษรเล็กเท่านั้น —
     ขีดใต้เมนู วงแหวนโฟกัส จุดบอกสไลด์ (เกณฑ์ UI 3:1 ค่าจริง 4.10:1) */
  --brand-accent: #0284c7;
  --brand-soft: #e0f2fe;
  --income: #0369a1;
  --income-soft: #e0f2fe;
  /* พื้นปุ่มซื้อเป็น Rose 600 ไม่ใช่ Rose 500 เพราะ #f43f5e กับตัวอักษรขาว
     ได้แค่ 3.67:1 ตกเกณฑ์ ค่านี้ได้ 4.70:1 */
  --offer: #e11d48;
  /* hover ของปุ่มซื้อ และตัวอักษรชมพูแดงที่ต้องวางบนพื้นที่ไม่ใช่ขาว
     ผ่าน 4.5:1 ครบทุกพื้น 6.29 / 6.01 / 5.74 / 5.48
     ราคาไม่ได้ใช้ค่านี้ เพราะราคาอยู่บนการ์ดขาวเสมอ ดูคอมเมนต์ที่ .price */
  --offer-dark: #be123c;
  /* Rose 500 เหลือไว้ให้ของที่ไม่ใช่ตัวอักษร เช่นลายจุดบนแถบพาราแลกซ์ */
  --offer-accent: #f43f5e;
  --offer-soft: #ffe4e6;
  /* ชมพูจางสำหรับไอคอนในกล่องยืนยัน อ่านจาก js/app.js ตรง ๆ ไม่ได้
     จึงเขียนซ้ำเป็นค่าดิบที่นั่น ตรงนี้คือทะเบียนกลางว่าสีนี้มีอยู่จริง */
  --offer-icon: #fb7185;
  /* แดงอิฐ เหลือไว้ให้หมายเหตุข้อจำกัดกับปุ่มยกเลิกในตารางประวัติ
     ข้อความผิดพลาดในฟอร์มย้ายไปใช้ --offer แล้ว ดูคอมเมนต์ที่ .field-error */
  --danger: #b91c1c;
  --danger-dark: #991b1b;
  --danger-soft: #fef2f2;
  /* แดงจางสำหรับไอคอนในกล่องแจ้งเตือน อ่านค่าจาก js/app.js ไม่ได้ตรง ๆ
     จึงเขียนซ้ำเป็นค่าดิบที่นั่น ตรงนี้คือทะเบียนกลางว่าสีนี้มีอยู่จริง */
  --danger-icon: #f87171;

  /* ป้ายสถานะคำสั่งซื้อ — พื้นย้อมอ่อนคู่กับตัวอักษรเข้มในวงศ์สีเดียวกัน
     สถานะจึงพูดด้วยสีของตัวเอง ไม่ใช่ข้อความสีเนื้อความวางบนพื้นสี
     ทุกคู่คำนวณตามสูตร WCAG แล้ว รอตรวจสอบ 6.37:1 · ชำระแล้ว 6.59:1
     · สลิปไม่ผ่าน 6.80:1 · ยกเลิกแล้ว 6.92:1 และใช้สี่วงศ์สีที่ห่างกันบนวงล้อ
     คนตาบอดสีจึงแยกออกโดยไม่ต้องพึ่งเนื้อสีอย่างเดียว
     "ชำระแล้ว" เป็นฟ้าไม่ใช่เขียว เพราะพาเลตต์นี้ไม่มีเขียวอยู่เลย
     ใส่เขียวเข้ามาเพื่อป้ายเดียวจะเพิ่มวงศ์สีที่ห้าที่ไม่มีที่ใช้ที่อื่น
     จุดนำหน้าเป็นองค์ประกอบ UI ใช้เกณฑ์ 3:1 ค่าจริง 3.57–5.03:1 */
  --status-pending: #fef3c7;
  --status-pending-ink: #92400e;
  --status-pending-dot: #b45309;
  --status-paid: #e0f2fe;
  --status-paid-ink: #075985;
  --status-paid-dot: #0284c7;
  --status-rejected: #fee2e2;
  --status-rejected-ink: #991b1b;
  --status-rejected-dot: #dc2626;
  --status-cancelled: #f1f5f9;
  --status-cancelled-ink: #475569;
  --status-cancelled-dot: #5c6a7d;

  /* ตัวอักษร */
  --font-head: "Prompt", system-ui, sans-serif;
  --font-body: "Sarabun", system-ui, sans-serif;
  --fs-h1: 1.5rem;
  --fs-h2: 1.5rem;
  --fs-h3: 1rem;
  --fs-body: 0.9375rem;
  --fs-small: 0.875rem;
  --fs-caption: 0.75rem;
  --fs-micro: 0.6875rem;
  --lh-body: 1.75;
  --lh-head: 1.35;

  /* มุมโค้ง — การ์ดโค้งใหญ่ ปุ่มเป็นแคปซูล */
  --r-sm: 0.5rem;
  --r-md: 0.75rem;
  --r-card: 1.25rem;
  --r-full: 9999px;

  /* ระยะ */
  --section-y: 1.75rem;
  --card-p: 1.25rem;
  --page-max: 72rem;
  --gutter: 1rem;

  /* เงา — กรมท่าโปร่ง ห้ามใช้สีดำ ดำบนพื้นเทาอมฟ้าจะดูเป็นคราบ */
  --shadow-card: 0 4px 12px rgb(15 23 42 / 0.05);
  --shadow-lift: 0 12px 24px -8px rgb(15 23 42 / 0.12);
  --shadow-panel: 0 24px 60px -20px rgb(11 19 38 / 0.35);

  /* ระยะเลื่อนของพาราแลกซ์ */
  --t2i-shift: 24px;
}

/* 2 ── พื้นฐาน ────────────────────────────────────────────────────────── */
*,
*::before,
*::after { box-sizing: border-box; }

/* overflow-x: clip กันหน้าไถลแนวนอนตอนแผงตะกร้ากำลังเลื่อนเข้ามาจากขอบขวา
   ระหว่างที่แผงยังอยู่นอกจอ มันสร้างพื้นที่เลื่อนแนวนอนขึ้นมาชั่วขณะหนึ่ง
   เบราว์เซอร์มือถือไถลตามแล้วค้างอยู่ตรงนั้น แผงจึงดูเหมือนออกมาไม่สุด
   ใช้ clip ไม่ใช่ hidden เพราะ hidden จะลากให้แกนตั้งกลายเป็น auto ไปด้วย */
html {
  overflow-x: clip;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

/* ส่วนท้ายชิดขอบล่างเสมอ แม้เนื้อหาจะสั้นกว่าความสูงจอ
   body เป็นคอลัมน์สูงอย่างน้อยเต็มจอ แล้ว main กินที่ว่างที่เหลือทั้งหมด
   พื้นขาวของ main จึงยืดลงไปจนชนส่วนท้าย ไม่เหลือแถบพื้นนอกกรอบคั่นกลาง
   ใช้ 100dvh ไม่ใช่ 100vh เพราะแถบที่อยู่เว็บของเบราว์เซอร์มือถือหดขยายได้
   ในกระแสมีแค่สามชิ้น — ส่วนหัว main ส่วนท้าย ที่เหลือ (ลิงก์ข้ามเนื้อหา
   ชุดไอคอน ฉากทึบ แผงตะกร้า หน้าต่างซ้อน) เป็น fixed หรือ absolute
   จึงไม่กลายเป็นช่องของ flex */
body {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  margin: 0;
  background-color: var(--outer);
  color: var(--body);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
}

body.is-locked { overflow: hidden; }

h1, h2, h3, h4, h5, h6 {
  margin: 0;
  color: var(--ink);
  font-family: var(--font-head);
  font-weight: 500;
  line-height: var(--lh-head);
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }

p { margin: 0; }

a { color: var(--brand); text-decoration: none; }
a:hover { color: var(--brand-dark); }

img { display: block; max-width: 100%; height: auto; }

ul, ol { margin: 0; padding: 0; list-style: none; }

button { font: inherit; color: inherit; }

:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

[x-cloak] { display: none; }

.skip-link {
  position: absolute;
  left: 0.5rem;
  top: -100%;
  z-index: 100;
  border-radius: var(--r-md);
  background-color: var(--ink);
  padding: 0.75rem 1rem;
  color: #fff;
  font-family: var(--font-head);
  font-size: var(--fs-small);
}
.skip-link:focus { top: 0.5rem; }
/* กฎ a:hover ตัวกลางจะเปลี่ยนสีตัวอักษรเป็นฟ้าเข้มบนพื้นหมึก จนอ่านไม่ออก */
.skip-link:hover,
.skip-link:focus { color: #fff; }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* 3 ── เลย์เอาต์ ──────────────────────────────────────────────────────── */
.page {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.site-header,
main,
.site-footer {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
}

main {
  /* 1 0 auto ไม่ใช่ flex: 1 เพราะ flex: 1 ตั้ง flex-basis เป็น 0
     ความสูงของเนื้อหาจะถูกมองข้ามแล้วคำนวณใหม่จากที่ว่าง ซึ่งไม่จำเป็นที่นี่
     ค่านี้คือ "ยืดได้ ห้ามหด ฐานคือความสูงจริงของเนื้อหา" */
  flex: 1 0 auto;
  background-color: var(--canvas);
  /* เงามีทิศทาง แสงมาจากด้านบนเหมือนแผ่นกระดาษวางบนโต๊ะจริง
     ไม่ใช่วงเรืองรอบตัวแบบ offset 0 ซึ่งไม่มีที่มาทางกายภาพ */
  box-shadow: 0 8px 30px -12px rgb(15 23 42 / 0.10);
}

.section { padding-block: var(--section-y); }

/* แถบที่อยู่ใต้แถบเส้นทางโดยตรงและขึ้นต้นด้วยหัวเรื่องของหน้า
   ระยะเต็มของ .section เผื่อไว้สำหรับแถบที่ต่อจากแถบอื่น ไม่ใช่จากแถบเส้นทาง
   ซึ่งเป็นบรรทัดเล็ก ๆ บรรทัดเดียว ช่องว่างเลยดูโหว่เกินจำเป็น
   เป็นค่าคงที่ ไม่โตตาม --section-y ที่จอกว้าง เพราะช่องว่างนี้ไม่ได้แยกเนื้อหาสองก้อน
   แค่ให้หัวเรื่องหายใจต่อจากแถบเส้นทางเท่านั้น */
.section-lead { padding-top: 0.75rem; }
.section-surface { background-color: var(--surface); }
.section-deep { background-color: var(--deep); color: var(--deep-body); }

.section-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 1.75rem;
  text-align: center;
}

.section-head-text { max-width: 34rem; }

.section-head-text h2 em {
  color: var(--brand);
  font-style: normal;
}

/* 4 ── ส่วนหัวของเว็บ ─────────────────────────────────────────────────── */
.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  border-top: 6px solid var(--deep);
  border-bottom: 1px solid var(--line);
  background-color: rgb(255 255 255 / 0.94);
  backdrop-filter: blur(10px);
  box-shadow: 0 6px 20px rgb(15 23 42 / 0.05);
}

.header-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-height: 60px;
}

.logo {
  color: var(--ink);
  font-family: var(--font-head);
  font-size: 1.125rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.logo:hover { color: var(--ink); }
.logo-mark { color: var(--income); }

.site-nav {
  display: none;
  /* เป็นกรอบอ้างอิงให้เส้นบอกตำแหน่งที่วางแบบ absolute */
  position: relative;
}

.site-nav-list {
  display: flex;
  align-items: center;
  gap: 1.5rem;
}

.site-nav-link {
  color: var(--body);
  font-family: var(--font-head);
  font-size: var(--fs-small);
  /* ทุกลิงก์มีขอบล่างใสความหนาเท่ากัน กล่องจึงสูงเท่ากันหมด
     ตอนสลับตัวที่กำลังเปิดอยู่ ข้อความจะได้ไม่ขยับขึ้นลง
     และเส้นที่เลื่อนไปมาจะได้ตกลงตำแหน่งเดียวกันทุกลิงก์ */
  padding-bottom: 0.25rem;
  border-bottom: 2px solid transparent;
}
.site-nav-link:hover { color: var(--ink); }
.site-nav-link[aria-current] {
  color: var(--ink);
  /* ฟ้าคือทางเดิน ตัวบอกตำแหน่งปัจจุบันจึงเป็นฟ้า ไม่ใช่ชมพูแดงซึ่งแปลว่า "ซื้อ"
     ใช้ --brand-accent เพราะเป็นเส้น 2px ไม่ใช่ตัวอักษร เกณฑ์ UI 3:1 ค่าจริง 4.10:1 */
  border-bottom-color: var(--brand-accent);
}

/* เมื่อ js/motion.js ใส่เส้นเลื่อนให้แล้ว ขอบล่างของลิงก์ก็ปิดไป
   เหลือเส้นเดียวที่วิ่งไปมาแทน ถ้า JS ไม่ทำงาน คลาสนี้จะไม่ถูกใส่
   ขอบล่างเดิมก็ยังทำหน้าที่บอกตำแหน่งได้ตามปกติ */
.site-nav-has-marker .site-nav-link[aria-current] { border-bottom-color: transparent; }

.site-nav-marker {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 2px;
  background-color: var(--brand-accent);
}

/* เลื่อนแบบมีจังหวะเฉพาะตอนที่อนิเมชันเปิดอยู่
   ถ้าผู้ใช้ขอลดการเคลื่อนไหว เส้นจะกระโดดไปตำแหน่งใหม่ทันที ซึ่งยังบอกตำแหน่งได้ครบ */
.t2i-motion .site-nav-marker {
  transition: transform 0.35s cubic-bezier(0.22, 0.61, 0.36, 1),
              width 0.35s cubic-bezier(0.22, 0.61, 0.36, 1);
}

.header-tools {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  min-width: 44px;
  min-height: 44px;
  border: 0;
  background-color: transparent;
  color: var(--ink);
  cursor: pointer;
}

/* วงที่มองเห็นเล็กกว่าพื้นที่กด — พื้นที่กดคง 44px ตามกฎสี่สิบสี่พิกเซล
   ส่วนวงกลมที่ขึ้นตอน hover กว้าง 36px จึงดูพอดีกับไอคอน ไม่เทอะทะ */
.icon-button::before {
  content: "";
  position: absolute;
  inset: 4px;
  border-radius: var(--r-full);
  background-color: transparent;
  transition: background-color 0.2s ease;
}
.icon-button:hover::before { background-color: var(--line); }

/* ไอคอนกับป้ายจำนวนต้องอยู่เหนือวงกลม */
.icon-button svg { position: relative; }

.icon-button svg { width: 20px; height: 20px; }

.cart-count {
  position: absolute;
  top: 4px;
  right: 2px;
  min-width: 18px;
  border-radius: var(--r-full);
  background-color: var(--offer);
  padding: 0 5px;
  color: #fff;
  font-family: var(--font-head);
  font-size: var(--fs-micro);
  line-height: 18px;
  text-align: center;
}

.header-tools .header-auth { display: none; }

.mobile-nav {
  border-top: 1px solid var(--line);
  padding-block: 0.75rem 1.25rem;
}

.mobile-nav-list {
  display: grid;
  gap: 0.25rem;
  margin-bottom: 0.75rem;
}

/* ปุ่มบัญชีในเมนูของจอเล็กมีสองปุ่มเรียงลง — แก้ไขข้อมูลสมาชิกกับออกจากระบบ
   ระยะคั่นมาจากกล่องที่ครอบทั้งคู่ ไม่ใช่ margin ของปุ่มเอง เพราะปุ่มเข้าสู่ระบบ
   ของคนที่ยังไม่ล็อกอินยืนเดี่ยวอยู่ตรงนั้น ไม่ควรมีระยะห้อยเพิ่มมาโดยไม่มีอะไรต่อท้าย */
.mobile-nav-account {
  display: grid;
  gap: 0.5rem;
}

.mobile-nav-link {
  display: block;
  border-radius: var(--r-md);
  padding: 0.625rem 0.5rem;
  color: var(--ink);
  font-family: var(--font-head);
  font-size: var(--fs-small);
}
.mobile-nav-link:hover { background-color: var(--brand-soft); color: var(--ink); }

/* ใช้ขีดใต้ฟ้าชุดเดียวกับเมนูจอใหญ่ ไม่ใช้พื้นสี เพราะพื้นสงวนไว้ให้ hover
   เขียนเป็น text-decoration ไม่ใช่ border-bottom เส้นจะได้ยาวเท่าข้อความ
   ไม่ใช่ยาวเต็มความกว้างของแผงจนดูเหมือนเส้นคั่น */
.mobile-nav-link[aria-current] {
  text-decoration: underline;
  text-decoration-color: var(--brand-accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.25rem;
}

/* 5 ── ปุ่ม ───────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 44px;
  border: 1px solid transparent;
  border-radius: var(--r-full);
  background-color: transparent;
  padding: 0.75rem 1.5rem;
  font-family: var(--font-head);
  font-size: var(--fs-small);
  text-align: center;
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.btn-primary {
  background-color: var(--brand);
  color: #fff;
  box-shadow: 0 3px 0 var(--brand-dark);
}
.btn-primary:hover { background-color: var(--brand-dark); color: #fff; }

/* ปุ่มซื้อ — ชมพูแดงคือการซื้อ ตามกฎสามสีสามหน้าที่ใน DESIGN.md
   ใช้ --offer ไม่ใช่ --offer-accent เพราะ #f43f5e กับตัวอักษรขาวได้แค่ 3.67:1
   ค่านี้ได้ 4.70:1 ส่วน hover ได้ 6.29:1
   คุมด้วยกฎชมพูแดงหนึ่งจุดต่อหนึ่งสายตา — การ์ดที่เรียงกันนับเป็นกลุ่มเดียว */
.btn-buy {
  background-color: var(--offer);
  color: #fff;
  box-shadow: 0 3px 0 var(--offer-dark);
}
.btn-buy:hover { background-color: var(--offer-dark); color: #fff; }

.btn-secondary {
  border-color: var(--soft);
  background-color: var(--card);
  color: var(--ink);
}
.btn-secondary:hover { background-color: var(--surface); color: var(--ink); }

/* พื้นเทาอมฟ้ากลาง ตัวอักษรขาว (5.51:1) — ทึบแต่เบากว่าปุ่มหลัก
   ใช้เป็นทางเลือกที่สองที่ยังต้องเห็นชัด เช่นปุ่มซื้อเลยข้างปุ่มใส่ตะกร้า */
.btn-muted {
  background-color: var(--muted);
  box-shadow: 0 3px 0 var(--body);
  color: #fff;
}
.btn-muted:hover { background-color: var(--body); color: #fff; }

/* ปุ่มลำดับสาม พื้นกรมท่า ใช้เฉพาะการกระทำกับไฟล์ เช่นเปิดไฟล์ตัวอย่าง
   หนักที่สุดในเว็บ (ขาวบนพื้น 17.85:1) แต่ไม่มีสี จึงไม่แย่งสายตาจากปุ่มซื้อ */
.btn-file {
  background-color: var(--file);
  box-shadow: 0 2px 0 var(--file-dark);
  color: #fff;
}
.btn-file:hover { background-color: var(--file-dark); color: #fff; }
.btn-file svg { width: 16px; height: 16px; }

.btn-small {
  min-height: 44px;
  padding: 0.5rem 0.875rem;
  font-size: var(--fs-caption);
}
.btn-primary.btn-small { box-shadow: 0 2px 0 var(--brand-dark); }
.btn-buy.btn-small { box-shadow: 0 2px 0 var(--offer-dark); }
.btn-muted.btn-small { box-shadow: 0 2px 0 var(--body); }

.btn-block { width: 100%; }

/* ติ๊กถูกบนปุ่มใส่ตะกร้า ขึ้นเมื่อเล่มนั้นอยู่ในตะกร้าแล้ว
   เส้นวิ่งเข้ามาทีละส่วนแทนการโผล่มาทั้งอัน เพราะการเคลื่อนไหวสั้น ๆ
   ตรงจุดที่นิ้วเพิ่งกด ยืนยันว่าการกดได้ผลโดยไม่ต้องเลื่อนสายตา
   ขึ้นไปดูตัวเลขบนไอคอนตะกร้าที่แถบบนซึ่งอยู่คนละมุมจอ

   stroke-dasharray กับ stroke-dashoffset เป็นคุณสมบัติที่สืบทอดได้ใน SVG
   สั่งจาก <svg> ข้างนอกจึงลงไปถึงเส้นที่อยู่ใน <symbol> ได้ ไม่ต้องแก้ตัวไอคอน
   ค่า 22 มาจากความยาวเส้นของ i-check (ประมาณ 21.2) ปัดขึ้นเผื่อไว้
   น้อยกว่าความยาวจริงเมื่อไหร่ เส้นจะขาดกลางแทนที่จะวิ่งจนสุด */
.btn-check {
  width: 16px;
  height: 16px;
  stroke-dasharray: 22;
  animation: t2i-check-draw 0.45s ease-out;
}

@keyframes t2i-check-draw {
  from {
    stroke-dashoffset: 22;
    transform: scale(0.7);
  }
  60% { transform: scale(1.15); }
  to {
    stroke-dashoffset: 0;
    transform: scale(1);
  }
}

.btn:disabled {
  background-color: var(--line);
  color: var(--soft);
  box-shadow: none;
  cursor: not-allowed;
}

/* ปุ่ม "ซื้อแล้ว" ปิดการกดแต่ไม่จาง เพราะมันไม่ใช่ปุ่มที่กดไม่ได้ชั่วคราว
   แต่เป็นสถานะถาวรที่ต้องอ่านออกชัดว่าเล่มนี้เป็นของเราแล้ว
   สองคลาสจึงชนะกฎ .btn:disabled ที่มีคลาสเดียว โดยไม่ต้องใช้ !important
   เคอร์เซอร์เป็นลูกศรปกติ ไม่ใช่เครื่องหมายห้าม เพราะไม่มีอะไรผิดพลาด */
.btn-muted:disabled {
  background-color: var(--muted);
  box-shadow: 0 3px 0 var(--body);
  color: #fff;
  cursor: default;
}
.btn-muted:disabled:hover { background-color: var(--muted); }

/* นำออกรายชิ้น — เป็นข้อความไม่ใช่ปุ่มทึบ เพราะในตะกร้ามีหลายอันเรียงกัน
   ถ้าทำเป็นปุ่มแดงทึบทุกอัน รายการจะกลายเป็นแถวปุ่มแดงแทนที่จะเป็นรายการสินค้า */
.btn-ghost-danger {
  border: 0;
  border-radius: var(--r-md);
  background-color: transparent;
  padding: 0.375rem 0.5rem;
  /* ใช้ --offer เฉดเดียวกับราคาที่อยู่บรรทัดเดียวกัน ไม่ใช่ --danger ซึ่งเป็นแดงอิฐ
     สองเฉดที่ต่างกันนิดเดียวในสายตาเดียวกันอ่านเหมือนความผิดพลาด ไม่ใช่ความตั้งใจ
     บนพื้นการ์ดขาวของแผงตะกร้าได้ 4.70:1 */
  color: var(--offer);
  font-family: var(--font-head);
  font-size: var(--fs-caption);
  cursor: pointer;
}
/* ตอน hover ต้องเข้มขึ้นเป็น --offer-dark ด้วย เพราะ --offer บนพื้น --offer-soft
   เหลือ 3.91:1 ตกเกณฑ์ ส่วนค่าเข้มได้ 5.24:1 */
.btn-ghost-danger:hover {
  background-color: var(--offer-soft);
  color: var(--offer-dark);
}

/* ล้างตะกร้า — ทึบเพราะลบของทั้งหมดทีเดียวและย้อนกลับไม่ได้ ต้องเห็นชัด
   ใช้เฉดเดียวกับแดงตัวอื่นในแผงตะกร้า (ขาวบนพื้น 4.70:1 hover 6.29:1)
   --danger แดงอิฐยังอยู่ในระบบ แต่เหลือที่ใช้แค่หมายเหตุข้อจำกัดกับปุ่มยกเลิก
   ในตารางประวัติ ซึ่งไม่เคยโผล่มาอยู่ในสายตาเดียวกับราคา */
.btn-danger {
  background-color: var(--offer);
  box-shadow: 0 2px 0 var(--offer-dark);
  color: #fff;
}
.btn-danger:hover { background-color: var(--offer-dark); color: #fff; }
.btn-danger svg { width: 16px; height: 16px; }

/* 6 ── แบนเนอร์หน้าแรก ────────────────────────────────────────────────── */
.hero {
  position: relative;
  overflow: hidden;
  background-color: var(--surface);
  color: var(--body);
}

.hero-stage {
  position: relative;
  display: block;
  min-height: 32rem;
}

/* ไม่มีแบนเนอร์ในฐานข้อมูล หรือต่อฐานข้อมูลไม่ติด ก็ไม่ต้องเว้นที่ว่างไว้ให้ภาพ
   เจาะจงกว่ากฎของ .hero-stage ทุกข้อ จึงทับค่าใน @media ข้างล่างได้ด้วย */
.hero-plain .hero-stage,
.hero-plain .hero-content {
  min-height: 0;
}

.hero-plain .hero-content { padding-block: 2.75rem; }

/* ภาพเป็นพื้นหลังของแบนเนอร์ทุกขนาดจอ ข้อความกับปุ่มจึงลอยอยู่บนภาพเสมอ
   ไม่ใช่แยกเป็นสองบล็อกบนจอเล็กเหมือนเดิม */
.hero-artwork {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  overflow: hidden;
}

/* ทุกขนาดจอใช้ภาพเดียวเป็นพื้นหลัง และวางข้อความด้านซ้ายทับบนแบนเนอร์
   ให้แถบพื้นค่อย ๆ จางไปทางขวา จึงอ่านง่ายโดยไม่แยกข้อความกับภาพเป็นคนละบล็อก

   z-index 2 เพราะ .swiper-wrapper ของ Swiper เป็น z-index 1 อยู่แล้ว
   ถ้าไม่ใส่ ชั้นไล่สีนี้จะไปอยู่ใต้ภาพและไม่ช่วยอะไรเลย */
.hero-artwork::after {
  content: "";
  position: absolute;
  z-index: 2;
  inset: 0;
  background: linear-gradient(90deg,
    rgb(248 250 252 / 0.94) 0%,
    rgb(248 250 252 / 0.86) 48%,
    rgb(248 250 252 / 0.48) 70%,
    transparent 100%);
}

.hero-artwork .swiper-wrapper,
.hero-slide { height: 100%; }

.hero-slide {
  margin: 0;
  overflow: hidden;
}

.hero-artwork img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 68% center;
}

.hero-pagination {
  --swiper-pagination-bottom: 1.125rem;
  position: absolute;
  z-index: 3;
  right: 0;
  bottom: var(--swiper-pagination-bottom);
  left: 0;
  display: flex;
  justify-content: center;
  gap: 0.375rem;
}

.hero-pagination .swiper-pagination-bullet {
  width: 0.625rem;
  height: 0.625rem;
  margin: 0;
  background-color: var(--brand-accent);
  box-shadow: 0 1px 4px rgb(15 23 42 / 0.3);
  opacity: 0.55;
  transition: width 0.25s ease, opacity 0.25s ease;
}

.hero-pagination .swiper-pagination-bullet-active {
  width: 1.75rem;
  border-radius: var(--r-full);
  background-color: var(--brand-accent);
  opacity: 1;
}

.hero-content {
  position: relative;
  z-index: 3;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 32rem;
  padding-block: clamp(2.5rem, 7vw, 4rem);
}

.hero-title {
  margin-top: 0;
  max-width: 100%;
  color: var(--ink);
  /* เริ่มที่ 26px สำหรับจอ 320px แล้วเพิ่มเป็นช่วง โดยยังคงสองบรรทัดตายตัว */
  font-size: 1.625rem;
  font-weight: 600;
  letter-spacing: -0.015em;
}

.hero-title-line {
  display: block;
  white-space: nowrap;
}

/* คำที่เน้นในหัวข้อแบนเนอร์ — ใช้ --brand-accent ได้เพราะขนาดนี้เป็นตัวอักษรใหญ่
   ตามนิยาม WCAG จึงใช้เกณฑ์ 3:1 ค่าจริง 4.10:1 บนพื้นขาว */
.hero-title em {
  color: var(--brand);
  font-style: normal;
}

.hero-text {
  margin-top: 0.875rem;
  max-width: 100%;
  color: var(--body);
  /* เริ่มที่ 10px สำหรับจอ 320px แล้วเพิ่มเป็นช่วง เพื่อรักษาสองบรรทัดตายตัว */
  font-size: 0.625rem;
}

/* สอง span คือสองบรรทัดตายตัว ห้ามเบราว์เซอร์ตัดเพิ่มเป็นบรรทัดที่ 3–4 */
.hero-text-line {
  display: block;
  white-space: nowrap;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.625rem;
  margin-top: 1.5rem;
}

.hero-actions .btn {
  min-height: 44px;
  padding: 0.5rem 1.125rem;
  font-size: 0.8125rem;
}

/* 7 ── การ์ด ──────────────────────────────────────────────────────────── */
.book-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem 0.875rem;
}

.book-empty {
  padding-block: 3rem;
  color: var(--muted);
  text-align: center;
}

.book-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  background-color: var(--card-soft);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.book-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lift);
}

.book-cover {
  position: relative;
  display: block;
  overflow: hidden;
  background-color: var(--surface);
  aspect-ratio: 5 / 7;
}

.book-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}
.book-card:hover .book-cover img { transform: scale(1.05); }

.book-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: 0.25rem;
  border-top: 1px solid var(--line);
  background-color: var(--card-soft);
  padding: 0.875rem;
}

.book-category {
  /* เทาอมฟ้ากลาง ไม่ใช่ฟ้า — ชื่อหมวดเป็นป้ายบอกประเภท กดไม่ได้
     ในพาเลตต์นี้ฟ้าแปลว่า "กดได้" ถ้าใช้ฟ้าผู้ใช้จะพยายามกดแล้วไม่มีอะไรเกิดขึ้น
     บนพื้นการ์ดขาวได้ 5.51:1 ผ่านเกณฑ์ตัวอักษร */
  color: var(--muted);
  font-family: var(--font-head);
  font-size: var(--fs-micro);
}

.book-title {
  /* ขนาดผูกกับการ์ด ไม่ใช่กับระดับหัวข้อ การ์ดใบเดียวกันจึงหน้าตาเท่ากันทุกหน้า
     ต่อให้หน้าหนึ่งใช้ h3 (หน้าแรก อยู่ใต้หัวข้อของหมวด) และอีกหน้าใช้ h2
     (ชั้นหนังสือ อยู่ใต้ h1 ของหน้าโดยตรง) ตามลำดับหัวข้อที่ถูกต้องของหน้านั้น */
  font-size: var(--fs-h3);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.book-title a { color: var(--ink); }
.book-title a:hover { color: var(--brand); }

.book-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: auto;
  padding-top: 0.75rem;
}

.price {
  display: flex;
  align-items: baseline;
  gap: 0.375rem;
  /* สีเดียวกับป้ายจำนวนในตะกร้า (--offer) เพราะทั้งคู่พูดเรื่องการซื้อเหมือนกัน
     ใช้ได้เพราะราคาอยู่บนพื้นขาวเสมอ — การ์ดหนังสือ แผงตะกร้า การ์ดคำสั่งซื้อ
     และหน้าสินค้า ได้ 4.70:1 ผ่านเกณฑ์ 4.5:1
     **ห้ามย้ายราคาไปวางบนแถบเทา** ตรงนั้นค่านี้เหลือ 4.29:1 ต้องสลับเป็น --offer-dark */
  color: var(--offer);
  font-family: var(--font-head);
  /* ขยับขึ้นหนึ่งขั้นเป็น --fs-h3 ไม่เพิ่มระดับใหม่นอกสเกลเจ็ดระดับ
     ราคาคือตัวเลขที่คนใช้ตัดสินใจ ควรอ่านออกก่อนชื่อเรื่องด้วยซ้ำ */
  font-size: var(--fs-h3);
}

/* บทความ (article.php และ article-detail.php) ───────────────────────────
   การ์ดบทความยืมโครงของ .book-card มาทั้งใบ ต่างกันแค่สัดส่วนภาพกับที่ว่าง
   จึงไม่มีคลาสพื้นหลัง เงา หรือ hover ซ้ำที่นี่ แก้ที่ .book-card แล้วเปลี่ยนทั้งสองแบบ */

/* คอลัมน์เดียวบนมือถือ ไม่ใช่สองเหมือนการ์ดหนังสือ เพราะการ์ดบทความมีทั้งภาพนอน
   ชื่อเรื่อง และรายละเอียดย่อย ยัดสองใบต่อแถวบนจอ 390px แล้วเหลือที่ให้ข้อความไม่พอ */
.article-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1rem;
}

/* ภาพประกอบเป็น 7:5 เท่าขนาดที่หลังบ้านแนะนำไว้ (840×600)
   ตัวเลขนี้ต้องตรงกับข้อความใต้ช่องเลือกไฟล์ใน admin/article-add.php
   ไม่งั้นภาพที่อัปตามคำแนะนำจะถูก object-fit ตัดขอบทิ้ง */
.article-cover {
  position: relative;
  display: block;
  overflow: hidden;
  background-color: var(--surface);
  aspect-ratio: 7 / 5;
}

.article-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}
.article-card:hover .article-cover img { transform: scale(1.05); }

/* รายละเอียดย่อยบนการ์ด — ตัดที่สามบรรทัด การ์ดทุกใบในแถวเดียวกันจะได้สูงเท่ากัน
   ไม่ต้องตัดข้อความตั้งแต่ฝั่งเซิร์ฟเวอร์ ค่าเต็มยังอยู่ครบให้หน้าอ่านบทความใช้ต่อ

   ไม่มี margin-top ของตัวเอง ระยะจากชื่อเรื่องมาจาก gap ของ .book-body อย่างเดียว
   ใส่ทั้งสองอย่างแล้วช่องไฟใต้ชื่อเรื่องจะกว้างกว่าช่องไฟใต้ชื่อหมวดทั้งที่เป็นการ์ดใบเดียวกัน

   ใช้กรมท่า (--ink) สีเดียวกับชื่อเรื่อง ลำดับความสำคัญบอกด้วยขนาดกับน้ำหนักตัวอักษร
   ไม่ใช่ด้วยสี บนพื้นการ์ด --card-soft ได้ 17.42:1 */
.article-details {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  color: var(--ink);
  font-size: var(--fs-small);
}

/* ปุ่มกรองหมวด — เป็นลิงก์จริง ไม่ใช่ปุ่มที่รอจาวาสคริปต์
   สูง 44px ตามเกณฑ์พื้นที่กดบนมือถือ ล้นแถวแล้วขึ้นบรรทัดใหม่ ไม่เลื่อนแนวนอน */
.article-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: var(--gutter);
}

.article-chip {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: 999px;
  background-color: var(--card);
  padding: 0.625rem 1rem;
  min-height: 44px;
  color: var(--body);
  font-family: var(--font-head);
  font-size: var(--fs-small);
  transition: border-color 0.2s ease, color 0.2s ease;
}

.article-chip:hover {
  border-color: var(--brand);
  color: var(--brand);
}

/* หมวดที่เลือกอยู่ — พื้นฟ้าเข้มกับตัวอักษรขาวได้ 5.93:1 เฉดเดียวกับปุ่มหลัก
   ไม่ได้บอกด้วยสีอย่างเดียว aria-current เป็นตัวบอกจริงสำหรับโปรแกรมอ่านหน้าจอ */
.article-chip[aria-current] {
  border-color: var(--brand);
  background-color: var(--brand);
  color: #fff;
}

/* หัวเรื่องของหน้าอ่านบทความ กับประโยคนำใต้ h1 ของหน้ารวม */
.article-head { margin-bottom: var(--gutter); }

.article-lead {
  max-width: 44rem;
  margin-top: 0.5rem;
  color: var(--body);
}

/* ภาพประกอบในหน้าอ่านบทความ กว้างเท่า .prose ขอบซ้ายจึงตรงกับเนื้อหาที่อยู่ข้างล่าง */
.article-figure {
  margin: 0 0 var(--card-p);
  max-width: 44rem;
  border-radius: var(--r-card);
  overflow: hidden;
  background-color: var(--surface);
}

.article-figure img {
  display: block;
  width: 100%;
  height: auto;
}

/* ลิงก์กลับไปหน้ารวม อยู่บรรทัดสุดท้ายของแถบเนื้อหาในหน้าสินค้าและหน้าอ่านบทความ
   เว้นเฉพาะด้านบน เพราะระยะด้านล่างมาจาก padding ของแถบที่มันอยู่ข้างในอยู่แล้ว */
.back-link { padding-top: var(--card-p); }

@media (min-width: 640px) {
  .article-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1024px) {
  .article-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* สไลด์หนังสือมาใหม่ — เว้นที่ด้านล่างไว้ให้เงาของการ์ดตอนยกขึ้น */
.book-swiper { padding-bottom: 0.5rem; }
.book-swiper .swiper-slide { height: auto; }

.swiper-nav {
  display: flex;
  gap: 0.375rem;
}

.swiper-nav-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background-color: var(--card);
  color: var(--brand-dark);
  cursor: pointer;
}
.swiper-nav-btn:hover { background-color: var(--brand-soft); }
.swiper-nav-btn svg { width: 16px; height: 16px; }
.swiper-nav-btn.swiper-button-disabled { color: var(--soft); cursor: default; }

/* ปุ่มรองก็เป็นแคปซูลอยู่แล้วผ่าน .btn — ปุ่มอันตรายคงเป็นข้อความ */

/* 8 ── แถบโปรโมชัน ─────────────────────────────────────────────────────── */
.parallax-band {
  position: relative;
  overflow: hidden;
  background-color: var(--brand-soft);
  background-image:
    radial-gradient(42% 70% at 92% 18%, rgb(244 63 94 / 0.10), transparent 62%),
    radial-gradient(rgb(2 132 199 / 0.10) 1.5px, transparent 1.5px);
  background-size: auto, 26px 26px;
  color: var(--body);
}

.parallax-inner {
  position: relative;
  display: grid;
  gap: 1.75rem;
  padding-block: var(--section-y);
  text-align: center;
}

.offer-kicker {
  display: inline-block;
  border-radius: var(--r-full);
  background-color: var(--brand);
  padding: 0.25rem 0.875rem;
  color: #fff;
  font-family: var(--font-head);
  font-size: var(--fs-caption);
}

.parallax-title {
  margin-top: 0.75rem;
  color: var(--ink);
  font-weight: 600;
}

/* บนพื้น --brand-soft ของแถบพาราแลกซ์ --offer-dark ได้ 5.48:1 ส่วน --offer ได้ 4.29:1 ตกเกณฑ์ */
.parallax-title strong { color: var(--offer-dark); }

.parallax-text {
  margin-inline: auto;
  margin-top: 0.75rem;
  max-width: 32rem;
  color: var(--body);
}

.offer-copy .btn { margin-top: 1.25rem; }

.offer-art {
  height: 14rem;
  margin: 0;
  border: 8px solid rgb(255 255 255 / 0.65);
  border-radius: 42% 58% 40% 60% / 55% 42% 58% 45%;
  overflow: hidden;
  box-shadow: var(--shadow-lift);
}

.offer-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 73% center;
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
  border-top: 1px solid rgb(15 23 42 / 0.12);
  padding-top: 1.75rem;
}

.stat-item {
  display: flex;
  flex-direction: column-reverse;
}

.stat-value {
  color: var(--ink);
  font-family: var(--font-head);
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.2;
}
.stat-value-income { color: var(--offer-dark); }

.stat-label {
  margin-top: 0.25rem;
  color: var(--muted);
  font-size: var(--fs-caption);
}

/* 9 ── ตะกร้า และหน้าต่างซ้อน ─────────────────────────────────────────── */
.overlay {
  position: fixed;
  inset: 0;
  height: 100dvh;
  z-index: 50;
  background-color: rgb(15 23 42 / 0.45);
}

/* 100dvh คือความสูงของจอ "ที่มองเห็นอยู่ตอนนี้" ซึ่งหดขยายตามแถบที่อยู่เว็บ
   ของเบราว์เซอร์มือถือ ต่างจาก top/bottom ที่วัดกับจอตอนแถบนั้นหุบไปแล้ว
   ใช้ top/bottom อย่างเดียวเมื่อไหร่ ส่วนท้าย (ยอดรวมกับปุ่มชำระเงิน)
   จะตกไปอยู่ใต้ขอบล่างจนต้องไถลหน้าลงไปหา ซึ่งเป็นสิ่งที่ทำไม่ได้อยู่แล้ว
   เพราะ body.is-locked ล็อกการเลื่อนหน้าไว้ตอนแผงเปิด
   บรรทัด bottom ยังอยู่เป็นทางถอยของเบราว์เซอร์เก่าที่ไม่รู้จัก dvh
   (มี height เมื่อไหร่ bottom จะถูกมองข้ามไปเอง) */
.drawer {
  position: fixed;
  transition: transform 0.3s cubic-bezier(0.22, 0.61, 0.36, 1);
  top: 0;
  right: 0;
  bottom: 0;
  height: 100dvh;
  z-index: 60;
  display: flex;
  flex-direction: column;
  width: min(24rem, 100%);
  background-color: var(--card);
  box-shadow: var(--shadow-panel);
}

.drawer.drawer-hidden { transform: translateX(100%); }

.drawer-head,
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  border-bottom: 1px solid var(--line);
  padding: 1rem 1.25rem;
}

.drawer-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 1.25rem;
}

/* ใช้ grid gap คุมระยะ ไม่ให้ปุ่มสองอันชนกันตอน hover ที่พื้นหลังโผล่ขึ้นมา */
.drawer-foot {
  display: grid;
  gap: 0.75rem;
  justify-items: stretch;
  border-top: 1px solid var(--line);
  background-color: var(--surface);
  padding: 1.25rem;
}

/* ปุ่มล้างตะกร้าไม่เต็มความกว้าง เพื่อไม่ให้หนักเท่าปุ่มชำระเงิน */
.drawer-foot .btn-danger { justify-self: center; }

.cart-list {
  display: grid;
  gap: 1rem;
}

.cart-item {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr);
  gap: 0.875rem;
  align-items: start;
}

.cart-item img {
  width: 56px;
  border-radius: var(--r-md);
  aspect-ratio: 5 / 7;
  object-fit: cover;
}

.cart-item-title {
  color: var(--ink);
  font-family: var(--font-head);
  font-size: var(--fs-small);
  line-height: 1.5;
}

.cart-item-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.375rem;
}

.cart-empty {
  padding-block: 3rem;
  color: var(--muted);
  font-size: var(--fs-small);
  text-align: center;
}

.cart-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}
.cart-total-value {
  color: var(--ink);
  font-family: var(--font-head);
  /* ยอดรวมคือตัวเลขสุดท้ายที่คนอ่านก่อนกดจ่าย จึงใหญ่กว่าอย่างอื่นในแผง
     ใช้ 1.5rem คงที่ ซึ่งคือค่าฐานของ --fs-h2 แต่ไม่ขยายตามจอ
     เพราะแผงตะกร้ากว้าง 24rem เท่ากันทุกจอ ตัวอักษรข้างในจึงไม่ต้องโตตาม
     ต่างจากหัวข้อในหน้าหลักที่พื้นที่รอบตัวมันกว้างขึ้นจริงเมื่อจอใหญ่ขึ้น */
  font-size: 1.5rem;
}

/* .modal ทับเต็มจออยู่เหนือ .overlay ตัวมันเองจึงเป็นชั้นที่รับคลิกจริง
   การกดพื้นที่ว่างเพื่อปิดจึงต้องผูกไว้ที่นี่ด้วย @click.self ไม่ใช่ที่ .overlay
   ซึ่งกดไม่ถึง — .overlay เหลือหน้าที่แค่ทำฉากหลังให้มืดลง */
.modal {
  position: fixed;
  inset: 0;
  height: 100dvh;
  z-index: 60;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 1rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.modal-panel {
  width: 100%;
  max-width: 26rem;
  border-radius: var(--r-card);
  background-color: var(--card);
  box-shadow: var(--shadow-panel);
}

.modal-body { padding: 1.25rem; }

/* คำอธิบายสั้น ๆ ใต้หัวข้อในหน้าต่างซ้อน บอกว่าจะเกิดอะไรขึ้นก่อนผู้ใช้กรอก */
.modal-note {
  margin-bottom: 1rem;
  color: var(--body);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

/* คำเตือนก่อนกรอกในฟอร์มสมัครสมาชิกกับแก้ไขข้อมูลสมาชิก ใช้ฟ้าของแบรนด์
   เพื่อให้อ่านก่อนช่องแรก โดยไม่ดูเหมือนข้อความผิดพลาดซึ่งเป็นสีแดง
   --brand บนการ์ดสีขาวได้คอนทราสต์ 5.30:1 ผ่านเกณฑ์ 4.5:1 */
.modal-note-brand { color: var(--brand); }

.tab-switch {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.25rem;
  border-radius: var(--r-full);
  background-color: var(--surface);
  padding: 0.25rem;
  margin-bottom: 1.25rem;
}

.tab-switch-btn {
  border: 0;
  border-radius: var(--r-full);
  background-color: transparent;
  padding: 0.5rem;
  color: var(--muted);
  font-family: var(--font-head);
  font-size: var(--fs-small);
  cursor: pointer;
}
.tab-switch-btn[aria-selected="true"] {
  background-color: var(--brand);
  color: #fff;
  box-shadow: var(--shadow-card);
}

/* 10 ── ฟอร์ม ─────────────────────────────────────────────────────────── */
.field { margin-bottom: 0.875rem; }

.field-label {
  display: block;
  margin-bottom: 0.375rem;
  color: var(--ink);
  font-family: var(--font-head);
  font-size: var(--fs-caption);
}

.field-input {
  width: 100%;
  height: 44px;
  /* ขอบต้องมองเห็นจริง — สีเส้นปกติ (--line) ได้คอนทราสต์แค่ 1.10:1
     ต่ำกว่าเกณฑ์ 3:1 ขององค์ประกอบ UI มาก ใช้เทาอมฟ้ากลาง (5.51:1) แทน */
  border: 1px solid var(--muted);
  border-radius: var(--r-md);
  background-color: var(--card);
  padding-inline: 0.875rem;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-small);
}
.field-input::placeholder { color: var(--soft); }

/* ช่องรหัสผ่านที่มีปุ่มลูกตาไว้กดดูรหัสที่พิมพ์ไป ปุ่มลอยอยู่ในกรอบช่องทางขวา
   เผื่อที่ว่างขวา 3rem ให้ปุ่ม รหัสยาว ๆ จะได้ไม่ลอดใต้ปุ่ม
   ปุ่มเป็น .icon-button อยู่แล้วจึงได้พื้นที่กด 44×44px ตามเกณฑ์มือถือ
   เทาอมฟ้ากลาง (--muted) บนพื้นการ์ดได้ 5.51:1 เกินเกณฑ์องค์ประกอบ UI ที่ 3:1 */
/* ช่องที่แสดงค่าอย่างเดียว — พื้นเทาแทนพื้นขาวของช่องที่พิมพ์ได้ ผู้ใช้จะได้รู้ตั้งแต่
   ก่อนกดว่าช่องนี้แก้ไม่ได้ ไม่ใช่กดแล้วพิมพ์ไม่ออกโดยไม่มีคำอธิบาย

   ย้ำสีตัวอักษรกับ opacity ไว้เอง เพราะเบราว์เซอร์จางช่องที่ disabled ให้อัตโนมัติ
   จนค่าในช่องอ่านไม่ออก ทั้งที่ค่านั้นคือสิ่งเดียวที่ช่องนี้มีไว้บอก
   (-webkit-text-fill-color มีไว้สำหรับ Safari ซึ่งไม่ฟัง color ของช่องที่ disabled) */
.field-input[readonly],
.field-input:disabled {
  background-color: var(--surface);
  color: var(--ink);
  -webkit-text-fill-color: var(--ink);
  opacity: 1;
  cursor: default;
}

.field-password { position: relative; }

.field-password .field-input { padding-right: 3rem; }

.field-eye {
  position: absolute;
  top: 50%;
  right: 0.25rem;
  transform: translateY(-50%);
  color: var(--muted);
}
/* ช่องกรอกไม่มีตัวบอกโฟกัสเลย เป็นการตัดสินใจของเจ้าของงาน (ไม่ใช่ความผิดพลาด)
   โฟกัสแล้วหน้าตาเหมือนตอนปกติทุกอย่าง

   ข้อแลก: ไม่ผ่าน WCAG 2.4.7 Focus Visible ระดับ AA — คนที่ไล่ช่องด้วยปุ่ม Tab
   ในฟอร์มสมัครสมาชิก 5 ช่อง จะไม่เห็นว่าเคอร์เซอร์อยู่ช่องไหน
   ถ้าจะเอากลับ ใช้ border-color: var(--ink) คู่กับ box-shadow: inset 0 0 0 1px var(--ink)
   ซึ่งบอกโฟกัสได้โดยไม่ทำให้ข้อความขยับ */
.field-input:focus-visible { outline: none; }
/* ขอบช่องที่กรอกผิดกับข้อความใต้ช่องใช้ --offer เฉดเดียวกับป้ายจำนวนในตะกร้า
   และปุ่มแดงในแผงตะกร้า ทั้งเว็บฝั่งลูกค้าจึงมีแดงวงศ์เดียว ไม่ใช่แดงอิฐกับชมพูแดง
   ปนกันคนละที่ ข้อความผิดพลาดอยู่บนพื้นการ์ดขาวเสมอ (ในหน้าต่างซ้อนกับ .order-card)
   ได้ 4.70:1 ผ่านเกณฑ์ตัวอักษร ส่วนขอบเป็นองค์ประกอบ UI ใช้เกณฑ์ 3:1 */
.field-input[aria-invalid="true"] { border-color: var(--offer); }

.field-error {
  display: block;
  margin-top: 0.25rem;
  color: var(--offer);
  font-size: var(--fs-caption);
}

.field-check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 1rem;
  color: var(--body);
  font-size: var(--fs-caption);
  cursor: pointer;
}

.field-check input {
  width: 18px;
  height: 18px;
  accent-color: var(--brand);
}

/* ดาวแดงท้ายป้ายกำกับของช่องที่ต้องกรอก
   ตัวมันเองเป็นภาพล้วน (aria-hidden) คนที่ใช้โปรแกรมอ่านหน้าจอรู้จาก
   aria-required ที่ช่องกรอกแทน ไม่ใช่จากการอ่านคำว่า "ดาว" ออกมา
   ใช้ --offer เฉดเดียวกับข้อความผิดพลาดที่อยู่ในฟอร์มเดียวกัน ไม่งั้นดาวกับข้อความ
   จะเป็นแดงคนละเฉดในสายตาเดียว บนขาวได้ 4.70:1 ผ่านเกณฑ์ตัวอักษร */
.field-req {
  margin-inline-start: 0.125rem;
  color: var(--offer);
}

/* ลิงก์ใต้ปุ่มในฟอร์ม — เป็น <button> ไม่ใช่ <a> เพราะเปิดหน้าต่างซ้อนในหน้าเดิม
   ไม่ได้พาไปที่อยู่ใหม่ ระยะในทำให้พื้นที่กดสูงครบ 44px ทั้งที่ตัวอักษรเล็ก
   เทาอมฟ้าเข้มบนพื้นขาวได้ 7.58:1 ผ่านเกณฑ์ 4.5:1 ของตัวอักษร
   ไม่ใช้ฟ้าเพราะฟ้าสงวนไว้ให้การกระทำหลัก ส่วนนี่เป็นทางเลือกรองที่สุดในฟอร์ม
   ขีดใต้จึงเป็นตัวบอกว่ากดได้แทนสี ไม่ได้พึ่งสีอย่างเดียว */
.form-foot {
  margin-top: 0.25rem;
  text-align: center;
}

.link-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  border: 0;
  background-color: transparent;
  padding-inline: 0.75rem;
  color: var(--body);
  font-family: var(--font-body);
  font-size: var(--fs-small);
  text-decoration: underline;
  cursor: pointer;
}
.link-button:hover { color: var(--ink); }
.link-button:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* 11 ── ส่วนท้าย ──────────────────────────────────────────────────────── */
.site-footer {
  background-color: var(--deep);
  color: var(--deep-body);
}

.footer-grid {
  display: grid;
  gap: 1.5rem;
  padding-block: 1.5rem;
}

.footer-title {
  margin-bottom: 0.625rem;
  color: #fff;
  font-size: var(--fs-h3);
}

.footer-text-line { display: block; }

.footer-text {
  /* กว้างพอให้แต่ละประโยคอยู่ครบในบรรทัดเดียว วัดจริงแล้วประโยคยาวสุด 390px */
  max-width: 30rem;
  color: var(--deep-body);
  font-size: var(--fs-small);
}

/* line-height ตั้งที่ ul เพื่อให้ li รับไปด้วย ถ้าตั้งแค่ที่ <a> กล่องของ li
   จะยังสูงตามค่า 1.75 ที่รับมาจาก body รายการก็ไม่แน่นขึ้นจริง */
.footer-list {
  display: grid;
  gap: 0.25rem;
  line-height: 1.5;
}

/* ไอคอนนำหน้าเบอร์โทรกับอีเมล — inline-flex เพื่อให้ไอคอนกับข้อความอยู่กลางแนวเดียวกัน
   ไอคอนรับสีจาก currentColor จึงจางเท่าข้อความ และเปลี่ยนเป็นขาวพร้อมกันตอน hover */
.footer-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--deep-body);
  font-size: var(--fs-small);
}

/* flex-shrink: 0 กันไอคอนถูกบีบแบนตอนอีเมลยาวจนเต็มความกว้างของคอลัมน์
   ตั้ง color ที่นี่เพื่อทับ currentColor ที่รับมาจาก .footer-link ไอคอนจึงเป็นฟ้า
   ส่วนข้อความยังเป็น --deep-body ตามเดิม และคงฟ้าไว้ตอน hover ด้วย
   เพราะสีของไอคอนเป็นเครื่องหมายประจำตัว ไม่ใช่สถานะของลิงก์ */
.footer-link svg {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  color: var(--deep-accent);
}
.footer-link:hover { color: #fff; }

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  border-top: 1px solid var(--deep-2);
  padding-block: 0.875rem;
  color: var(--deep-body);
  font-size: var(--fs-caption);
}

/* 12 ── หน้ารายละเอียดสินค้า ──────────────────────────────────────────── */
.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
  /* ไม่เว้นด้านล่าง เพราะ section ที่ตามมามี padding ของตัวเองอยู่แล้ว
     ถ้าเว้นทั้งคู่จะกลายเป็นช่องว่างซ้อนกันสองชั้น */
  padding-block: 0.75rem 0;
  color: var(--muted);
  font-size: var(--fs-caption);
}
.breadcrumb a { color: var(--muted); }
.breadcrumb a:hover { color: var(--ink); }
.breadcrumb-sep { color: var(--soft); }

.detail-grid {
  display: grid;
  gap: 1.75rem;
  align-items: start;
}

/* แกลเลอรีภาพหนังสือ — แถบภาพย่อตั้งอยู่ซ้าย ภาพใหญ่อยู่ขวา
   คลาส .is-ready กับ .can-zoom มาจาก js/motion.js เท่านั้น
   ถ้าไฟล์นั้นพัง ภาพทุกใบจะเรียงต่อกันลงมาให้เห็นครบตามค่าตั้งต้นข้างล่างนี้
   และแถบภาพย่อที่กดแล้วไม่เกิดอะไรจะไม่ขึ้นมาเลย ตรงกับหลักที่ว่า JS เป็นของเสริม */
.gallery {
  /* กำลังขยายตอนวางเมาส์ — js/motion.js อ่านค่านี้ไปคำนวณระยะเลื่อนภาพ
     จึงมีที่เดียวที่นี่ ห้ามเขียนตัวเลขซ้ำในไฟล์ JS */
  --gallery-zoom: 2;
  display: grid;
  gap: 0.75rem;
  max-width: 24rem;
}

.gallery.is-ready { grid-template-columns: 5rem minmax(0, 1fr); }

.gallery-viewport {
  position: relative;
  display: grid;
  gap: 0.75rem;
}

.gallery.is-ready .gallery-viewport {
  grid-column: 2;
  grid-row: 1;
  gap: 0;
  border-radius: var(--r-card);
  box-shadow: var(--shadow-lift);
  overflow: hidden;
}
.gallery.can-zoom .gallery-viewport { cursor: zoom-in; }

.gallery-image {
  width: 100%;
  border-radius: var(--r-card);
  aspect-ratio: 5 / 7;
  object-fit: cover;
}

/* ทุกใบซ้อนอยู่ช่องเดียวกัน ใบที่เลือกอยู่จางเข้ามาทับใบเดิม
   ระยะเลื่อน --zx/--zy ตั้งจาก js/motion.js ตอนเมาส์ขยับ ค่าตั้งต้นคือไม่เลื่อน */
.gallery.is-ready .gallery-image {
  grid-area: 1 / 1;
  border-radius: 0;
  opacity: 0;
  pointer-events: none;
  transform: translate(var(--zx, 0), var(--zy, 0)) scale(var(--zoom-now, 1));
  transition: opacity 0.35s ease, transform 0.18s ease-out;
}
.gallery.is-ready .gallery-image.is-active { opacity: 1; }

.gallery-viewport.is-zooming { --zoom-now: var(--gallery-zoom); }

/* ป้ายบอกวิธีใช้ ขึ้นเฉพาะเครื่องที่มีเมาส์จริง แล้วหลบให้ตอนกำลังส่องภาพ */
.gallery-hint {
  display: none;
  position: absolute;
  bottom: 0.625rem;
  left: 50%;
  z-index: 1;
  transform: translateX(-50%);
  border-radius: var(--r-full);
  /* เทาอมฟ้ากลาง ไม่ใช่กรมท่า เพราะป้ายนี้เป็นคำใบ้ ไม่ใช่ปุ่มและไม่ใช่ข้อมูลหลัก
     ขาวบนพื้นนี้ได้ 5.51:1 ผ่านเกณฑ์ 4.5:1 ของตัวอักษร */
  background-color: var(--muted);
  padding: 0.25rem 0.625rem;
  color: #fff;
  font-family: var(--font-head);
  font-size: var(--fs-micro);
  white-space: nowrap;
  transition: opacity 0.2s ease;
}
.gallery.can-zoom .gallery-hint { display: block; }
.gallery-viewport.is-zooming .gallery-hint { opacity: 0; }

/* แถวกลางเป็น minmax(0, 1fr) เพื่อให้แถบภาพย่อสูงเท่าภาพใหญ่พอดีแล้วเลื่อนในตัวเอง
   ถ้าใช้ auto แถบจะยืดตามจำนวนภาพจนดันปุ่มลงล่างหลุดออกไปนอกกรอบ */
.gallery-side {
  display: none;
  position: relative;
}
.gallery.is-ready .gallery-side {
  display: block;
  grid-column: 1;
  grid-row: 1;
  /* สูงศูนย์แล้วดันกลับด้วย min-height เพื่อไม่ให้ความสูงของภาพย่อทั้งกอง
     ไปกำหนดความสูงของแถว ไม่งั้นกรอบภาพใหญ่จะถูกยืดจนมีที่ว่างใต้ภาพ
     ความสูงของแถวต้องมาจากภาพใหญ่ฝั่งเดียว แล้วแถบภาพย่อค่อยเลื่อนเอาเอง */
  height: 0;
  min-height: 100%;
}

/* ปุ่มดูตัวอย่างหนังสือ นั่งเต็มความกว้างของแกลเลอรีในแถวของตัวเองเสมอ
   1 / -1 จึงครอบทั้งตอนจอเล็กที่แกลเลอรีมีคอลัมน์เดียว และตอนจอกว้าง
   ที่ .gallery.is-ready แตกเป็นแถบภาพย่อ 6rem กับภาพใหญ่ ปุ่มไม่ไปแทรก
   ช่องไหนของสองอันนั้น เพราะทั้งคู่ถูกตรึงไว้ที่แถวหนึ่งด้วย grid-row: 1
   justify-self ให้ตัวปุ่มอยู่กึ่งกลาง ไม่ใช่ text-align ซึ่งจัดแค่ตัวอักษร
   ข้างในปุ่มที่กว้างเต็มแถวอยู่ดี */
/* ใช้ .btn-buy สีชมพูแดง ไม่ใช่ .btn-file กรมท่า และไม่ได้เขียนสีเฉพาะกิจให้ปุ่มนี้
   ขนาดไอคอนจึงต้องประกาศที่นี่ เพราะเดิมรับมาจากกฎ .btn-file svg */
.gallery-sample {
  grid-column: 1 / -1;
  justify-self: center;
}
.gallery-sample svg { width: 16px; height: 16px; }

/* แบ่งความสูงของแถบออกเป็นสามช่องเท่า ๆ กัน (หักช่องไฟสองช่องออกก่อน)
   ภาพย่อจึงใหญ่ที่สุดเท่าที่จะโชว์ครบสามใบได้ ไม่ว่าจอกว้างเท่าไร
   ใบที่สี่จะโผล่มาแค่ริม ๆ เป็นตัวบอกว่ายังมีต่อให้เลื่อนดู */
.gallery-strip {
  position: relative;
  display: grid;
  height: 100%;
  align-content: start;
  grid-auto-rows: calc((100% - 0.75rem) / 3);
  gap: 0.375rem;
  overflow-y: auto;
  scroll-behavior: smooth;
  /* ซ่อนแถบเลื่อนของระบบ เพราะปุ่มขึ้น-ลงทำหน้าที่นั้นอยู่แล้ว
     ยังลากด้วยนิ้วหรือ trackpad ได้ตามปกติ */
  scrollbar-width: none;
}
.gallery-strip::-webkit-scrollbar { display: none; }

.gallery-thumb {
  display: block;
  width: 100%;
  height: 100%;
  border: 2px solid transparent;
  border-radius: var(--r-md);
  background-color: transparent;
  padding: 0;
  opacity: 0.65;
  cursor: pointer;
  transition: opacity 0.2s ease, border-color 0.2s ease;
}
.gallery-thumb:hover { opacity: 1; }
.gallery-thumb.is-active {
  border-color: var(--brand);
  opacity: 1;
}

.gallery-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: calc(var(--r-md) - 2px);
  object-fit: cover;
}

/* ปุ่มเลื่อนเป็นแถบโปร่งแสงวางทับหัวและท้ายแถบภาพย่อ ยังเห็นปกหนังสือที่อยู่ข้างใต้

   แถบที่มองเห็นสูง 30px แต่พื้นที่กดสูง 44px เต็มตามกฎสี่สิบสี่พิกเซล
   ใช้วิธีเดียวกับปุ่มไอคอนบนหัวเว็บ คือให้ปุ่มเป็นพื้นที่กดใส ๆ
   แล้ววาดแถบที่มองเห็นด้วย ::before ชิดขอบบนหรือขอบล่างของพื้นที่นั้น
   ส่วนที่ยื่นเกินแถบจึงกดได้แต่มองไม่เห็น ไม่ไปบังปกเพิ่ม

   ขาว 62% ต่อให้ทับภาพที่ดำสนิทก็ยังได้คอนทราสต์กับลูกศรสีกรมท่าเข้ม 6.66:1
   ผ่านเกณฑ์ 3:1 ขององค์ประกอบ UI ส่วนบนภาพที่สว่างกว่านั้นยิ่งได้มากกว่า */
.gallery-nav {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 1;
  display: flex;
  justify-content: center;
  height: 2.75rem;
  border: 0;
  background-color: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: opacity 0.2s ease;
}

/* โค้งเฉพาะสองมุมที่ตรงกับมุมของภาพย่อ ส่วนขอบที่หันเข้าหาปกตัดเรียบ
   แถบจึงดูเหมือนถูกเฉือนออกมาจากภาพย่อใบนั้น ไม่ใช่ชิปลอยทับอยู่ */
.gallery-nav::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 1.875rem;
  background-color: rgb(255 255 255 / 0.62);
  backdrop-filter: blur(6px);
  transition: background-color 0.2s ease;
}

/* 0.3125rem คือระยะที่ทำให้ลูกศร 20px อยู่กลางแถบ 30px พอดี */
.gallery-prev {
  top: 0;
  align-items: flex-start;
  padding-top: 0.3125rem;
}
.gallery-prev::before {
  top: 0;
  border-radius: var(--r-md) var(--r-md) 0 0;
}

.gallery-next {
  bottom: 0;
  align-items: flex-end;
  padding-bottom: 0.3125rem;
}
.gallery-next::before {
  bottom: 0;
  border-radius: 0 0 var(--r-md) var(--r-md);
}

/* ลูกศรต้องอยู่เหนือแถบ ไม่ใช่ใต้แถบ */
.gallery-nav svg {
  position: relative;
  width: 20px;
  height: 20px;
}

.gallery-nav:hover::before { background-color: rgb(255 255 255 / 0.82); }

/* ปุ่มที่กดไม่ได้จางลงจนเกือบกลืนไปกับปกที่อยู่ข้างใต้ ไม่ต้องเปลี่ยนสีลูกศร */
.gallery-nav:disabled {
  opacity: 0.4;
  cursor: default;
}
.gallery-nav:disabled:hover::before { background-color: rgb(255 255 255 / 0.62); }

.detail-title { margin-block: 0.5rem 0.625rem; }

.detail-price {
  display: flex;
  align-items: baseline;
  gap: 0.625rem;
  margin-block: 1.25rem;
  /* สีเดียวกับ .price และป้ายจำนวนในตะกร้า อยู่ใน section พื้นขาว 4.70:1 */
  color: var(--offer);
  font-family: var(--font-head);
  /* ใช้ระดับเดียวกับหัวข้อส่วน (--fs-h2) ไม่เพิ่มขนาดใหม่นอกสเกลเจ็ดระดับ
     ราคาคือตัวเลขที่คนตัดสินใจจากมัน จึงหนา 600 เท่าหัวข้อแบนเนอร์ */
  font-size: var(--fs-h2);
  font-weight: 600;
}
.detail-actions {
  display: grid;
  gap: 0.625rem;
  margin-bottom: 1.25rem;
}

/* ไม่ใช้ gap เพราะแถวที่มีปุ่มสูงกว่าแถวข้อความ ระยะจะไม่เท่ากัน
   ให้ทุกแถวสูง 44px เท่ากันหมด จังหวะจึงสม่ำเสมอไม่ว่าข้างในเป็นอะไร */
.spec-list {
  display: grid;
  border-top: 1px solid var(--line);
  padding-top: 1.25rem;
}

.spec-row {
  display: flex;
  align-items: center;
  min-height: 44px;
  justify-content: space-between;
  gap: 1rem;
  font-size: var(--fs-small);
}
.spec-row dt { color: var(--muted); }
.spec-row dd { margin: 0; color: var(--ink); font-family: var(--font-head); }

.prose { max-width: 44rem; }
.prose p + p { margin-top: 0.875rem; }

/* รายละเอียดหนังสือเป็น HTML ที่ผู้ดูแลจัดรูปแบบเองจาก admin/book-edit.php
   แท็กที่ผ่านด่าน utils/richtext.php มาได้มีเท่าที่เขียนกฎไว้ข้างล่างนี้เท่านั้น
   **เพิ่มแท็กใน RICHTEXT_TAGS เมื่อไหร่ต้องมาเพิ่มกฎที่นี่ด้วย**
   ไม่งั้นหน้าร้านจะได้แท็กที่ไม่มีใครกำหนดหน้าตาไว้ */

/* Sarabun โหลดมาแค่น้ำหนัก 400 กับ 600 ขอ 700 แล้วเบราว์เซอร์จะปลอมตัวหนาให้
   ซึ่งขอบตัวอักษรจะฟุ้งกว่าฟอนต์จริง จึงระบุ 600 ตรง ๆ เหมือน .book-video-title */
.prose strong { font-weight: 600; }

.prose a {
  color: var(--brand);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.prose a:hover { color: var(--brand-dark); }

/* ตัวหนังสือที่ผู้ดูแลใส่สีพื้นหลังไว้ ต้องมีที่หายใจซ้ายขวาเล็กน้อย
   ไม่งั้นสีจะชนขอบตัวอักษรจนอ่านยาก มุมโค้งให้ดูเป็นปากกาเน้นข้อความ */
.prose span[style*="background-color"] {
  border-radius: 0.25rem;
  padding: 0.0625rem 0.25rem;
}

/* บุลเล็ตในรายละเอียดหนังสือ — เยื้องเข้ามาพอให้ตัวเลขไม่ล้นออกนอกคอลัมน์เนื้อหา
   ต้องเปิด list-style กลับมาเอง เพราะบรรทัดรีเซ็ตที่หัวไฟล์ปิดหัวข้อรายการทิ้งทั้งเว็บ
   (เมนูกับรายการการ์ดไม่ต้องการหัวข้อ) ที่นี่เป็นที่เดียวที่ต้องการมันจริง ๆ */
.prose ol {
  margin: 0.875rem 0 0;
  padding-left: 1.75rem;
  list-style: decimal;
}

.prose ol:first-child { margin-top: 0; }
.prose li + li { margin-top: 0.375rem; }

/* บุลเล็ตเลขวงกลม ①②③ ประกาศชื่อตัวนับซ้ำกับ admin/css/style.css
   เพราะเป็นคนละเอกสารกัน **แก้ที่ไหนต้องแก้ให้ตรงกันทั้งสองที่** */
@counter-style t2i-circled {
  system: fixed;
  symbols: "①" "②" "③" "④" "⑤" "⑥" "⑦" "⑧" "⑨" "⑩" "⑪" "⑫" "⑬" "⑭" "⑮" "⑯" "⑰" "⑱" "⑲" "⑳";
  suffix: " ";
  fallback: decimal;
}

.prose ol.list-circled { list-style: t2i-circled; }

/* เนื้อหาที่ตามหลังหัวข้อทันที ต้องมีที่หายใจ ไม่งั้นตัวอักษรจะติดกันเป็นก้อนเดียว */
h2 + .prose { margin-top: 0.625rem; }

/* วิดีโอแนะนำหนังสือ — กว้างเท่ากับ .prose ขอบซ้ายจึงตรงกับเนื้อหาที่อยู่ข้างบน
   overflow hidden ให้มุมโค้งกินถึงตัววิดีโอ ไม่ใช่โค้งแต่กรอบแล้วภาพทะลุมุมออกมา
   พื้นดำเพราะวิดีโอที่สัดส่วนไม่พอดีกรอบจะเหลือแถบว่างสองข้าง ดำแล้วกลมกลืนกับตัวภาพ
   ตัวเล่นใช้หน้าตาที่ video.js ให้มา ไม่ทับสไตล์ของไลบรารี */
.book-video {
  margin: 0;
  max-width: 44rem;
  border-radius: var(--r-card);
  overflow: hidden;
  background-color: #000;
}

/* หัวข้อวิดีโอนำหน้าตัวเล่นเสมอ ทั้งตอนเป็น h3 ใต้ "รายละเอียดหนังสือ"
   และตอนเป็น h2 ของแถบตัวเอง จึงมีระยะกฎเดียวพอ */
.book-video-title + .book-video { margin-top: 1rem; }

/* หัวข้อย่อยของวิดีโอ อยู่ใต้ h2 รายละเอียดหนังสือ จึงต้องมีระยะแยกจากหัวข้อใหญ่
   สีฟ้าแยกให้เห็นว่าเป็นคำชวนดูวิดีโอ ไม่ใช่หัวข้อของเนื้อหาที่อ่านต่อข้างล่าง
   ใช้ --brand-dark บนพื้น --surface ได้ 7.23:1 (--brand ได้ 5.67:1 ก็ผ่าน
   แต่คงค่าเข้มไว้เพราะบรรทัดนี้เป็นข้อความยาวที่ผู้ดูแลพิมพ์เอง) */
.book-video-title {
  color: var(--brand-dark);

  /* ฟอนต์มีหัว (Sarabun) ทั้งที่เป็นแท็กหัวข้อ เพราะบรรทัดนี้เป็นประโยคเต็ม
     ไม่ใช่ป้ายชื่อหัวข้อสั้น ๆ อ่านเป็นคำต่อคำ ฟอนต์มีหัวจึงอ่านง่ายกว่า
     ไม่ขัดกฎ "Sarabun ห้ามใช้กับหัวข้อใหญ่" เพราะ h3 เป็นระดับเล็กสุด
     ขนาดเท่าเนื้อความอยู่แล้ว น้ำหนัก 600 เพราะ Sarabun โหลดมาแค่ 400 กับ 600
     ใส่ 500 ตามหัวข้ออื่นแล้วเบราว์เซอร์จะถอยไปใช้ 400 ซึ่งบางเกินไป */
  font-family: var(--font-body);
  font-weight: 600;

  /* 1rem คงที่ = 16px ทุกความกว้างจอ ซึ่งคือค่าฐานของ --fs-h3 แต่ไม่ขยายตามจอ
     ใช้โทเคนตรง ๆ ไม่ได้ เพราะ --fs-h3 โตเป็น 1.125rem (18px) ตั้งแต่ 640px ขึ้นไป
     และปล่อยตามแท็กก็ไม่ได้ เพราะบรรทัดนี้เป็น h2 ในเล่มที่ไม่มีรายละเอียดหนังสือ
     ซึ่งจะได้ --fs-h2 (24–30px) วิธีเดียวกับ .modal-title ที่ตรึง 1.5rem ไว้ */
  font-size: 1rem;
}

h2 + .book-video-title { margin-top: 0.625rem; }

/* เนื้อหาที่ตามหลังวิดีโอ ต้องห่างพอให้เห็นว่าเป็นคนละก้อนกับตัวเล่น */
.book-video + .prose { margin-top: var(--card-p); }

/* ข้อความสำรองตอนเบราว์เซอร์เล่นไม่ได้ ต้องอ่านออกบนพื้นดำของกรอบข้างบน */
.book-video .vjs-no-js {
  padding: var(--gutter);
  color: #fff;
}

.book-video .vjs-no-js a { color: #fff; }


/* กล่องข้อความของ SweetAlert2 — ให้ใช้ฟอนต์และมุมโค้งชุดเดียวกับเว็บ */
.swal2-popup {
  border-radius: var(--r-card);
  font-family: var(--font-body);
  font-size: var(--fs-body);
}

.swal2-title {
  font-family: var(--font-head);
  font-size: 1.125rem;
  font-weight: 500;
  color: var(--ink);
}

.swal2-html-container { color: var(--body); font-size: var(--fs-small); }

.swal2-styled {
  border-radius: var(--r-md);
  font-family: var(--font-head);
  font-size: var(--fs-small);
}

.t2i-toast .swal2-title { font-size: var(--fs-small); }

/* ไอคอนวงกลมของ SweetAlert2 มากับสีของมันเอง — เตือนเป็นส้มสด บอกข้อมูลเป็นฟ้าอมเขียว
   ซึ่งเป็นคนละโทนกับพาเลตต์ของเว็บ ดึงกลับมาใช้ชมพูจางของเราแทน
   เลือกไว้สามคลาสเพื่อให้ชนะกฎของ SweetAlert2 ที่มีสองคลาส โดยไม่ต้องใช้ !important
   (ไอคอนตัวที่ต้องการสีเฉพาะกิจ เช่นติกแดงตอนล้างตะกร้า ส่งผ่าน iconColor ใน JS
   ซึ่งเป็น inline style จึงยังชนะกฎนี้อยู่) */
.swal2-popup .swal2-icon.swal2-warning,
.swal2-popup .swal2-icon.swal2-info {
  border-color: var(--offer-icon);
  color: var(--offer-icon);
}

/* 13 ── หน้าชำระเงินกับชั้นหนังสือ ────────────────────────────────────── */
.order-head {
  display: grid;
  gap: 0.375rem;
  margin-bottom: 1.5rem;
}

.order-ref {
  color: var(--muted);
  font-size: var(--fs-small);
}

.order-ref-code {
  color: var(--ink);
  font-family: var(--font-head);
  font-weight: 500;
}

/* บรรทัดเลขสมาชิกในหน้าชั้นหนังสือกับประวัติคำสั่งซื้อ ใหญ่ขึ้นหนึ่งขั้นเป็น --fs-h3
   เพราะเป็นข้อมูลที่ลูกค้าต้องอ่านออกตอนแจ้งร้าน ไม่ใช่คำอธิบายประกอบ
   เลขที่คำสั่งซื้อในหน้าชำระเงินยังใช้ .order-ref ขนาดเดิม ไม่ถูกกระทบ */
.order-ref-member { font-size: var(--fs-h3); }

.order-grid {
  display: grid;
  gap: 1.25rem;
  align-items: start;
}

/* การ์ดเป็นสีขาวเสมอตามระบบออกแบบ ไม่ว่าจะวางอยู่บนพื้นเทาระดับไหน */
.order-card {
  display: grid;
  gap: 1.25rem;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  background-color: var(--card);
  padding: var(--card-p);
  box-shadow: var(--shadow-card);
}

.order-card-title { font-size: var(--fs-h3); }

/* ปกในรายการนี้ใหญ่กว่าในแผงตะกร้า เพราะที่นี่คือใบสรุปก่อนจ่ายเงิน
   ลูกค้าต้องดูออกว่าจ่ายค่าเล่มไหนอยู่โดยไม่ต้องอ่านชื่อทีละบรรทัด */
.order-items .cart-item { grid-template-columns: 72px minmax(0, 1fr); }
.order-items .cart-item img { width: 72px; }

.order-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  border-top: 1px solid var(--line);
  padding-top: 1rem;
  color: var(--muted);
  font-size: var(--fs-small);
}

/* ยอดรวมในใบสั่งซื้อเป็นแดง --offer เฉดเดียวกับราคาสินค้าทั้งเว็บ
   ต่างจากยอดรวมในแผงตะกร้าที่ยังเป็นกรมท่า เพราะในใบสั่งซื้อมันคือตัวเลข
   ที่ต้องเอาไปกรอกในแอปธนาคาร ไม่ใช่แค่สรุปให้ดู
   จำเพาะสองคลาสจึงชนะ .cart-total-value ที่มีคลาสเดียว โดยไม่ต้องใช้ !important
   ขนาดยังเท่ากันทั้งสองที่ (1.5rem) เปลี่ยนแค่สี ตัวเลขเดียวกันจึงยังหนักเท่ากัน
   บนพื้นการ์ดขาวได้ 4.70:1 ผ่านเกณฑ์ 4.5:1 */
.order-total .cart-total-value { color: var(--offer); }

.order-note {
  color: var(--muted);
  font-size: var(--fs-caption);
  line-height: 1.6;
}

/* หมายเหตุใต้ตารางประวัติคำสั่งซื้อ อ่านต่อจากตารางที่จบด้วยเส้นขอบการ์ด
   จึงต้องมีระยะหายใจและตัวใหญ่กว่าหมายเหตุที่อยู่ในการ์ดของหน้าชำระเงิน

   ต้องเขียนถึงทั้งสองกรณี เพราะแถบเลขหน้าโผล่มาคั่นระหว่างตารางกับหมายเหตุ
   เมื่อประวัติยาวเกินหนึ่งหน้า ทิ้งไว้แค่ .table-scroll + .order-note
   หมายเหตุจะเสียทั้งระยะและขนาดตัวอักษรทันทีที่แถบเลขหน้าขึ้น */
.table-scroll + .order-note,
.pagination + .order-note {
  margin-top: 1.5rem;
  font-size: var(--fs-small);
}

/* ต่อจากแถบเลขหน้าใช้ระยะแคบกว่า เพราะแถบนั้นมีระยะบนของตัวเอง 2rem มาแล้ว
   บวกกับ 1.5rem เดิมจะกลายเป็นช่องว่างก้อนใหญ่คั่นกลางโดยไม่มีอะไรอยู่ในนั้น */
.pagination + .order-note { margin-top: 0.625rem; }

/* หมายเหตุที่บอกข้อจำกัดของการสั่งซื้อ ต้องสะดุดตากว่าหมายเหตุทั่วไป
   แดง --danger บนพื้นการ์ดสีขาวได้คอนทราสต์ 4.94:1 ผ่านเกณฑ์ 4.5:1 */
.order-note-danger { color: var(--danger); }

.order-qr {
  display: grid;
  justify-items: center;
  gap: 0.75rem;
  margin: 0;
  border-radius: var(--r-card);
  background-color: var(--card-soft);
  padding: 1.25rem;
}

/* คิวอาร์โค้ดมีขอบขาวรอบตัวมาในไฟล์ SVG อยู่แล้ว จึงไม่ต้องเว้นขอบซ้ำอีกชั้น
   จำกัดความกว้างไว้ 15rem เพราะกว้างกว่านี้กล้องมือถือก็ไม่ได้อ่านง่ายขึ้น */
.order-qr-code {
  width: min(15rem, 100%);
  border-radius: var(--r-md);
  overflow: hidden;
  line-height: 0;
}

.order-qr-code svg {
  display: block;
  width: 100%;
  height: auto;
}

.order-qr-fallback {
  padding-block: 2rem;
  color: var(--muted);
  font-size: var(--fs-small);
  text-align: center;
}

.order-qr-amount {
  /* กรมท่าเข้มเท่าหัวข้อ ไม่ใช่ฟ้า เพราะยอดใต้คิวอาร์ไม่ใช่ทางเดินให้กด
     เป็นตัวเลขที่ต้องอ่านเทียบกับหน้าจอแอปธนาคาร
     บนพื้น --card-soft ของกรอบคิวอาร์ได้ 17.19:1 */
  color: var(--ink);
  font-family: var(--font-head);
  /* ยอดที่ต้องโอนคือตัวเลขที่ลูกค้าต้องเทียบกับหน้าจอแอปธนาคาร จึงใหญ่ที่สุดในการ์ด
     ใช้ 1.5rem คงที่เท่ากับยอดรวมในแผงตะกร้า เพื่อให้ตัวเลขเดียวกันหนักเท่ากันทั้งเว็บ */
  font-size: 1.5rem;
}

/* ทางลัดไปช่องแนบสลิป จัดกึ่งกลางให้ตรงกับคิวอาร์ที่อยู่เหนือมัน
   .order-card เป็น grid อยู่แล้ว จึงใช้ justify-self ได้ตรง ๆ */
.order-qr-attach { justify-self: center; }

.order-copy { justify-self: start; }

/* ขั้นตอนการโอนต้องอ่านเรียงลำดับ จึงใส่เลขกำกับเอง
   เพราะ ol ทั้งเว็บถูกตัดจุดนำหน้าทิ้งไปที่กฎพื้นฐาน */
.order-steps {
  display: grid;
  gap: 0.5rem;
  counter-reset: order-step;
  color: var(--body);
  font-size: var(--fs-small);
  line-height: 1.6;
}

.order-steps li {
  display: grid;
  grid-template-columns: 1.5rem minmax(0, 1fr);
  gap: 0.625rem;
  counter-increment: order-step;
}

.order-steps li::before {
  content: counter(order-step);
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: var(--r-full);
  background-color: var(--brand-soft);
  color: var(--deep);
  font-family: var(--font-head);
  font-size: var(--fs-caption);
}

/* -- ประตูก่อนชำระเงิน --------------------------------------------------
   คำสั่งซื้อต้องผูกกับบัญชี หน้าที่ยังไม่ล็อกอินจึงหยุดอยู่ที่การ์ดใบนี้ */
/* การ์ดชวนเข้าสู่ระบบของชั้นหนังสือกับประวัติคำสั่งซื้อ
   พื้นขาววิ่งเต็มความกว้างของ .page เท่ากับการ์ดใบอื่นในหน้าเดียวกัน
   ส่วนเนื้อหาข้างในเป็นก้อนเดียวกว้าง 34rem วางไว้กลาง หัวข้อ ข้อความ และปุ่ม
   จึงกว้างเท่ากันหมดและเรียงตรงขอบเดียวกันทั้งซ้ายและขวาเหมือนเดิม
   ตัวอักษรยังชิดซ้ายตามปกติ ที่อยู่กลางคือตำแหน่งของก้อน ไม่ใช่การจัดข้อความ */
.order-gate { justify-items: center; }

.order-gate > * { width: min(34rem, 100%); }

/* ปุ่มกว้างครึ่งหนึ่งของก้อนเนื้อหา (17rem = ครึ่งของ 34rem) ไม่ใช่ครึ่งของการ์ด
   เพราะการ์ดวิ่งเต็ม .page แล้ว ครึ่งของการ์ดจะกว้างเกินจนไม่เป็นปุ่ม
   จอแคบกว่านั้นให้ยืดเต็มความกว้างที่มี ปุ่มจะได้ไม่หดจนกดยาก

   จำเพาะสองส่วนเพื่อให้ชนะทั้ง .btn-block ที่ยังติดอยู่ในมาร์กอัปและกฎความกว้าง
   ของลูกทุกตัวข้างบน โดยไม่ต้องพึ่งลำดับในไฟล์และไม่ต้องใช้ !important */
.order-gate > .btn { width: min(17rem, 100%); }

.order-gate-text {
  color: var(--body);
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

/* ใบที่รอตรวจสลิปกับใบที่ปิดแล้วไม่มีคิวอาร์ให้แสดงคู่กัน จึงเหลือคอลัมน์เดียวทุกความกว้าง
   ต้องเขียนคอลัมน์ทับด้วย ไม่ใช่แค่จำกัดความกว้าง เพราะ .order-grid ตั้งสองคอลัมน์ไว้
   ตั้งแต่ 768px ขึ้นไป (คอลัมน์ขวากว้างคงที่ 20–22rem สำหรับคิวอาร์) ถ้าไม่ทับ
   การ์ดใบเดียวจะถูกบีบให้เหลือความกว้างที่หักคอลัมน์คิวอาร์ที่ไม่มีอยู่จริงออกไปแล้ว
   จำเพาะสองคลาสเพื่อให้ชนะกฎใน @media ที่อยู่ท้ายไฟล์โดยไม่ต้องใช้ !important */
.order-grid.order-grid-single {
  grid-template-columns: minmax(0, 1fr);
  max-width: 34rem;
}

/* -- แนบสลิป ------------------------------------------------------------ */
.order-slip { display: grid; gap: 0.75rem; }

/* แถบบนเป็น sticky สูงราว 4.5rem ถ้าไม่เผื่อระยะไว้ ช่องที่ถูกลิงก์พามาจะไปนอนอยู่ใต้แถบ
   เผื่อเพิ่มอีกหน่อยให้ป้าย "ภาพสลิปการโอนเงิน" ที่อยู่เหนือช่องโผล่มาด้วย */
.order-slip #order-slip-file { scroll-margin-top: 7rem; }

.order-slip .field { margin-bottom: 0; }

/* ช่องเลือกไฟล์สูงกว่าช่องข้อความปกติ เพราะปุ่มของเบราว์เซอร์อยู่ข้างใน
   และต้องกดโดนได้ด้วยนิ้วตามเกณฑ์ 44px */
.field-file {
  min-height: 2.75rem;
  padding: 0.5rem 0.75rem;
  line-height: 1.6;
}

.field-hint {
  display: block;
  margin-top: 0.25rem;
  color: var(--muted);
  font-size: var(--fs-caption);
}

.order-slip-preview {
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 0.5rem;
}

.order-slip-preview img {
  display: block;
  width: 100%;
  max-width: 15rem;
  height: auto;
  margin-inline: auto;
  border-radius: var(--r-sm);
}

/* ส่งสลิปใหม่เป็นทางออกของคนส่วนน้อย จึงพับเก็บไว้ไม่ให้แย่งสายตา */
.order-slip-again summary {
  cursor: pointer;
  padding-block: 0.625rem;
  color: var(--body);
  font-family: var(--font-head);
  font-size: var(--fs-small);
}

.order-slip-again[open] summary { margin-bottom: 0.75rem; }

/* -- ชั้นหนังสือ กับ ประวัติคำสั่งซื้อ ------------------------------------ */
.library-meta {
  color: var(--muted);
  font-size: var(--fs-caption);
  line-height: 1.6;
}

.library-actions {
  display: flex;
  flex-wrap: wrap;
  /* ปุ่มสองใบตกบรรทัดทับกันบนการ์ดแคบ ระยะห่างจึงต้องพอให้เห็นว่าเป็นคนละปุ่ม */
  gap: 0.75rem;
  margin-top: 0.75rem;
}

/* การ์ดในชั้นหนังสือเรียงสองใบต่อแถวตั้งแต่จอเล็กสุด ปุ่มจึงกว้างราวครึ่งจอ
   ป้ายเต็ม ("ดาวน์โหลดหนังสือ" "ไฟล์ประกอบการเรียน") ตกบรรทัดกลางปุ่ม
   จอเล็กจึงเห็นแค่ครึ่งแรกของคำ ซึ่งบอกได้อยู่แล้วว่าปุ่มไหนทำอะไร
   ส่วนที่เหลือค่อยโผล่ตอนจอกว้างพอ ใน @media (min-width: 768px) ท้ายไฟล์ */
.btn-word-wide { display: none; }

/* ปุ่มแบ่งหน้าของชั้นหนังสือ — หน้าตาเดียวกับ Pagination ของหลังบ้าน แต่เป็นคนละไฟล์
   สไตล์กันตามกติกาห้ามปนฝั่ง และที่นี่ปุ่มเป็น <a> จริงเพราะเซิร์ฟเวอร์เป็นคนตัดหน้า
   ไม่ใช่ปุ่มที่สั่งจาวาสคริปต์ซ่อนแถวเหมือนตารางหลังบ้าน */
.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  margin-top: 2rem;
}

.page-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

.page-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  border-radius: var(--r-md);
  padding-inline: 0.75rem;
  color: var(--ink);
  font-family: var(--font-head);
  font-size: var(--fs-small);
  transition: background-color 0.2s ease, color 0.2s ease;
}

/* ต้องมีสองคลาสขึ้นไปเพราะกฎ a:hover ที่หัวไฟล์จำเพาะกว่า .page-btn เฉย ๆ
   ถ้าเขียนแค่ .page-btn สีตอนชี้จะกลายเป็นฟ้าลิงก์ทั่วไป */
.page-btn:hover { background-color: var(--line); color: var(--ink); }

/* หน้าที่เปิดอยู่ ประกาศหลัง :hover เพื่อให้ชนะโดยไม่ต้องใช้ !important
   สีขาวบนฟ้า --brand ได้ 5.93:1 ผ่านเกณฑ์ 4.5:1 */
.page-btn[aria-current="page"],
.page-btn[aria-current="page"]:hover {
  background-color: var(--brand);
  color: #fff;
}

/* อยู่หน้าริมสุดแล้ว ไม่มีที่ให้ไปต่อ จึงไม่ใช่ลิงก์ตั้งแต่ต้น (เป็น <span>)
   ทำให้จางลงเพื่อบอกว่ากดไม่ได้ องค์ประกอบที่ใช้งานไม่ได้ไม่อยู่ในเกณฑ์คอนทราสต์ */
.page-btn-off {
  opacity: 0.35;
  cursor: default;
}

.page-ellipsis {
  display: inline-grid;
  place-items: center;
  min-width: 1rem;
  min-height: 44px;
  color: var(--muted);
  font-family: var(--font-head);
  font-size: var(--fs-small);
}

/* ตารางบนมือถือเลื่อนแนวนอนในกล่องของตัวเอง ทั้งหน้าจึงไม่เลื่อนตาม */
.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  background-color: var(--card);
}

.order-history-table {
  width: 100%;
  min-width: 34rem;
  border-collapse: collapse;
  font-size: var(--fs-small);
}

/* nowrap ทุกช่อง กันข้อความสั้น ๆ อย่างเลขที่คำสั่งซื้อกับวันที่หักบรรทัด
   ช่องแคบให้เลื่อนแนวนอนใน .table-scroll แทน */
.order-history-table th,
.order-history-table td {
  padding: 0.75rem 1rem;
  text-align: left;
  vertical-align: top;
  white-space: nowrap;
}

.order-history-table thead th {
  border-bottom: 1px solid var(--line);
  color: var(--muted);
  font-family: var(--font-head);
  font-size: var(--fs-small);
  font-weight: 500;
}

.order-history-table tbody tr + tr th,
.order-history-table tbody tr + tr td { border-top: 1px solid var(--line-soft); }

.order-history-table tbody th {
  color: var(--ink);
  font-family: var(--font-head);
  font-weight: 500;
}

.order-history-table td { color: var(--body); }

/* ป้ายสถานะกับลิงก์อยู่คนละช่อง จัดกึ่งกลางแนวตั้งทั้งแถวให้เส้นกลางตรงกัน */
.order-history-table tbody th,
.order-history-table tbody td { vertical-align: middle; }

/* min-height 44px ให้พื้นที่กดบนมือถือครบตามเกณฑ์ ทั้งที่ตัวอักษรเตี้ยกว่านั้น */
.order-history-link {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  min-height: 44px;
  color: var(--brand-dark);
  font-size: var(--fs-small);
  white-space: nowrap;
}

.order-history-link svg { width: 16px; height: 16px; }

/* ลิงก์ไปชั้นหนังสือที่มีไอคอนนำหน้า ตัวลิงก์เป็น inline ธรรมดา ไม่ใช่ inline-flex
   เพราะ inline-flex จะเอาขอบล่างของไอคอนไปเป็นเส้นฐานของทั้งลิงก์
   ข้อความในลิงก์เลยเลื่อนหนีข้อความรอบ ๆ ที่อยู่ประโยคเดียวกัน
   ปล่อยให้ทุกคำเรียงตามเส้นฐานเดียวกัน แล้วขยับเฉพาะไอคอนลงมาให้ตรงกลางตัวอักษร
   ขนาดเป็น em ไอคอนจึงโตตามขนาดตัวอักษรของบรรทัดนั้นเสมอ */
.shelf-link svg {
  width: 1.15em;
  height: 1.15em;
  margin-right: 0.375rem;
  vertical-align: -0.24em;
}

/* คอลัมน์ยกเลิกมีแต่ไอคอนตัวเดียว จัดกึ่งกลางทั้งหัวตารางและช่องข้างล่าง
   ไม่ชิดซ้ายเหมือนคอลัมน์ที่เป็นข้อความ ไอคอนจะได้อยู่ใต้หัวคอลัมน์พอดี
   ต้องเขียนทับกฎ text-align: left ของทุกช่องในตารางนี้ จึงเจาะจงกว่าด้วยคลาส
   ผูกกับคลาสไม่ใช่ตำแหน่งของคอลัมน์ สลับลำดับคอลัมน์แล้วยังจัดกลางเหมือนเดิม */
.order-history-table .order-history-cancel {
  text-align: center;
}

/* ปุ่มยกเลิกในตารางประวัติ เห็นเป็นไอคอนกากบาทเปล่า ๆ ไม่มีกรอบไม่มีพื้น
   แต่ตัวปุ่มยังกว้าง 44×44px ตามเกณฑ์พื้นที่กดบนมือถือ นิ้วจึงกดติดเหมือนเดิม
   ใช้สีแดงเพราะเป็นการกระทำที่ลูกค้าย้อนกลับเองไม่ได้ ต้องอ่านออกว่าไม่ใช่ปุ่มธรรมดา */
.order-cancel-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background-color: transparent;
  width: 44px;
  height: 44px;
  color: var(--danger);
  cursor: pointer;
}

.order-cancel-button svg { width: 24px; height: 24px; }

/* ไม่มีกรอบให้เปลี่ยนสีแล้ว จึงบอกว่ากำลังชี้อยู่ด้วยการขยายไอคอนแทน */
.order-cancel-button:hover svg { transform: scale(1.12); }

.order-cancel-button:disabled {
  opacity: 0.5;
  cursor: default;
}

/* กล่องของ Tippy ใช้ธีมของไลบรารี ปรับแค่ตัวอักษรให้เป็นฟอนต์เดียวกับเว็บ
   พื้นเทาเข้ม #333 กับตัวอักษรขาวของธีมเดิมได้คอนทราสต์ 12.6:1 อยู่แล้ว */
.tippy-box {
  font-family: var(--font-body);
  font-size: var(--fs-caption);
  line-height: 1.6;
}

/* ป้ายสถานะ — สีทั้งชุดอยู่ที่โทเคน --status-* ในหัวข้อ 1 ที่เดียว
   แก้ที่นั่นแล้วเปลี่ยนพร้อมกันทั้งหน้าร้านและหลังบ้าน ตัวเลขคอนทราสต์กำกับไว้ตรงนั้น */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  border-radius: var(--r-full);
  padding: 0.25rem 0.625rem;
  font-family: var(--font-head);
  font-size: var(--fs-caption);
  white-space: nowrap;
}

/* จุดกลมนำหน้าทุกป้าย ชุดเดียวกับหลังบ้าน สถานะเดียวกันจึงหน้าตาเหมือนกันทั้งสองฝั่ง
   เป็นองค์ประกอบ UI ใช้เกณฑ์ 3:1 ค่าจริงอยู่ที่ 4.11–4.99:1 ทุกจุด */
.badge::before {
  content: "";
  border-radius: var(--r-full);
  width: 0.5rem;
  height: 0.5rem;
}

.badge-pending { background-color: var(--status-pending); color: var(--status-pending-ink); }
.badge-pending::before { background-color: var(--status-pending-dot); }

.badge-paid { background-color: var(--status-paid); color: var(--status-paid-ink); }
.badge-paid::before { background-color: var(--status-paid-dot); }

.badge-rejected { background-color: var(--status-rejected); color: var(--status-rejected-ink); }
.badge-rejected::before { background-color: var(--status-rejected-dot); }

.badge-cancelled { background-color: var(--status-cancelled); color: var(--status-cancelled-ink); }
.badge-cancelled::before { background-color: var(--muted); }

/* ป้ายของสองสถานะที่ร้านเขียนเหตุผลไว้ (สลิปไม่ผ่าน กับ ยกเลิกแล้ว) เป็นปุ่มจริง
   ชี้หรือแตะแล้ว Tippy.js เปิดกล่องข้อความให้ หน้าตายังเป็นป้ายเหมือนใบอื่นทุกอย่าง
   ต่างแค่เคอร์เซอร์ที่บอกว่ามีอะไรให้อ่านเพิ่ม ที่นี่ดูแลแค่ตัวปุ่มกับพื้นที่กด 44px
   ต้องอยู่หลัง .badge เพราะ .badge เป็นคนกำหนด display ของป้าย */
.status-tip {
  position: relative;
  border: 0;
  cursor: help;
}

/* ขยายพื้นที่กดด้วย ::after ที่ไม่มีสี ตัวป้ายจึงยังเล็กเท่าเดิมไม่ผิดรูป */
.status-tip::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  min-width: 44px;
  height: 44px;
}


/* 14 ── การเคลื่อนไหว ─────────────────────────────────────────────────── */
/* สถานะ "ซ่อนไว้ก่อน" ผูกกับคลาส .t2i-motion ที่ js/motion.js เป็นคนใส่ให้ <html>
   ถ้าไฟล์นั้นโหลดไม่ได้หรือพัง จะไม่มีคลาสนี้ เนื้อหาจึงแสดงครบตามปกติ
   ตรงกับหลักในเอกสารที่ว่าอนิเมชันเป็นของเสริม ไม่ใช่เงื่อนไขของการอ่านเนื้อหา */
.t2i-motion .t2i-reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.t2i-motion .t2i-reveal.is-in {
  opacity: 1;
  transform: none;
}

.t2i-word { display: inline-block; }

.t2i-motion .t2i-word {
  opacity: 0;
  transform: translateY(0.4em);
  transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.t2i-motion .t2i-word.is-in {
  opacity: 1;
  transform: none;
}

/* แบนเนอร์เข้าเป็นชุดเดียวกัน หัวข้อวิ่งทีละคำ (จบที่ 0.665 วิ)
   ข้อความกับปุ่มไหลขึ้นตามหลังแบบเหลื่อมกัน ตาจะอ่านว่าเป็นการเคลื่อนไหวครั้งเดียว
   ไม่ใช่ของสามชิ้นที่ต่างคนต่างโผล่

   ใช้ @keyframes ไม่ใช่ .t2i-reveal ที่เป็น transition เพราะแบนเนอร์อยู่ในจอ
   ตั้งแต่เปิดหน้า transition ต้องเคยวาดค่าเริ่มต้นออกไปก่อนถึงจะมีอะไรให้วิ่งจาก
   ซึ่งแข่งกับจังหวะที่ IntersectionObserver รายงานกลับมา บางรอบทันบางรอบไม่ทัน
   ผลคือบางครั้งเล่นบางครั้งไม่เล่น ส่วน @keyframes เล่นเองจากคีย์เฟรมแรกเสมอ
   ไม่ต้องมีเฟรมตั้งต้นให้เทียบ จึงได้ผลเหมือนกันทุกครั้ง

   ไม่ต้องเฝ้าดูด้วย observer เลย เพราะของที่อยู่ในจอตั้งแต่แรกไม่ต้องรอให้เลื่อนถึง */
@keyframes t2i-rise {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: none; }
}

/* both ทำให้ค้างที่ opacity 0 ระหว่างรอหน่วงเวลา ไม่งั้นจะโผล่มาก่อนแล้วค่อยขยับ */
.t2i-motion .hero-text,
.t2i-motion .hero-actions {
  animation: t2i-rise 0.6s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
.t2i-motion .hero-text { animation-delay: 0.22s; }
.t2i-motion .hero-actions { animation-delay: 0.34s; }

/* ข้อความแบนเนอร์สะกิดออกแล้วไหลกลับ ตอนภาพพื้นหลังครอสเฟด
   อยู่ใต้ .t2i-motion เพราะเป็นของเสริมล้วน ถ้า js/motion.js ไม่ทำงาน
   คลาสนี้จะไม่ถูกใส่ ข้อความก็อยู่นิ่งตามปกติ ไม่มีอะไรขาดหาย */
.t2i-motion .hero-content {
  transition: transform 0.7s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.t2i-motion .hero-content.is-shifting {
  transform: translateX(-10px);
  transition-duration: 0.18s;
}

[data-t2i-parallax] { will-change: transform; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  /* ตัดแค่การวิ่งของเส้น ตัวติ๊กถูกยังอยู่ครบ เพราะมันคือสถานะ ไม่ใช่ของประดับ */
  .btn-check { animation: none; }

  .t2i-motion .t2i-reveal,
  .t2i-motion .t2i-word {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .book-card,
  .book-cover img {
    transition: none;
  }

  /* การซูมยังทำงานเพราะเป็นหน้าที่ ไม่ใช่ของประดับ ตัดแค่ช่วงที่ไหลเข้าที่ */
  .gallery.is-ready .gallery-image,
  .gallery-thumb {
    transition: none;
  }

  .gallery-strip { scroll-behavior: auto; }
}

/* 15 ── จอใหญ่ขึ้น (min-width เท่านั้น) ───────────────────────────────── */
@media (min-width: 360px) {
  .hero-title { font-size: 1.75rem; }
  .hero-text { font-size: 0.6875rem; }

  .hero-actions .btn {
    padding-inline: 1.25rem;
    font-size: 0.875rem;
  }
}

@media (min-width: 480px) {
  .hero-title { font-size: 1.875rem; }
  .hero-text { font-size: 0.8125rem; }

  .hero-actions .btn {
    min-height: 48px;
    padding-inline: 1.5rem;
    font-size: 0.9375rem;
  }
}

@media (min-width: 640px) {
  :root {
    --fs-h1: 1.875rem;
    --fs-h2: 1.875rem;
    --fs-h3: 1.125rem;
    --t2i-shift: 48px;
  }

  .detail-actions { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .modal { align-items: center; }

  .hero-title { font-size: 2.125rem; }

  .hero-text { font-size: 1rem; }

  .hero-actions .btn {
    min-height: 52px;
    padding-inline: 1.75rem;
    font-size: 1rem;
  }

  .offer-art { height: 18rem; }
}

@media (min-width: 768px) {
  /* ใบสรุปรายการอยู่ซ้าย คิวอาร์อยู่ขวา — คอลัมน์ขวากว้างคงที่พอดีตัวคิวอาร์
     ต่ำกว่านี้เรียงลงมาเป็นคอลัมน์เดียว รายการที่จ่ายมาก่อน แล้วค่อยถึงคิวอาร์ */
  .order-grid { grid-template-columns: minmax(0, 1fr) 20rem; }

  /* แท็บเล็ตได้สามคอลัมน์ คั่นระหว่างสองใบต่อแถวของจอเล็กกับสี่ใบต่อแถวที่ 1024px
     ต่ำกว่านี้การ์ดสามใบจะแคบจนปกอ่านไม่ออก ส่วนช่วงนี้ถ้าปล่อยไว้สองใบ
     การ์ดจะกว้างเกือบครึ่งจอจนดูโหว่ ใช้กับทั้งหน้าแรกและชั้นหนังสือ
     เพราะสองหน้านี้ใช้ตารางการ์ดชุดเดียวกันมาแต่ต้น */
  .book-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }

  /* คอลัมน์แบรนด์กว้างเป็นสองเท่าของคอลัมน์ติดต่อ เพื่อให้ประโยคแนะนำร้าน
     ลงพอดี 2 บรรทัด ส่วนคอลัมน์ติดต่อมีแค่เบอร์กับอีเมลจึงไม่ต้องการที่มาก */
  .footer-grid { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }

  .stat-item {
    display: flex;
    flex-direction: column-reverse;
  }

  .stat-value { font-size: 1.875rem; }

  .hero-title { font-size: 2.5rem; }

  .hero-text { font-size: 1.125rem; }

  .hero-actions .btn {
    min-height: 56px;
    padding-inline: 2rem;
    font-size: 1.0625rem;
  }

  /* inline ไม่ใช่ inline-block เพราะเป็นครึ่งหลังของคำเดียวกัน
     ต้องไหลต่อจากครึ่งแรกในบรรทัดเดียวกันเหมือนไม่เคยถูกแยก */
  .btn-word-wide { display: inline; }
}

@media (min-width: 1024px) {
  :root {
    --section-y: 2rem;
    --gutter: 2rem;
    --t2i-shift: 88px;
  }

  .site-nav { display: block; }

  .header-tools .header-auth { display: inline-flex; }

  .header-menu-toggle { display: none; }

  /* เมนูของจอเล็กห้ามโผล่บนเดสก์ท็อปไม่ว่า menuOpen จะค้างเป็น true อยู่หรือไม่
     เพราะพอกว้างเกินจุดนี้ ปุ่มที่ใช้ปิดมันหายไปแล้ว เปิดค้างไว้ตอนจอแคบ
     แล้วขยายจอจึงเคยได้เมนูกางขวางกลางหน้าโดยไม่มีอะไรปิดได้
     x-show ตอนเปิดคือการถอด inline style ออก กฎนี้จึงชนะได้โดยไม่ต้องใช้ !important */
  .mobile-nav { display: none; }

  .book-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1.5rem 1.25rem; }

  /* ปุ่มบนการ์ดเตี้ยลงเหลือ 36px เฉพาะจอนี้ขึ้นไป ซึ่งใช้เมาส์เป็นหลัก
     ต่ำกว่า 1024px ยังคง 44px ตามกฎพื้นที่กดบนมือถือ ห้ามลดตาม */
  .book-foot .btn {
    min-height: 36px;
    padding-block: 0.375rem;
  }

  .hero-stage { min-height: 36rem; }

  /* จอกว้างให้แถบพื้นสั้นลง เพื่อเห็นภาพด้านขวาได้มากขึ้น */
  .hero-artwork::after {
    background: linear-gradient(90deg, var(--surface) 0%, rgb(248 250 252 / 0.98) 27%, rgb(248 250 252 / 0.62) 49%, transparent 70%);
  }

  .hero-artwork img { object-position: center; }

  .hero-content {
    align-items: flex-start;
    justify-content: center;
    min-height: 36rem;
    padding-block: 5rem;
  }

  .hero-title { font-size: 3rem; }

  .hero-content .hero-text {
    max-width: 100%;
    font-size: 1.25rem;
  }

  .hero-actions .btn { font-size: 1.125rem; }

  /* 24rem ไม่ใช่ 20rem เพราะแถบภาพย่อกินไป 3.5rem บวกช่องไฟ
     ภาพใหญ่จึงยังกว้างเท่าเดิมกับตอนที่ยังเป็นปกใบเดียว */
  .detail-grid { grid-template-columns: 26rem minmax(0, 1fr); gap: 3rem; }

  .order-grid { grid-template-columns: minmax(0, 1fr) 22rem; gap: 2rem; }

  .gallery { max-width: 100%; }

  .gallery.is-ready { grid-template-columns: 6rem minmax(0, 1fr); }

  .parallax-inner {
    grid-template-columns: minmax(0, 0.9fr) minmax(24rem, 1.1fr);
    align-items: center;
    column-gap: 3rem;
    text-align: left;
  }

  .parallax-text { margin-inline: 0; }

  .parallax-title strong { font-size: 3rem; }

  .offer-art {
    height: 22rem;
    border-width: 10px;
  }

  .stat-grid {
    grid-column: 1 / -1;
    width: 100%;
  }
}
