/* ============================================================
   Nutcharin Dryclean & Laundry — base stylesheet
   mobile-first. เลย์เอาต์ <768px = แบบมือถือที่อนุมัติแล้ว (2a)
   ส่วนเดสก์ท็อปอยู่ในไฟล์ styles/desktop-d2.css (>=1000px)

   🔴 อ่านก่อนเชื่อคอมเมนต์ในไฟล์นี้ — อัปเดต 29 ส.ค. 2569
   d1.html / d3.html / styles/desktop-d1.css ถูก "ถอดออกจากโปรเจกต์แล้ว"
   (ย้ายเข้า backup/finalize-index-20260829-121129/ · ไม่ได้ลบ) และ d2.html ถูก rename
   เป็น index.html ⇒ ตอนนี้มีหน้าเดียวคือ index.html ที่ใช้ไฟล์นี้
   ⇒ คอมเมนต์ทุกบรรทัดข้างล่างที่เขียนทำนอง "d1/d3 ยังใช้อยู่ ห้ามลบ" หรือ "มีเฉพาะใน d2.html"
     เป็น "บันทึกตอนยังมีสามไฟล์" ไม่ใช่ข้อจำกัดที่ยังบังคับอยู่ — เก็บไว้เพราะยังอธิบาย
     "ทำไมกฎถึงเขียนแบบนี้" ได้ดี แต่ห้ามเอาไปอ้างว่า "ยังมีไฟล์อื่นใช้"
   ⇒ ผลข้างเคียง: กฎที่เคยมีไว้เพื่อ d1/d3 อย่างเดียว (.field/.select/.price-result/.price-table
     รวมถึงแถวไอคอนแนวนอนของ .step) กลายเป็น dead CSS
     ✅ 2 ก.ย. 2569 — เก็บกวาดแล้ว (บรรทัดนี้เคยเขียนว่า "ยังไม่ลบ" ซึ่งไม่จริงอีกต่อไป)
     กฎ .field/.select/.price-* ถูกลบหมด · เหลือแถวไอคอนแนวนอนของ .step ที่ยังอยู่
     เพราะเป็นฐานที่บล็อกมือถือ override ทับ — ลบแล้วต้องรื้อ @media ตามด้วย
   ============================================================ */

/* ---------- tokens (จาก design system: tokens/colors.css) ---------- */
:root{
  --green-700:#00714A;  /* brand green: buttons, headings, logo */
  --green-800:#005C39;  /* primary hover */
  --green-600:#00854F;  /* button green (white type 4.7:1) */
  --green-500:#00AB6A;  /* accent: rules, icon strokes — NOT small text */
  --green-300:#92CEA8;
  --green-200:#B9E0CB;
  --green-50:#DFF2E7;
  --paper:#FBFCFA;
  --white:#FFFFFF;
  --ink-900:#1F2A22;
  --ink-600:#4F5C52;
  --line-200:#D2E8DA;
  --line-green:#06C755;  /* LINE brand green — LINE mark ONLY */

  --surface-page:var(--paper);
  --surface-card:var(--white);
  --surface-sub:var(--green-50);
  --surface-invert:var(--green-700);
  --text-body:var(--ink-900);
  --text-muted:var(--ink-600);
  --text-brand:var(--green-700);
  --border-card:var(--line-200);

  --font-ui:'Noto Sans Thai',system-ui,-apple-system,'Segoe UI',sans-serif;
  --font-seal:'Cormorant Garamond',Georgia,serif;

  --radius-card:20px;
  --radius-sub:14px;
  --radius-pill:9999px;
  --shadow-card:0 2px 12px rgba(0,113,74,.05);
  --shadow-raised:0 8px 24px rgba(0,113,74,.08);
  --ease:cubic-bezier(.2,.6,.2,1);
  --gutter:24px;
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--surface-page);color:var(--text-body);
  font-family:var(--font-ui);font-size:16px;line-height:1.72;
  overflow-x:hidden;max-width:100%;
}
/* กัน grid/flex item ดันหน้าให้กว้างเกินจอ — ค่าเริ่มต้น min-width:auto ทำให้ย่อต่ำกว่า
   min-content ไม่ได้ ตารางราคาจึงเคยดันทั้งหน้าให้ล้นบนมือถือ */
/* ⚠️ 2 ก.ย. 2569 ถอด .stack .stack--sm .tools__group .hero__inner .hero__text ออกจากรายการนี้
   (คลาสทั้งห้าถูกลบไปในรอบเก็บกวาดแล้ว) เหลือเฉพาะสองตัวที่ยังมี element จริงในหน้า */
.card,.feature > div{min-width:0}
img{max-width:100%;display:block}
a{color:var(--green-700)}
a:hover{color:var(--green-800)}
h1,h2,h3{color:var(--text-brand);font-weight:700;margin:0}
h1{font-size:34px;line-height:1.3;letter-spacing:-.01em;text-wrap:pretty}
h2{font-size:22px;line-height:1.34}
h3{font-size:19px;line-height:1.4}
p{margin:0}
.lead{font-size:17px;line-height:1.62;color:var(--text-muted)}
.small{font-size:15px;line-height:1.66;color:var(--text-muted)}
.caption{font-size:13px;line-height:1.6;color:var(--text-muted)}
.center{text-align:center}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
/* ⚠️ การ์ดที่ "อุ้ม" .sr-only ไว้ ต้องเป็น positioning context ด้วยเสมอ
   บั๊กที่แก้ (26 ส.ค. 2569): .sr-only เป็น position:absolute แต่การ์ด #steps ไม่มี position
   ⇒ containing block ตกไปเป็น ICB (ทั้งหน้า) ไม่ใช่การ์ด · มันจึงไม่ถูก .tools__track
     (overflow-x:auto) ตัด แล้วไปโผล่ที่ x=432 ตามตำแหน่ง static ของแผงที่ 2 ในราง
   ⇒ หน้าเว็บที่ 390px เลื่อนซ้าย-ขวาได้ 43px (scrollWidth 433 vs viewport 390)
   :has(> .sr-only) = ยิงเฉพาะการ์ดที่มี .sr-only เป็นลูกตรง ซึ่งมีแต่ใน d2.html เท่านั้น
   (d1/d3 ไม่มี .sr-only สักจุด ตรวจแล้ว) ⇒ ไม่ไปแตะสองไฟล์นั้นแม้จะใช้ base.css ร่วมกัน
   ⚠️ ห้ามเปลี่ยน .sr-only เป็น display:none เพื่อเลี่ยงปัญหานี้ — ข้อความ 4 ขั้นตอน/รายการบริการ
      ทั้งหมดอยู่ในรูปภาพ ก้อน sr-only คือตัวเดียวที่ Google กับ screen reader อ่านได้
   ⚠️ .services .card มี position:relative อยู่แล้ว (base.css บล็อก services) กฎนี้จึงไม่เปลี่ยนอะไรที่นั่น */
.card:has(> .sr-only){position:relative}

/* ---------- พื้นผิว (texture) — หัวใจของแนวทาง 1C ---------- */
.bg-marble,.leaf-edge{position:fixed;pointer-events:none;z-index:0;mix-blend-mode:multiply}
.bg-marble{
  /* ⚠️ .webp แปลงมาจาก texture-marble-tile.png ด้วย npm run images (26 ส.ค. 2569)
     208 KB → 3.1 KB · ต่างจากต้นฉบับเฉลี่ย 0.82/255 (ตาไม่เห็น) และภาพนี้อยู่ใต้ opacity .4 อีกชั้น
     ⚠️ ไฟล์ .png ยังอยู่ใน assets/ = ต้นฉบับของ build ห้ามลบ
     ⚠️ ยังเป็น cover + no-repeat เหมือนเดิม — ไฟล์นี้ไม่ใช่ tile ที่ต่อกันได้
        (วัดขอบซ้าย↔ขวาต่างกัน 21.5/255 บน↔ล่าง 19.7/255 · ของ .png เดิมคือ 21.9 / 19.4 พอกัน)
        จะเปลี่ยนเป็น repeat ได้ต่อเมื่อได้ไฟล์ seamless ตัวจริงจาก design project ดู docs/ASSETS.md */
  inset:0;background:url(../assets/texture-marble-tile.webp) center/cover no-repeat;
  opacity:.4;
}
/* ⚠️ ต้องใช้ไฟล์ -seamless (ภาพเดิม + ภาพพลิกกลับหัวต่อท้าย) เท่านั้น
   ถ้าใช้ไฟล์ธรรมดา repeat-y จะเห็นรอยต่อเป็นเส้นแนวนอนพาดจอทุกช่วงความสูงของภาพ */
.leaf-edge{
  top:0;bottom:0;width:clamp(0px,calc((100vw - 1160px)/2 + 90px),230px);
  background:url(../assets/texture-leaves-seamless.png) left top/230px auto repeat-y;
  opacity:.5;
  -webkit-mask-image:linear-gradient(to right,#000 0%,rgba(0,0,0,.85) 30%,transparent 100%);
  mask-image:linear-gradient(to right,#000 0%,rgba(0,0,0,.85) 30%,transparent 100%);
}
.leaf-edge--left{left:0}
.leaf-edge--right{right:0;transform:scaleX(-1)}
@media (max-width:999px){.leaf-edge{display:none}}
.page{position:relative;z-index:1}

/* ---------- container ---------- */
.container{width:100%;margin-inline:auto;padding-inline:var(--gutter)}
section{scroll-margin-top:90px}

/* ---------- header ---------- */
.site-header{
  position:sticky;top:0;z-index:40;
  background:rgba(251,252,250,.92);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  border-bottom:1px solid var(--border-card);
}
.site-header__inner{
  display:flex;align-items:center;justify-content:center;gap:14px;
  padding:12px var(--gutter);min-height:64px;
}
.site-nav,.site-header__hours{display:none}

/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  height:48px;padding:0 22px;border-radius:var(--radius-pill);
  font-family:inherit;font-size:16px;font-weight:500;line-height:1;
  border:1px solid transparent;cursor:pointer;text-decoration:none;
  white-space:nowrap;   /* ป้ายปุ่มห้ามตกบรรทัด */
  transition:background-color 180ms var(--ease),color 180ms var(--ease),border-color 180ms var(--ease);
}
.btn:active{transform:translateY(1px)}
/* ---------- focus ring ----------
   ⚠️ ทุกตัวที่กด Tab ถึงได้ ต้องอยู่ในรายการนี้ — เพิ่มปุ่ม/ลิงก์ใหม่เมื่อไหร่ ต้องมาเติมที่นี่ด้วย
   เดิมมีแต่ .btn ⇒ แท็บ/จุดแบนเนอร์/เมนู ได้ outline ดีฟอลต์ของเบราว์เซอร์ซึ่งหน้าตาคนละเรื่อง
   (แก้ 2 ก.ย. 2569 · ไม่เคยมีจุดไหนตั้ง outline:none ทั่วไฟล์ ⇒ ไม่มีที่ไหน focus หายสนิทอยู่แล้ว)
   ⚠️ outline:0 ต้องมาคู่กับ box-shadow เสมอ ห้ามใส่ตัวแรกเดี่ยว ๆ
   ⚠️ .hero-dot แยกกฎเพราะอยู่บนภาพแบนเนอร์ (พื้นเข้ม) ริงเขียวจะจมหาย ต้องใช้ริงขาว */
.btn:focus-visible,
.tools__tab:focus-visible,
.site-nav a:focus-visible{outline:0;box-shadow:0 0 0 3px rgba(0,113,74,.22)}
/* จุด 8px ใช้ริงบางกว่า (2px) ไม่งั้นริงหนากว่าตัวจุดเองจนดูเป็นก้อนเดียว */
.hero-dot:focus-visible{outline:0;box-shadow:0 0 0 2px rgba(255,255,255,.85)}
.btn--primary{background:var(--green-600);color:var(--white);box-shadow:0 1px 0 rgba(0,92,57,.18)}
.btn--primary:hover{background:var(--green-700);color:var(--white)}
.btn--secondary{background:var(--white);border-color:var(--green-600);color:var(--green-700)}
.btn--secondary:hover{background:var(--surface-sub);color:var(--green-700)}
/* ปุ่ม LINE — พื้นขาว ขอบเขียว LINE ตัวอักษรเขียวแบรนด์ (บนขาวได้คอนทราสต์ 6.4:1 ผ่าน AA)
   สูงกว่าปุ่มอื่นและตัวอักษรใหญ่กว่า เพราะเป็นปุ่มที่ลูกค้าต้องกดจริง */
.btn--line{
  background:var(--white);border-color:var(--line-green);border-width:2px;
  color:var(--green-700);height:56px;padding:0 28px;font-size:17px;font-weight:700;gap:10px;
}
.btn--line:hover{background:var(--surface-sub);border-color:var(--line-green);color:var(--green-700)}
.btn--line .line-mark{width:28px;height:28px}
/* ปุ่มนำทาง: ข้อความชิดซ้าย ลูกศรชิดขวา — อ่านเป็น "พาไปที่อื่น" ชัดกว่าปุ่มไอคอนกลาง */
.btn--nav{justify-content:space-between;padding-inline:20px}
.btn__lead{display:inline-flex;align-items:center;gap:10px}
.btn--nav .btn__arrow{transition:transform 180ms var(--ease)}
.btn--nav:hover .btn__arrow{transform:translateX(4px)}
.btn--block{width:100%}
.btn svg{width:20px;height:20px}
/* เครื่องหมาย LINE = ไฟล์โลโก้ทางการ (assets/line-icon.png)
   🔒 ห้ามย้อมสี ห้ามใส่ filter ห้ามบิดสัดส่วน — เป็นเครื่องหมายการค้าของ LINE
   ตัวไฟล์มีพื้นเขียว #06C755 ในตัวอยู่แล้ว จึงวางบนปุ่มสีอะไรก็อ่านออก */
.line-mark{display:block;flex:none;width:24px;height:24px}

/* ---------- cards ---------- */
.card{
  background:var(--surface-card);border:1px solid var(--border-card);
  border-radius:var(--radius-card);box-shadow:var(--shadow-card);
  padding:22px 18px;
}
.card__head{display:grid;gap:4px;margin-bottom:16px}

/* ---------- chip ---------- */
.chip{
  display:inline-flex;align-items:center;gap:8px;
  height:34px;padding:0 14px;border-radius:var(--radius-pill);
  background:var(--surface-sub);color:var(--text-body);
  font-size:14px;line-height:1;white-space:nowrap;
}
.chip svg{width:16px;height:16px;color:var(--green-700)}

/* ---------- notice box (บังคับมี ห้ามตัด) ---------- */
.notice{
  display:flex;gap:12px;align-items:flex-start;
  background:var(--surface-sub);border-radius:var(--radius-sub);
  padding:14px 18px;font-size:14px;line-height:1.66;color:var(--text-muted);
}
.notice svg{width:18px;height:18px;flex:none;margin-top:3px;color:var(--green-700)}

/* ════ ถอด "ฟอร์ม + เครื่องคำนวณราคา + ตารางราคา" ออกแล้ว (2 ก.ย. 2569) ════
   คลาสที่หายไปจากตรงนี้: .field .field__label .select .select:focus-visible
                          .price-result .price-result__value .price-result__unit
                          .price-result--empty .price-table (+ caption/th/td ทุกกฎ)
   เหตุผล: index.html ถอด panel #panel-calc ออกตั้งแต่ 25 ส.ค. 2569 (ลูกค้าเอาราคาบนเว็บ
           ไปเทียบกับหน้าร้านแล้วต้องอธิบายทุกครั้ง) และ scripts/site.js ถอดบล็อกคำนวณ
           ออกวันเดียวกับที่ลบตรงนี้ ⇒ ไม่มีใครอ้างถึงกฎชุดนี้อีกเลย
   ⚠️ ถ้าต้องเอากลับมา ลอกได้ครบทั้งชุดจาก backup/finalize-index-20260829-121129/d1.html
      (markup) + git history ของไฟล์นี้ (CSS) · กฎเดิมยังอยู่: ราคาเป็นสตริง '000' เสมอ
      จนกว่าเจ้าของร้านจะยืนยันราคาจริง ห้ามใส่ตัวเลขสมมุติ
   ℹ️ desktop-d2.css มีคู่ของบล็อกนี้ (.tools__group / .price-result__value) ถอดออกพร้อมกันแล้ว ════ */

/* ---------- icon circle / service tile / feature ---------- */
.icon-circle{
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:52px;height:52px;border-radius:var(--radius-pill);
  background:var(--surface-sub);color:var(--green-700);
}
.icon-circle svg{width:24px;height:24px}
.icon-circle--lg{width:66px;height:66px}
.icon-circle--lg svg{width:30px;height:30px}

/* วางแบบสัญลักษณ์โอลิมปิก: แถวบน 3 · แถวล่าง 2
   แถวล่างจัดกึ่งกลางก็ตกร่องกลางของแถวบนพอดีอยู่แล้ว ไม่ต้องเลื่อนเพิ่ม
   --tile / --tile-gap คุมความกว้างที่เดียว เพื่อให้ไอคอนสมดุลกับกล่อง */
.services{--tile:92px;--tile-gap:14px}
/* กิ่งใบไม้ลายเส้นจากโซน DECORATIVE ELEMENTS ของแบรนด์บอร์ด แซมมุมทแยงของการ์ดบริการ
   ใช้ลายเส้น (ไม่ใช่ภาพถ่ายเบลอ) เพราะที่ความจางระดับนี้ ลายเส้นยังอ่านออกว่าเป็นใบไม้
   กลางการ์ดยังขาวสะอาด — พื้นผิวอยู่ที่ขอบ ห้ามแข่งกับไอคอนและตัวอักษร */
/* overflow:clip ไม่ใช่ hidden — ใบไม้ ::after ด้านล่างตั้ง bottom:-38px จึงล้นออกนอกกรอบ 57px
   ถ้าใช้ hidden การ์ดจะกลายเป็น scroll container: ภาพถูกตัดเหมือนกันก็จริง แต่ scrollTop ยังขยับได้
   พอมีอะไรไป focus .sr-only ข้างใน (กด Tab / screen reader / บอต) เบราว์เซอร์จะเลื่อนการ์ดขึ้น
   โปสเตอร์เลยขยับ ขอบบน-ล่างจาก 16/16 กลายเป็น 1/33 · clip ตัดเหมือน hidden แต่ไม่สร้าง scroll container
   ⚠️ ประกาศ hidden ไว้บรรทัดเดียวกันก่อน clip โดยตั้งใจ — เบราว์เซอร์เก่าที่ไม่รู้จัก clip (Safari <16)
      จะทิ้งค่าหลังแล้วใช้ hidden ต่อ ไม่ใช่ปล่อยเป็น visible ให้ใบไม้ทะลุออกมา ห้ามลบตัวใดตัวหนึ่งทิ้ง */
.services .card{position:relative;overflow:hidden;overflow:clip}
.services .card::before,
.services .card::after{
  content:"";position:absolute;pointer-events:none;z-index:0;
  width:150px;height:202px;
  background:url(../assets/leaf-sprig.png) center/contain no-repeat;
  opacity:.13;
}
.services .card::before{left:-26px;top:-34px;transform:rotate(-18deg)}
.services .card::after{right:-30px;bottom:-38px;transform:rotate(162deg)}
.services .card > *{position:relative;z-index:1}
/* ข้อยกเว้นของบรรทัดบน: .sr-only ที่เป็นลูกตรงของการ์ดบริการต้องคง position:absolute ไว้
   ถ้าปล่อยให้โดนทับเป็น relative → clip:rect(0 0 0 0) จะไม่ทำงาน (clip ใช้ได้เฉพาะ absolute/fixed)
   กลายเป็นกล่อง 1px ที่ยังกินที่ในโฟลว์ ดันความสูงการ์ดเกินมา ~15px
   (d1.html/d3.html ไม่มี .sr-only เลย กฎนี้จึงมีผลกับ d2.html เท่านั้น) */
.services .card > .sr-only{position:absolute}
.services__ring{display:grid;gap:var(--tile-gap);justify-items:center}
.service-tile{display:grid;gap:10px;justify-items:center;align-content:start;text-align:center;width:var(--tile)}
.service-tile__label{
  font-size:16px;line-height:1.4;font-weight:700;color:var(--text-body);
  min-height:calc(2 * 1.4em);   /* จองที่ 2 บรรทัด ทุกไทล์จึงสูงเท่ากัน ไอคอนเรียงตรงแนว */
  display:flex;align-items:flex-start;justify-content:center;text-wrap:balance;
}
/* ── ไทล์บริการ "แบบมีคำอธิบาย" — ใช้ใน d2.html ที่ <1000px เท่านั้น ──
   d1/d3 ใช้ .services__ring เปล่า ๆ ที่ไม่มีคลาส --detailed จึงไม่โดนสี่กฎล่างนี้เลย
   (เลือกใส่คลาสตรง ๆ แทน :has() เพราะ markup ชุดนี้ผมเขียนเองอยู่แล้ว อ่านง่ายกว่าและไม่ต้องพึ่ง :has())
   auto-fit + minmax(130px) = ที่ 360px ได้ 2 คอลัมน์ · จอกว้างขึ้นเพิ่มคอลัมน์เอง
   ⚠️ ทับ min-height ของ label เป็น 0 เพราะมีคำอธิบายต่อท้ายแล้ว ไม่ต้องจองที่ 2 บรรทัดให้ไอคอนตรงแนว */
/* ⚠️ 130px ไม่ใช่เลขสวย — เป็น "เลขสูงสุด" ที่ยังทำให้จอ 360px ได้ 2 คอลัมน์:
     ช่องวางกริดที่ 360px = 360 - gutter 24x2 - padding การ์ด 18x2 - border 1x2 = 274px (วัดจริงในเบราว์เซอร์ ไม่ใช่คำนวณลอย ๆ)
     gap 14px ⇒ 2x130 + 14 = 274 พอดีเป๊ะ ไม่เหลือเศษสักพิกเซล
     ⚠️ ห้ามขยับขึ้นเกิน 130px แม้แต่ 1px — 131 จะได้ 2x131+14 = 276 > 274 ⇒ จอ 360 ตกกลับเป็นคอลัมน์เดียวทันที
        (เลข 276 "พอดีที่ 360" ที่เคยว่ากันไว้ คิดจาก padding อย่างเดียว ลืมหัก border 1px สองข้างของ .card)
     ⚠️ และห้ามลดต่ำกว่านี้มาก (เช่น 111px) — จอกว้างจะแตกคอลัมน์เยอะเกินจนไอคอนเล็กลง
        จอ 320px ยอมให้เหลือคอลัมน์เดียว เครื่องกว้างเท่านั้นแทบไม่เหลือใช้ในไทยแล้ว
     คอลัมน์ที่วัดได้จริง: 320=1 · 360=2 · 390=2 · 480=2 · 600=3 · 768=4 · 999=5 · ≥1000 สลับไปใช้โปสเตอร์แทน
     (≥768px gap เปลี่ยนเป็น 28px ตาม .services{--tile-gap} ในบล็อกแท็บเล็ต ⇒ ได้คอลัมน์เท่าเดิมกับตอน 140px) */
.services__ring--detailed{grid-template-columns:repeat(auto-fit,minmax(130px,1fr));justify-items:stretch;gap:20px var(--tile-gap)}
.services__ring--detailed .service-tile{width:auto}
.services__ring--detailed .service-tile__label{min-height:0}
.service-tile__body{font-size:14px;line-height:1.66;color:var(--text-muted);margin:0;text-wrap:pretty}

/* โปสเตอร์ "บริการของเรา" — ใช้ใน d2.html เท่านั้น (d1/d3 ยังเป็นไอคอนวงกลมตามกฎด้านบน)
   ⚠️ กฎ .services__ring / .services__row / .service-tile / .service-tile__label ด้านบน "ห้ามคอมเมนต์ทิ้ง"
      d1.html กับ d3.html ยังใช้อยู่ — ที่ปิดได้มีแค่ตัวที่อยู่ใน styles/desktop-d2.css
   เต็มความกว้างกรอบการ์ด (ในกรอบ padding var(--poster-pad)) ปล่อยความสูงไหลตามไฟล์ (1.7380:1)
   ห้ามใส่ aspect-ratio หรือ object-fit:cover ไม่งั้นจะเฉือนภาพ
   ⚠️ height:auto จำเป็นเพื่อล้าง attribute height="897" ไม่งั้นความสูงถูกล็อกที่ 897px
   ⚠️ ห้ามใส่ max-width — ตัวจำกัดความกว้างคือกรอบการ์ด (.card + .services .card) เหมือนการ์ดขั้นตอน/แผนที่
      ภาพหลัง extract กว้าง 1559px จึงยังไม่ถูกขยายที่ slot 1088px ของเดสก์ท็อป (slot = การ์ด 1120 ลบ pad 16x2 · การ์ดไม่มี border แล้ว)
      ⚠️ กรอบเขียวอ่อนในไฟล์ถูกตัดทิ้งแล้ว (25 ส.ค. 2569) กรอบที่เห็นในหน้าคือ padding ขาวของการ์ดล้วนๆ */
.service-poster{
  width:100%;height:auto;display:block;
}

.feature{display:flex;gap:14px;align-items:center}
.feature__title{font-size:16px;font-weight:700;color:var(--text-body)}
.feature__body{font-size:14px;line-height:1.66;color:var(--text-muted)}

/* ---------- steps ---------- */
/* กล่องขั้นตอน = พื้นขาว ขอบเขียวจาง · วางบนพื้นเขียวอ่อนของการ์ด (สลับจากเดิมที่กล่องเป็นเขียว) */
.step{display:flex;gap:14px;align-items:center;background:var(--white);border-radius:var(--radius-sub);padding:14px 16px;border:1.5px solid var(--border-card)}
.step__icon{width:44px;height:44px;flex:none;object-fit:contain}
/* ไอคอนขั้นที่ 1 เป็นทรงสูงแคบ (สัดส่วน 0.69) พอ contain ในกรอบสี่เหลี่ยมจะถูกย่อตามด้านสูง
   เหลือความกว้างจริงน้อยกว่าตัวอื่น ~18% จึงขยายกรอบเฉพาะตัวนี้ให้ดูเท่ากันด้วยตา */
.step__icon[src$="step1.webp"]{width:52px;height:52px}
.step__num{
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:32px;height:32px;border-radius:var(--radius-pill);
  background:var(--green-700);color:var(--white);font-size:15px;font-weight:700;
}
.step svg{width:24px;height:24px;color:var(--green-700);flex:none}
.step__label{font-size:17px;line-height:1.5;font-weight:700;color:var(--text-body);text-wrap:balance}
/* คำอธิบายใต้หัวข้อในการ์ดขั้นตอน — โผล่เฉพาะ <1000px (≥1000px ทั้งการ์ดถูกซ่อน ใช้โปสเตอร์แทน)
   เล็กกว่าและจางกว่า .step__label หนึ่งระดับ ใช้ค่าชุดเดียวกับ .feature__body เพื่อให้ทั้งหน้าเป็นระบบเดียว
   ⚠️ เดิมบรรทัดนี้เป็น display:none · ไม่มีไฟล์ไหนนอกจาก d2.html ใช้คลาสนี้เลย (d1/d3 = 0 จุด)
      เปลี่ยนตรงนี้จึงไม่กระทบสองไฟล์นั้น */
.step__body{font-size:14px;line-height:1.66;font-weight:400;color:var(--text-muted);margin:0}
/* กล่องข้อความในการ์ดขั้นตอน = หัวข้อ + คำอธิบาย เรียงลงมา
   (.step เป็น flex แนวนอน: เลข → ไอคอน → กล่องนี้) · min-width:0 กันข้อความยาวดันกล่องจนล้น */
.step__text{display:grid;gap:2px;min-width:0}
/* ── การ์ด 4 ใบของ d2 — คืนกลับมา 26 ส.ค. 2569 ใช้ที่ <1000px เท่านั้น ──
   ≥1000px ยังเป็นโปสเตอร์แผงเดียวเหมือนเดิม · ตัวสลับสองฝั่งอยู่ที่
   บล็อก "สลับการ์ด/ไอคอน ↔ โปสเตอร์" ท้ายไฟล์นี้ กับ styles/desktop-d2.css
   ℹ️ 2 ก.ย. 2569 — คอมเมนต์เดิมตรงนี้เขียนว่า "ห้ามคอมเมนต์ทิ้ง เพราะ d1.html กับ d3.html ยังใช้อยู่"
      ซึ่ง **ไม่จริงแล้ว**: d1/d3 ถูกถอดออกจากโปรเจกต์ตั้งแต่ 29 ส.ค. (ย้ายเข้า backup/)
      กฎ .step / .step__num / .step__icon / .step__label ยังต้องเก็บอยู่ดี — แต่เพราะ index.html
      ใช้เองโดยตรง ไม่ใช่เพราะสองไฟล์ที่ไม่อยู่แล้ว
   🔴 ถอด .steps__arrow ออกแล้ว (2 ก.ย. 2569) — เดิมเป็น display:none "เก็บไว้เผื่อเอาเลย์เอาต์
      แนวนอนกลับมา" แต่กฎ display:none ของคลาสที่ไม่มีใน markup เลยนั้นไม่ได้ป้องกันอะไรจริง
      และ desktop-d2.css ก็มี .steps__arrow{display:inline-flex} ที่ override มันอยู่แล้ว
      ถ้าเอาเลย์เอาต์แนวนอนกลับมา ต้องมาไล่ตั้งค่าใหม่ทั้งชุดอยู่ดี — ลอกได้จาก git history */
.steps__row{display:grid;gap:12px}
.steps__head{text-align:left}

/* ── จัดองค์ประกอบการ์ด 4 ขั้นตอนใหม่ (27 ส.ค. 2569) · <1000px เท่านั้น ──────────────
   ปัญหาเดิม: .step เป็น flex แนวนอน "เลข → ไอคอน → ข้อความ" ⇒ ของสองก้อนแรกกินความกว้างตายตัว
   137px (num 32 + gap 14 + icon 44 + gap 14 + padding 32 + border 3 · ใบ 1 = 145px เพราะไอคอน 52)
   ที่ 390px การ์ดกว้าง 304 ⇒ คอลัมน์ข้อความเหลือ 166px = 54.6% ของการ์ด ⇒ หัวข้อตกสองบรรทัด
   และคำบรรยายตกทีละคำ · ที่ 360px เหลือ 136px = 49.6% ยิ่งหนักกว่า

   ทางแก้: เรียงในการ์ดเป็นแนวตั้ง จัดกึ่งกลาง — ไอคอน → หัวข้อ → คำบรรยาย
   แล้วย้ายวงกลมเลขไปลอยทับมุมบนซ้ายด้วย position:absolute ⇒ ไม่กินความกว้างในแถวอีกต่อไป
   ⇒ คำบรรยายได้เต็มความกว้างในกรอบการ์ด (การ์ด − padding 16x2 − border 1.5x2) = 87-95% ทุกช่วงจอ

   🔴 ทุกกฎในบล็อกนี้ scope ด้วย .steps__row เสมอ — d1.html/d3.html ใช้ .step / .step__num /
      .step__label ร่วมกันแต่ "ไม่มี .steps__row เลยสักจุด" (ตรวจแล้วทั้งสองไฟล์ = 0)
      ⇒ สองไฟล์นั้นยังเป็นแถวแนวนอนเหมือนเดิมทุกพิกเซล · ห้ามถอด .steps__row ออกจาก selector เด็ดขาด
   ⚠️ ต้องอยู่ใน max-width:999px — ที่ ≥1000px .steps__row{display:none} (styles/desktop-d2.css)
      โชว์โปสเตอร์แทน · บล็อกนี้จึงไม่มีผลกับเดสก์ท็อปอยู่แล้ว แต่กันไว้ไม่ให้หลุดไปทับของที่นั่น
   ⚠️ ค่า gap ทั้งหมดหยิบจากชุดที่หน้านี้ใช้อยู่แล้ว ไม่ได้ตั้งเลขใหม่:
      20px / 10px = ชุดเดียวกับ .services__ring--detailed + .service-tile ซึ่งเป็นโครงแบบเดียวกันเป๊ะ
      (ไอคอน → หัวข้อ → คำบรรยาย จัดกึ่งกลาง) · 8px มีอยู่แล้วในชุด gap ของไฟล์นี้ */
@media (max-width:999px){
  /* ระยะ "ระหว่างการ์ด" ต้องมากกว่าระยะ "ในการ์ด" เสมอ เพื่อให้สายตาจับกลุ่มถูกว่าอะไรอยู่ใบเดียวกัน
     20px (ระหว่าง) > 10px (หัวข้อ→คำบรรยาย) > 8px (ไอคอน→หัวข้อ)
     ⚠️ grid-auto-rows:1fr = ตัวที่ทำให้การ์ดทั้ง 4 สูงเท่ากัน — แถวทุกแถวถูกยืดเท่าใบที่สูงที่สุด
        แล้ว .step ได้ align-self:stretch เป็นค่าเริ่มต้นของ grid item อยู่แล้ว จึงเต็มแถวเอง
        (ห้ามเปลี่ยนเป็น auto — การ์ดจะกลับไปสูงไม่เท่ากันตามจำนวนบรรทัดของแต่ละใบ) */
  .steps__row{gap:20px;grid-auto-rows:1fr}

  .steps__row .step{
    position:relative;              /* กรอบอ้างอิงของ .step__num ที่ลอยอยู่ */
    flex-direction:column;
    align-items:center;
    justify-content:flex-start;     /* เนื้อหาชิดบน ใบที่เตี้ยกว่าจึงไม่ลอยกลางกรอบที่ถูกยืด */
    text-align:center;
    gap:8px;                        /* ไอคอน → กล่องข้อความ (ชิดกว่าระยะในกล่องข้อความ) */
  }

  /* วงกลมเลข: ลอยทับมุมบนซ้าย ไม่กินที่ในโฟลว์
     top/left ใช้ตัวเลขเดียวกับ padding ของ .step (14px 16px) ⇒ ขอบบนของเลขเสมอกับขอบบนของไอคอนพอดี
     ⚠️ ไอคอนอยู่กึ่งกลางการ์ด (กว้าง 52px) ส่วนเลขกว้าง 32px ชิดซ้าย — ที่การ์ดแคบสุดที่รองรับ (320px)
        ขอบขวาของเลขอยู่ที่ 48px ขอบซ้ายของไอคอนอยู่ที่ ~118px ⇒ ไม่มีทางชนกัน */
  .steps__row .step__num{position:absolute;top:14px;left:16px}

  /* ไอคอนทั้ง 4 ใบขนาดเท่ากัน — พอเรียงแนวตั้งแล้วกรอบที่ไม่เท่ากันทำให้หัวข้อของแต่ละใบอยู่คนละระดับ
     ใช้ 52px = ค่าเดิมของใบที่ 1 ที่ประกาศไว้ด้านบนอยู่แล้ว ไม่ได้ตั้งเลขใหม่
     ⚠️ ต้อง override .step__icon[src$="step1.webp"] ตรงๆ ด้วย ไม่งั้นกฎเดิม (0,2,0) ชนะ
     ⚠️ object-fit:contain จากกฎเดิมยังทำงาน ⇒ ภาพไม่ถูกบีบ/ยืด แค่ใส่กรอบสี่เหลี่ยมให้เท่ากัน
        สี่ใบสัดส่วนไม่เท่ากัน (0.70-0.90) ความกว้างที่เห็นจริงจึงต่างกันตามไฟล์ แต่ "ความสูงเท่ากัน"
        ซึ่งเป็นตัวที่กำหนดระดับของหัวข้อที่อยู่ถัดลงมา */
  .steps__row .step__icon,
  .steps__row .step__icon[src$="step1.webp"]{width:52px;height:52px}

  /* width:100% จำเป็น — .step เป็น column flex + align-items:center ⇒ ลูกได้ขนาดตามเนื้อหา
     ไม่ได้เต็มความกว้างเอง ถ้าไม่ใส่ คำบรรยายจะยังแคบเหมือนเดิม */
  .steps__row .step__text{gap:10px;width:100%}

  /* กันบรรทัดสุดท้ายเหลือคำโดด — ถึงกล่องจะกว้างขึ้นแล้ว ใบที่ 4 ยังตัดเป็น
     "…ช่วยให้รับผ้าได้ตรง / เวลา" (วัดจริงที่ 390px) = อาการ "ตกทีละคำ" ที่เจ้าของแจ้งมาเป๊ะๆ
     pretty ดึงคำขึ้น-ลงเฉพาะเพื่อไม่ให้บรรทัดท้ายสั้นเกินไป ⇒ ได้ "…รับผ้าได้ / ตรงเวลา" แทน
     ใช้ค่าเดียวกับ .service-tile__body (บรรทัด 295) ซึ่งเป็นคอมโพเนนต์พี่น้องกัน หน้านี้ใช้อยู่แล้ว
     ⚠️ เคยลอง balance ก่อน — กันคำโดดได้เหมือนกัน แต่ไปตัดคำประสมกลางคำ
        ("ตามประเภทเนื้อ / ผ้า" · "ไม่ต้องคอยมารับ / กลับ") ซึ่งอ่านแย่กว่าเดิม จึงเลือก pretty
     ⚠️ ภาษาไทยไม่มีช่องว่างคั่นคำ เบราว์เซอร์ตัดบรรทัดด้วยพจนานุกรมของมันเอง
        ⇒ แก้ข้อความเมื่อไหร่ ต้องวัดการตัดบรรทัดใหม่ทุกครั้ง อย่าเชื่อว่ายังสวยเหมือนเดิม
     ⚠️ เบราว์เซอร์เก่าที่ไม่รู้จัก pretty จะข้ามบรรทัดนี้ไปเฉยๆ ได้การตัดบรรทัดแบบปกติ ไม่พัง */
  .steps__row .step__body{text-wrap:pretty}
}
/* โปสเตอร์ 4 ขั้นตอน — เต็มความกว้างกรอบการ์ด ปล่อยความสูงไหลตามไฟล์ (2.3115:1)
   ห้ามใส่ aspect-ratio หรือ object-fit:cover ไม่งั้นจะกลับไปเฉือนภาพอีก
   ⚠️ height:auto จำเป็นเพื่อล้าง attribute height="807" ไม่งั้นความสูงถูกล็อกที่ 807px
   ⚠️ ไม่มี max-width แล้ว — ตัวจำกัดความกว้างคือกรอบการ์ด (.card + .tools__track) เหมือนการ์ดแผนที่
      ภาพหลัง extract กว้าง 1846px จึงยังไม่ถูกขยายที่ slot 1088px ของเดสก์ท็อป (slot = การ์ด 1120 ลบ
      pad var(--poster-pad) 16x2 ที่ตั้งไว้ใน styles/desktop-d2.css · การ์ดสองใบนั้นถูกถอด border ออกแล้ว)
      ⚠️ กรอบเขียวอ่อนในไฟล์ถูกตัดทิ้งแล้ว (25 ส.ค. 2569) กรอบที่เห็นในหน้าคือ padding ขาวของการ์ดล้วนๆ */
.steps-poster{
  width:100%;height:auto;display:block;
}

/* ---------- hero ----------
   🔴 ถอด hero "รุ่นก่อนแบนเนอร์" ออกทั้งบล็อกแล้ว (2 ก.ย. 2569)
   คลาสที่หายไป: .hero .hero__inner .hero__text .hero__media .hero__logo .hero__photo
                 .hero__points .hero__note .hero__brand .hero__brand-logo .hero__brand-name
                 (+ .hero .eyebrow / .hero .lead ที่ต้องมี .hero เป็นบรรพบุรุษจึงตายตามไปด้วย)
   เหตุผล: index.html เปลี่ยนมาใช้ .hero-wide + .hero-carousel (แบนเนอร์เลื่อน) ตั้งแต่ 23 ส.ค. 2569
           ไม่มี element ไหนในหน้าใช้คลาสชุดนี้อีกเลย
   ⚠️ .hero__cta ด้านล่างเป็นตัวเดียวในตระกูล hero__ ที่ "ยังใช้อยู่จริง" (index.html กล่องปุ่มใต้ h1)
      อย่าลบตามไปด้วยเพราะเห็นชื่อขึ้นต้นเหมือนกัน
   ℹ️ กฎคู่ของบล็อกนี้ใน desktop-d2.css (.hero__points/.hero__point) ถอดออกพร้อมกันแล้ว
      เอากลับได้จาก git history ของสองไฟล์นี้ */
.hero__cta{display:grid;gap:10px;margin-top:24px;width:100%}

/* ---------- แถบ hero เต็มความกว้างจอ ----------
   ⚠️ ใช้ภาพถ่ายล้วน ไม่มีตัวอักษรฝังในรูปแล้ว — ข้อความทุกบรรทัดเป็น HTML จริง
   มือถือ: ภาพขึ้นก่อน แล้วตามด้วยข้อความ */
/* กินเต็มความกว้างของ body ตรงๆ — ไม่ใช้ 100vw เพราะ 100vw รวมพื้นที่ scrollbar
   ทำให้เหลือช่องว่างซ้าย-ขวาไม่เท่ากันแล้วแต่เบราว์เซอร์ */
/* พื้นแถบเป็น "เขียวเข้ม" ของแบรนด์ เพื่อถ่วงกับรูปโทนมิ้นต์สว่าง
   รูปอยู่ปลายอ่อนของแรมป์สีเขียว (#92C2A6 ≈ token green-300) แถบอยู่ปลายเข้ม (#00714A)
   ⇒ อยู่ตระกูลสีเดียวกัน แต่ต่างค่าความสว่างมากพอให้ตัดกันชัดและไม่ดูหม่น */
.hero-wide{
  position:relative;width:100%;margin-top:0;
  background:var(--green-700);
  border-bottom:1px solid rgba(0,0,0,.06);
}
/* ════ ถอด "คอมโพเนนต์แบนเนอร์เลื่อนซ้าย-ขวา รุ่นแรก" ออกทั้งชุดแล้ว (2 ก.ย. 2569) ════
   คลาสที่หายไป: .banner-slider .banner-slider__track .banner-frame (+ a/img/nth-child)
                 .banner-slider__arrow (+ --prev/--next/:hover/svg)
                 .banner-slider__dots .banner-slider__dot (+ [aria-selected])
   เหตุผล: ถูกแทนด้วย .hero-carousel / .hero-track / .hero-slide / .hero-dot ที่อยู่ถัดจากนี้
           ตั้งแต่ยุคเปลี่ยน hero (23 ส.ค. 2569) · ตัวใหม่ใช้ <picture> สลับไฟล์คนละชุดที่ 768px
           แทนการ object-position ครอปจากไฟล์เดียว และมี Ken Burns + แถบนับเวลาในจุด
   ⚠️ อย่าสับสนสองชุดนี้เวลาอ่าน git history — ชื่อคลาสคนละตระกูล (.banner-* vs .hero-*)
      แต่ทำงานเดียวกัน · ตัวที่ใช้จริงวันนี้คือ .hero-* เท่านั้น ════ */

/* ---------- แบนเนอร์ hero เลื่อนซ้าย-ขวา (ใช้ใน d2) ----------
   scroll-snap = กลไก "ปัดนิ้ว" ของผู้ใช้
   ส่วนการเปลี่ยนใบเอง (autoplay / กด dot) ไม่ใช้ scroll-behavior แล้ว แต่ tween scrollLeft
   ทีละเฟรมด้วย requestAnimationFrame ใน site.js — คุมจังหวะและ easing ได้ละเอียดกว่า
   ⚠️ ห้ามใส่ scroll-behavior:smooth ที่ .hero-track เด็ดขาด จะไปสู้กับ tween จนกระตุก
   ⚠️ ค่าปรับได้ (ระยะซูม ความยาว) มาจาก const ใน site.js ผ่าน custom property
      ค่า fallback ในไฟล์นี้มีไว้เผื่อ JS ไม่ทำงานเท่านั้น อย่าถือเป็นค่าจริง
   ⚠️ คลาสขึ้นต้น hero- ทั้งชุด เพื่อไม่ชนกับ .banner-* ของสไลเดอร์เดิมที่ยังคงอยู่ด้านบน */
.hero-carousel{position:relative}
.hero-track{
  display:flex;
  overflow-x:auto;
  scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;      /* Firefox */
  -ms-overflow-style:none;   /* Edge เก่า */
}
.hero-track::-webkit-scrollbar{display:none}   /* WebKit/Blink */
.hero-slide{
  flex:0 0 100%;min-width:0;
  scroll-snap-align:start;
  scroll-snap-stop:always;   /* ปัดแรงแค่ไหนก็ทีละใบ ไม่ข้ามใบ */
  overflow:hidden;           /* กันภาพที่ยังซูมค้างอยู่ล้นออกนอกกรอบ */
}
.hero-slide a{display:block}
/* <picture> เป็น inline ตามดีฟอลต์ ต้องบังคับเป็น block — กล่อง inline ที่ห่อ <img> display:block
   ทำให้เกิด anonymous block box และมีโอกาสได้ช่องว่างบาง ๆ ใต้ภาพ */
.hero-slide picture{display:block}
.hero-slide img{
  width:100%;
  /* กรอบผูกกับ "ความกว้างของตัว element" ไม่ใช่ viewport — จึงไม่มีปัญหา scrollbar กินความกว้าง
     แล้วทำให้สัดส่วนเพี้ยนแบบตอนใช้ height:<n>vw
     ค่าตั้งต้น 4/3 = สัดส่วนไฟล์ชุดมือถือเป๊ะ (banner-N-4x3 · ต้นฉบับ 1448x1086)
     พอถึง 768px สลับเป็น 3/1 = สัดส่วนไฟล์ชุดเดสก์ท็อปเป๊ะ (banner-N-3x1 · ต้นฉบับ 2172x724)
     ⇒ ทั้งสองช่วง cover ไม่เฉือนอะไรเลย และไม่มีแถบว่าง
     ⚠️ เส้นแบ่ง 768px ต้องตรงกับ media="(min-width:768px)" ใน <source> ของ d2.html เป๊ะ
        ถ้าเลื่อนข้างเดียวจะได้กรอบกับไฟล์คนละสัดส่วน แล้วภาพโดนเฉือน
     ⚠️ ห้ามใส่ height เป็นตัวเลข หรือ max-height กลับมา จะไปทับ aspect-ratio ทันที
        (height:auto ด้านล่างคนละเรื่อง — มีไว้ล้าง attribute ต้องคงไว้) */
  height:auto;      /* ⚠️ จำเป็น! ล้าง attribute height="585" ไม่งั้นความสูงถูกล็อกที่ 585px
                       แล้ว aspect-ratio จะไม่ทำงานเลย (ระบุครบสองด้าน = aspect-ratio ถูกเมิน) */
  aspect-ratio:4/3;
  object-fit:cover;
  display:block;
  transform:scale(var(--hero-kb-scale,1.02));   /* ท่าพัก = ซูมไว้ รอ Ken Burns คลายออก */
}
/* จอ >= 768px ใช้ไฟล์ชุด 3:1 (มาจาก <source> ตัวเดียวกัน) กรอบต้องเปลี่ยนตาม */
@media (min-width:768px){
  .hero-slide img{aspect-ratio:3/1}
}
/* พื้นสำรองเผื่อกรอบกับไฟล์ไม่ตรงสัดส่วน (เช่นวันไหนเปลี่ยนไฟล์แล้วลืมแก้ aspect-ratio)
   ค่าเฉลี่ยแถบขอบภาพจริงกว้าง 24px ของแต่ละใบ · ปกติมองไม่เห็นเพราะ cover ปิดเต็มกรอบอยู่แล้ว
   ⚠️ เปลี่ยนไฟล์ภาพเมื่อไหร่ ต้องวัดสีขอบใหม่ */
.hero-slide:nth-child(1){background:#dce1d5}   /* ใบ 1 = banner-1 ป้ายร้าน */
.hero-slide:nth-child(2){background:#dee1da}   /* ใบ 2 = banner-2 LINE OA */

/* ⚠️ ห้ามใส่ opacity/transition ที่ตัว <img> ของสไลด์เด็ดขาด — ภาพจะโปร่งจนพื้นเขียว
   var(--green-700) ของ .hero-wide ที่อยู่ข้างหลังทะลุขึ้นมาเป็นแฟลชสีเขียว
   (เห็นชัดมากบนมือถือที่ภาพเต็มความกว้างจอ) ภาพต้องทึบ 100% ทุกจังหวะ
   เคยมี .hero-overlay เป็นม่านทับตอนเปลี่ยนใบ ถอดออกแล้ว — ตัวมันเองทา --green-700 ที่ .18
   ซึ่งกลายเป็นเขียวก้อนเดียวในจอ หลังจากที่ h1/ปุ่มย้ายลงมาใต้แบนเนอร์แล้ว
   พื้นเขียวของ .hero-wide ก็ถูกปิดทับสนิทจนไม่มีรอยต่อให้ต้องกลบอีก
   ถ้าจะเอาม่านกลับมา อย่าใช้สีเขียว — พื้นรอบแบนเนอร์ตอนนี้เป็น var(--surface-page) */

/* Ken Burns — ซูมออกทางเดียว (1.06 → 1) ห้ามซูมเข้า เพราะจอกว้างภาพโดนครอบหนักอยู่แล้ว */
/* Ken Burns ซูมออก — สเกลตั้งต้นลดเหลือ 1.02 (เดิม 1.06)
   ทั้งสองใบมีตัวหนังสือและโลโก้ชิดขอบ 1.03 ขึ้นไปยังเสี่ยงเฉือน · ค่าจริงมาจาก
   KENBURNS_SCALE ใน site.js ที่ฉีดเข้ามาเป็น --hero-kb-scale */
.hero-slide.is-active img{animation:heroKenBurns var(--hero-kb-ms,6500ms) ease-out forwards}
@keyframes heroKenBurns{
  from{transform:scale(var(--hero-kb-scale,1.02))}
  to{transform:scale(1)}
}

/* ⚠️ อย่าลบ — สไตล์ .hero-caption ทั้งชุด ถูกคอมเมนต์ไว้ ไม่ใช่ dead code
   element ถูกถอดออกจาก d2.html แล้ว เพราะโปสเตอร์มีตัวหนังสือครบในตัว วางทับกันแล้วอ่านไม่ออกทั้งคู่
   ถ้าวันไหนได้ภาพชุดที่ "ไม่มีตัวหนังสือ" มา ให้เอาคอมเมนต์นี้ออกแล้วใส่ element กลับ
   (text-shadow ที่เคยเพิ่มไว้ถูกถอดตามไปด้วย เพราะไม่มีข้อความทับภาพแล้ว)
.hero-caption{
  opacity:0;transform:translateY(14px);
  transition:opacity .5s ease-out .15s,transform .5s ease-out .15s;
}
.hero-slide.is-active .hero-caption{opacity:1;transform:translateY(0)}
.hero-carousel > .hero-caption{
  position:absolute;z-index:4;
  left:0;right:0;bottom:0;
  background:none;
  opacity:1;transform:none;transition:none;
  display:flex;flex-direction:column;align-items:stretch;
  gap:10px;
  padding:0 var(--gutter) 20px;
  text-align:left;
}
.hero-carousel > .hero-caption .hero-wide__brand{
  align-self:flex-start;margin:0;padding:0;gap:6px;
  background:none;
  border-radius:0;
  box-shadow:none;
}
.hero-carousel > .hero-caption .hero-wide__logo{
  height:44px;
  filter:drop-shadow(0 1px 5px rgba(0,0,0,.55));
}
.hero-carousel > .hero-caption .hero-wide__name,
.hero-wide .hero-carousel > .hero-caption .hero-wide__brand .hero-wide__name{
  font-size:19px;color:var(--white);
  text-shadow:0 1px 6px rgba(0,0,0,.7);
}
.hero-carousel > .hero-caption .icon-circle{
  background:none;
  border:1px solid rgba(255,255,255,.55);
  color:var(--white);
}
.hero-carousel > .hero-caption .btn{box-shadow:none}
.hero-carousel > .hero-caption .btn:focus-visible{box-shadow:0 0 0 3px rgba(255,255,255,.55)}
.hero-carousel > .hero-caption .eyebrow{
  margin:0;color:rgba(255,255,255,.92);
  text-shadow:0 1px 6px rgba(0,0,0,.7);
}
.hero-carousel > .hero-caption .hero-wide__tagline,
.hero-carousel > .hero-caption .hero__point{
  text-shadow:0 1px 6px rgba(0,0,0,.6);
}
.hero-carousel > .hero-caption h1{
  margin:0;font-size:26px;line-height:1.28;color:var(--white);
  word-break:keep-all;overflow-wrap:normal;
  text-shadow:0 2px 14px rgba(0,0,0,.65);
}
.hero-carousel > .hero-caption .hero-wide__tagline{margin:0;font-size:17px}
.hero-carousel > .hero-caption .hero__cta{margin-top:4px}
*/

/* จุดบอกหน้า ลอยทับมุมล่างกึ่งกลางแบนเนอร์ */
.hero-dots{
  /* มือถือ: ย้ายขึ้นมุมขวาบน เพราะครึ่งล่างเป็นที่ของข้อความและปุ่มแล้ว
     (>=768px ย้ายกลับลงมุมขวาล่าง ดูบล็อกแท็บเล็ตด้านล่าง) */
  position:absolute;top:14px;right:14px;z-index:3;
  display:flex;gap:8px;
  /* พื้นจางรองทั้งกลุ่ม แทนการใส่เงาทีละจุด — จุดขาวไม่จมหายไปกับพื้นภาพที่สว่าง
     และไม่มีเงาซ้อนกันเป็นคราบเวลาจุดอยู่ชิดกัน */
  background:rgba(0,0,0,.25);
  backdrop-filter:blur(6px);
  -webkit-backdrop-filter:blur(6px);
  border-radius:999px;
  padding:6px 10px;
}
.hero-dot{
  position:relative;overflow:hidden;   /* คุมแถบนับเวลาที่วิ่งอยู่ข้างใน */
  width:8px;height:8px;padding:0;border:0;border-radius:50%;cursor:pointer;
  background:var(--white);opacity:.5;
  transition:width 180ms var(--ease),border-radius 180ms var(--ease),opacity 180ms var(--ease);
}
/* ตัว active: แคปซูล 24px · พื้นจางลงเพื่อให้แถบขาวข้างในอ่านออกว่าวิ่งไปถึงไหนแล้ว
   ⚠️ เคยเป็น [aria-selected="true"] — เปลี่ยนเป็น aria-current เมื่อ 2 ก.ย. 2569 พร้อมกับที่ถอด
      role="tab" ออกจาก dot (aria-selected ใช้ได้เฉพาะบาง role · ปุ่มเปล่าต้องใช้ aria-current)
      JS ที่เซ็ตค่าอยู่ที่ scripts/site.js ฟังก์ชัน heroMark() — แก้ที่ไหนต้องแก้ตามกันทั้งสองที่ */
.hero-dot[aria-current="true"]{
  width:24px;border-radius:4px;opacity:1;
  background:rgba(255,255,255,.35);
}
/* แถบนับเวลา — วิ่งจนเต็มพอดีตอนที่สไลด์ถัดไปมา (ยาวเท่า HERO_INTERVAL_MS) */
.hero-dot::after{
  content:'';position:absolute;left:0;top:0;bottom:0;width:0;
  background:var(--white);border-radius:inherit;
}
.hero-dot.is-timing[aria-current="true"]::after{
  animation:heroDotProgress var(--hero-interval-ms,6000ms) linear forwards;
}
.hero-carousel.is-paused .hero-dot::after{animation-play-state:paused}
@keyframes heroDotProgress{from{width:0}to{width:100%}}

/* คำโปรยที่ย้ายลงมาจาก hero (ยาวเกินกว่าจะทับบนภาพแล้วยังอ่านออก) */
.tools__intro{max-width:720px;margin:0 auto 26px;text-align:center}

/* ---------- แถบหัวเรื่อง + ปุ่ม ใต้แบนเนอร์ ----------
   ไม่ absolute ไม่ทับภาพ · พื้นหลังใช้สีเดียวกับ section ถัดไป จึงต่อเนื่องกันเป็นผืนเดียว
   ไม่ใช่เขียวเข้มของ .hero-wide (ซึ่งตอนนี้เหลือหน้าที่แค่รองแบนเนอร์)
   padding พอประมาณ ไม่ดันเนื้อหาอื่นหลุดจอ */
.hero-headline{
  background:var(--surface-page);
  padding:20px var(--gutter) 24px;
  display:flex;flex-direction:column;align-items:center;gap:16px;
  text-align:center;
}
/* ทับกฎ .hero-wide h1{color:white} ที่ตั้งไว้ตอนหัวเรื่องยังอยู่บนพื้นเขียวเข้ม */
.hero-wide .hero-headline h1{
  margin:0;color:var(--text-brand);
  font-size:26px;line-height:1.3;
  word-break:keep-all;overflow-wrap:normal;   /* ไทยไม่มีช่องว่างระหว่างคำ ห้ามให้เบราว์เซอร์ตัดเอง */
}
/* มือถือ: ปุ่มเต็มความกว้าง ซ้อนสองแถว (.hero__cta ตัวฐานเป็น grid อยู่แล้ว) */
.hero-headline .hero__cta{margin:0;width:100%;max-width:520px}
/* ปุ่มรองกลับมาเป็นขอบเขียว — กฎ .hero-wide .btn--secondary ทำขอบขาวไว้สำหรับพื้นเขียวเข้ม
   ซึ่งจะหายไปเลยบนพื้นสว่างของแถบนี้ */
.hero-wide .hero-headline .btn--secondary{
  background:var(--white);border-color:var(--green-600);color:var(--green-700);
}
.hero-wide .hero-headline .btn--secondary:hover{
  background:var(--surface-sub);border-color:var(--green-600);color:var(--green-700);
}

/* ---------- hero ตั้งแต่ 768px ขึ้นไป ---------- */
@media (min-width:768px){
  /* จุดบอกหน้ากลับลงมุมขวาล่าง — ไม่มีข้อความทับภาพแล้ว จึงไม่ชนอะไร */
  .hero-dots{top:auto;bottom:18px;right:24px}

  .hero-headline{padding:28px var(--gutter) 32px;gap:20px}
  .hero-wide .hero-headline h1{font-size:34px}
  .hero-headline .hero__cta{
    width:auto;max-width:none;grid-auto-flow:column;justify-content:center;gap:14px;
  }
  .hero-headline .hero__cta .btn{width:auto;min-width:240px}
}

/* เคารพการตั้งค่า "ลดการเคลื่อนไหว" ของเครื่องผู้ใช้
   (JS ปิด autoplay + ข้าม tween ให้อีกชั้น · บล็อก * ท้ายไฟล์ตัด duration ให้อีกชั้น) */
@media (prefers-reduced-motion:reduce){
  .hero-track{scroll-behavior:auto}
  .hero-dot{transition:none}
  .hero-slide img{transform:none}
  .hero-slide.is-active img{animation:none}
  .hero-dot::after{display:none;animation:none}             /* ไม่มีแถบนับเวลา */
  .hero-dot[aria-current="true"]{background:var(--white)}
}

/* ⚠️ กฎบรรทัดล่างนี้ "ยัง match ของจริง" (h1 ตัวเดียวของหน้าอยู่ใน .hero-wide) แต่ถูกทับทันที
   ด้วย .hero-wide .hero-headline h1 ที่บรรทัด ~621 ⇒ เก็บไว้เพราะไม่ใช่ dead code
   ถ้าวันไหนเอาหัวเรื่องกลับขึ้นไปทับบนพื้นเขียวเข้ม กฎนี้คือตัวที่ทำให้มันขาว */
.hero-wide h1{color:var(--white)}
/* 🔴 ถอดออก 2 ก.ย. 2569: .hero-wide__tagline / .hero-wide .eyebrow / .hero-wide .lead /
   .hero-wide .rule / .hero-wide .hero__point — ทั้งหมดเป็นสีตัวอักษรสำหรับยุคที่ .hero-wide
   เป็น "แถบเขียวเข้มที่มีข้อความอยู่บนนั้น" · วันนี้ .hero-wide เหลือหน้าที่แค่รองแบนเนอร์
   (ดูคอมเมนต์เหนือ .hero-headline) และไม่มี .lead/.eyebrow/.rule/.hero__point อยู่ข้างในเลย */
/* ปุ่ม "เช็คราคาเบื้องต้น" บนพื้นเขียวเข้ม = พื้นขาว ตัวอักษรเขียว (เหมือนปุ่ม LINE) */
.hero-wide .btn--secondary{background:var(--white);border-color:var(--white);color:var(--green-700)}
.hero-wide .btn--secondary:hover{background:var(--surface-sub);border-color:var(--surface-sub);color:var(--green-700)}
/* ⚠️ element .hero-wide__inner ถูกถอดออกจาก d2.html แล้ว (เนื้อหาย้ายไปทับบนภาพใน .hero-caption)
   คอมเมนต์ไว้เฉยๆ ยังไม่ลบ เผื่อต้องย้อนกลับไปแบบ "แถบเขียวใต้แบนเนอร์"
   .hero-wide__inner{display:flex;flex-direction:column} */
/* ════ ถอด "แถบเขียวเข้มใต้แบนเนอร์ + การ์ดจุดเด่น" ออกทั้งชุดแล้ว (2 ก.ย. 2569) ════
   คลาสที่หายไป: .hero-wide__copy .hero-wide__brand .hero-wide__logo .hero-wide__name
                 .eyebrow--latin .hero-wide__tagline
                 .hero__point .hero__note-head .hero__note-body (+ ลูกทั้งหมด)
   เหตุผล: เนื้อหาที่กฎชุดนี้จัดหน้าให้ ถูกย้ายไปอยู่ใน .hero-headline (หัวเรื่อง + ปุ่ม ใต้แบนเนอร์
           บนพื้นสว่าง) ตั้งแต่ยุคเปลี่ยน hero · ไม่มี element ไหนใช้แล้ว
   ℹ️ desktop-d2.css มีคู่ของบล็อกนี้ (.hero-wide__* / .hero__points / .hero__point) ถอดพร้อมกันแล้ว
   ⚠️ ไม่เกี่ยวกับ .hero-wide เปล่า ๆ ซึ่งยังใช้อยู่ (เป็น section รองแบนเนอร์) ════ */

/* ---------- seal ---------- */
.seal__sub{font-size:13px}
.seal{
  display:grid;place-content:center;text-align:center;flex:none;
  width:104px;height:104px;border-radius:var(--radius-pill);
  border:1.5px solid var(--green-300);background:var(--surface-sub);color:var(--green-700);
  font-family:var(--font-seal);font-weight:600;font-size:17px;line-height:1.15;
}
.seal--green{background:var(--green-700);color:var(--white);border-color:rgba(255,255,255,.5)}

/* ---------- tools: แท็บ + สไลด์ (มือถือ) ---------- */
.tools{padding-top:30px}
.tools__tabs{display:flex;gap:9px;justify-content:center;padding:0 var(--gutter) 14px;overflow-x:auto;scrollbar-width:none}
.tools__tabs::-webkit-scrollbar{display:none}
.tools__tab{
  flex:none;width:112px;height:44px;padding:0;cursor:pointer;
  border-radius:var(--radius-pill);border:1px solid var(--border-card);
  background:var(--white);color:var(--green-700);
  font-family:inherit;font-size:14px;font-weight:500;
}
.tools__tab[aria-selected="true"]{background:var(--green-700);color:var(--white);border-color:transparent}
.tools__track{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;align-items:flex-start;scrollbar-width:none}
.tools__track::-webkit-scrollbar{display:none}
.tools__panel{flex:0 0 100%;width:100%;padding:0 var(--gutter);scroll-snap-align:start}
/* 🔴 ถอด .tools__group / .price-table__scroll / .price-table ออก 2 ก.ย. 2569
   — ทั้งสามตัวรับใช้ panel "คำนวณราคา" ที่ถูกถอดออกจาก index.html ตั้งแต่ 25 ส.ค. */
.tools__hint{margin:12px var(--gutter) 0;text-align:center}

/* การ์ดแผนที่ — มือถือ: ให้ลูกของ .map-info ไหลออกมาเป็นไอเทมเดียวกับการ์ด (display:contents)
   จะได้สั่งลำดับด้วย order ให้แผนที่แทรกอยู่กลาง (ป้ายร้าน → หัวข้อ → แผนที่ → ที่อยู่ → ปุ่ม) */
.map-card{display:flex;flex-direction:column;gap:16px}
.map-info{display:contents}
.map-card .map-brand{order:1}
.map-card .card__head{order:2;margin-bottom:0}
.map-card .map-embed{order:3}
.map-card .feature{order:4}
.map-card .btn{order:5}

/* ป้ายชื่อร้าน (ไทย+อังกฤษ) ในการ์ดแผนที่ — ให้คนที่เลื่อนมาถึงตรงนี้รู้ทันทีว่าหมุดคือร้านไหน */
.map-brand{
  display:flex;align-items:center;gap:14px;
  padding-bottom:16px;border-bottom:1px solid var(--border-card);
}
.map-brand__logo{height:60px;width:auto;flex:none}
.map-brand__name{font-size:20px;line-height:1.35;font-weight:700;letter-spacing:-.01em;color:var(--text-brand)}

/* ── <1000px: ชื่อร้านลงมาอยู่บรรทัดใหม่ใต้โลโก้ (26 ส.ค. 2569) ──
   ปัญหาเดิม: เรียงแนวนอน ⇒ ต้องการ โลโก้ 123 + gap 14 + ชื่อ 146 = 283px
   แต่ช่องในการ์ด = viewport - 84 ⇒ ที่ 360px เหลือ 137px ชื่อเลยตก 2 บรรทัดและดูชิดขอบขวา
   ที่ 375/390px ได้ 146px = "พอดีเป๊ะ เหลือ 0px" ซึ่งเสี่ยงเกินไป (ฟอนต์ fallback กว้างกว่านิดเดียวก็ตก)
   ⇒ เปลี่ยนเป็นคอลัมน์ทุกความกว้างที่ <1000px ไม่ใช่แก้เฉพาะ 360
   • align-items:flex-start = ชิดซ้าย ⇒ ขอบซ้ายโลโก้กับชื่อตรงแนวกับ h2 "แผนที่ร้าน" ที่อยู่ใต้ลงมาพอดี
     (วัดแล้วขอบซ้ายโลโก้ = ขอบซ้าย h2 อยู่ก่อนแก้แล้ว เพราะทั้งคู่เป็นลูกตรงของการ์ดใบเดียวกัน)
   • gap ไม่ได้ตั้งใหม่ — ใช้ 14px ตัวเดิมที่ .map-brand ประกาศไว้อยู่แล้ว แค่เปลี่ยนจากระยะแนวนอนเป็นแนวตั้ง
   • ไม่ใส่ white-space:nowrap เพราะพอเป็นคอลัมน์แล้วชื่อได้ทั้งความกว้างการ์ด (276px ที่ 360)
     เทียบกับที่ต้องการ 146px = เหลือ 130px ไม่ใช่การรอดแบบเฉียดอีกต่อไป
     และ nowrap มีความเสี่ยงดันหน้าให้เลื่อนแนวนอนถ้าวันไหนข้อความยาวขึ้น
   ⚠️ .map-brand / .map-brand__logo / .map-brand__name มี "เฉพาะใน d2.html" (d1/d3 = 0 จุด ตรวจแล้ว)
      กฎพวกนี้อยู่ใน base.css ที่แชร์กันก็จริง แต่ไม่แมตช์อะไรเลยในสองไฟล์นั้น ⇒ ไม่ต้อง scope เพิ่ม
   ⚠️ ≥1000px ไม่แตะ — ยังเป็นแถวแนวนอนตามกฎด้านบน + override ใน styles/desktop-d2.css (โลโก้ 72px ชื่อ 24px) */
@media (max-width:999px){
  .map-brand{flex-direction:column;align-items:flex-start}
}

.map-embed{border-radius:var(--radius-sub);overflow:hidden;border:1px solid var(--border-card);background:var(--surface-sub)}
.map-embed iframe{display:block;width:100%;height:196px;border:0}

/* ---------- LINE section ---------- */
.line-card{display:grid;gap:16px;justify-items:center;text-align:center}
.line-qr{width:200px;height:200px;border-radius:var(--radius-sub);border:1px solid var(--border-card);background:var(--white)}
.line-id{font-size:14px;color:var(--text-muted)}

/* ---------- trust band ----------
   มือถือเรียงลงมาเป็นรายการ (ไม่ซ่อน — ซ่อนเนื้อหาบนมือถือเสียทั้ง SEO และความเท่าเทียมของข้อมูล) */
.trust{display:grid;gap:16px;justify-items:center;text-align:center}
.trust__item{display:flex;gap:12px;align-items:center;text-align:left;font-size:15px;line-height:1.6;color:var(--text-body);width:100%;max-width:320px}
.trust__item svg{width:26px;height:26px;flex:none;color:var(--green-700)}
.trust__sub{color:var(--text-muted)}

/* ---------- SEO bands ---------- */
.seo-bands{display:grid;gap:12px;margin-top:26px}
.seo-band{background:var(--white);border:1px solid var(--border-card);border-radius:var(--radius-sub);padding:18px 20px;display:grid;gap:6px}
.seo-band h2{font-size:20px}
/* บรรทัดที่อยู่ / เวลาทำการ ในกล่อง SEO ซ้าย — แยกบรรทัดด้วย display:block
   ระยะห่างระหว่างบรรทัดมาจาก line-height ของ .small ล้วน ไม่มี margin เพิ่ม (ตามที่เจ้าของสั่ง) */
.seo-band__line{display:block}
/* ⚠️ nowrap เฉพาะบรรทัดเวลา — ห้ามตัดคำกลาง "8.00 - 17.30 น."
   วัดจริงในเบราว์เซอร์ (26 ส.ค. 2569): ที่ viewport 390px ตัวอักษรบรรทัดนี้กว้าง 159px
   ในช่องที่กว้าง 300px ⇒ เหลือที่ว่าง 141px ไม่ล้นแน่นอน (ที่ 1440px คือ 170px ในช่อง 494px)
   ⚠️ อย่าเอา nowrap ไปใส่ .seo-band__line ตัวแม่ บรรทัดที่อยู่ยาวเกิน ต้องตัดคำได้
      (ที่ 390px บรรทัดที่อยู่ตกเป็น 2 บรรทัดตามปกติ) */
.seo-band__line--hours{white-space:nowrap}

/* ---------- footer ---------- */
.site-footer{
  margin-top:28px;padding:28px var(--gutter) 40px;
  background:var(--surface-invert);color:rgba(255,255,255,.9);
  display:grid;gap:16px;justify-items:center;text-align:center;
}
.site-footer p{font-size:15px;line-height:1.72}
.site-footer__logo{height:92px;width:auto}

/* 🔴 ถอด .modal / .modal__scrim / .modal__card ออก 2 ก.ย. 2569
   ปุ่ม LINE ทุกปุ่มเป็นลิงก์ตรงไป lin.ee แล้ว จึงไม่มีโมดัลในหน้าอีก (ดูคอมเมนต์หัว scripts/site.js)
   ⚠️ ลบชื่อ .modal ออกจากกฎ @media print ท้ายไฟล์ด้วยแล้ว */

/* ---------- สลับ "การ์ด/ไอคอน ↔ โปสเตอร์" ที่เส้น 1000px (มีผลกับ d2.html เท่านั้น) ----------
   <1000px  = การ์ด 4 ใบ + ไอคอนบริการ 6 อัน · ซ่อนโปสเตอร์ทั้งสองใบ
   ≥1000px = โปสเตอร์สองใบ · ซ่อนการ์ด/ไอคอน (กฎอีกฝั่งอยู่ใน styles/desktop-d2.css)
   ⚠️ สลับด้วย CSS ล้วน ไม่ใช้ JS · ทั้งสองชุดอยู่ใน DOM ครบตลอดเวลา
   ⚠️ ทุกคลาสในบล็อกนี้มีเฉพาะใน d2.html — ตรวจแล้ว d1/d3 = 0 จุดทั้ง .steps-poster .service-poster
      .steps-list .service-list ⇒ ไม่กระทบสองไฟล์นั้นแม้ใช้ base.css ร่วมกัน
   ⚠️ .steps-list / .service-list คือก้อน sr-only ที่ทำหน้าที่ SEO แทนข้อความที่ฝังอยู่ในรูปโปสเตอร์
      ปิดที่ <1000px เพราะข้อความชุดเดียวกันโผล่ในการ์ดที่ "มองเห็นจริง" แล้ว จะซ้ำสองที่
      ≥1000px ยังต้องมีเหมือนเดิม เพราะการ์ดถูก display:none ทั้งชุด — ห้ามลบ <ol>/<ul> ทิ้ง */
@media (max-width:999px){
  .steps-poster,.service-poster{display:none}
  .steps-list,.service-list{display:none}
}

/* ---------- ช่องว่างใต้การ์ดที่เตี้ยกว่าใน .tools__track (แก้ 26 ส.ค. 2569) ----------
   .tools__track เป็น flex + align-items:flex-start ⇒ ความสูง track = การ์ดที่สูงที่สุด
   แล้วการ์ดอีกใบลอยอยู่ข้างบน เหลือพื้นเปล่าใต้ตัวเองเท่าส่วนต่าง (วัดจริง: 138px ที่ 360px ในแท็บแผนที่
   · 214px ที่ 768-999px ในแท็บ 4 ขั้นตอน) และดัน "ปัดซ้าย-ขวาเพื่อดูส่วนอื่น" ลงไปอยู่ก้นล่าง
   ⇒ ให้การ์ดที่เตี้ยกว่ายืดเต็มความสูง track: ที่ว่างเท่าเดิมแต่ย้ายเข้าไปอยู่ "ในการ์ด" แทนที่จะเป็นพื้นเปล่า
   ⚠️ เลือกทางนี้แทนการย่อแผนที่ที่แท็บเล็ต (เจ้าของสั่งว่าแผนที่ใหญ่ที่แท็บเล็ตเป็นเรื่องดี)
      และแทนการสั่งความสูงด้วย JS (โปรเจกต์นี้ยึด CSS ก่อน)
   ⚠️ :has(.steps__row) = ยิงเฉพาะ track ของ d2 — d1.html ใช้ .tools__track เหมือนกันแต่มี 3 แผง
      และไม่มี .steps__row เลย (ตรวจแล้ว d1/d3 = 0 จุด) จึงไม่โดนกฎนี้
   ⚠️ ต้องอยู่ใน max-width:999px — ที่ ≥1000px .tools__track เปลี่ยนเป็น display:grid แล้ว
      (styles/desktop-d2.css) ห้ามให้กฎนี้หลุดไปถึงตรงนั้น */
@media (max-width:999px){
  .tools__track:has(.steps__row){align-items:stretch}
  .tools__track:has(.steps__row) > .tools__panel > .card{height:100%}
}

/* ---------- ที่เหลือในการ์ดแผนที่ → ยกให้แผนที่ ไม่ใช่พื้นเปล่า (29 ส.ค. 2569) ----------
   ปัญหา: กฎ stretch ด้านบนยืด "กล่องการ์ด" ให้สูงเท่า track ก็จริง แต่ลูกในการ์ดยังสูงเท่าเดิม
   ⇒ ที่ว่างไปกองอยู่ใต้ปุ่ม "ขอเส้นทางไปร้าน" เป็นพื้นขาวเปล่า
   วัดจริง 29 ส.ค. (ส่วนเกินหลังหัก padding 23px ของการ์ด):
     320→223 · 360/375→176 · 390/412→201 · 480→194 · 600→123 · 768/999→39
   ต้นเหตุที่ทำให้พุ่งขึ้น: 27 ส.ค. จัดการ์ด 4 ขั้นตอนเป็นแนวตั้ง การ์ดสูงจาก 674 → 898 (ที่ 412)
   จากเดิมที่การ์ดแผนที่เป็นตัวคุมความสูง track กลายเป็นการ์ดขั้นตอนคุมทุกความกว้าง 320-999

   ทางแก้: ให้ .map-embed (order:3 ในการ์ด) เป็นตัวกินที่เหลือ ⇒ ช่องว่างกลายเป็นแผนที่ที่ใหญ่ขึ้น
   เจ้าของเลือกทางนี้เอง (29 ส.ค.): ร้านนี้ลูกค้าขับรถมาเอง แผนที่ใหญ่มีประโยชน์กว่าปุ่มที่อยู่สูงขึ้น
   และการเลื่อนจอในแท็บแผนที่ก็เท่ากับแท็บ 4 ขั้นตอนอยู่แล้ว (การ์ดสูงเท่ากันเพราะ stretch)

   🔴 ทำไมต้องใส่ flex ที่ .map-embed ด้วย ไม่ใช่แค่ flex:1 ตัวเดียว:
      .map-embed เป็น flex item ของ .map-card ⇒ flex:1 ยืด "กล่อง" ได้ แต่ <iframe> ข้างในมี
      height ตายตัว (196px) จึงไม่ยืดตาม เหลือพื้นสี --surface-sub ใต้แผนที่แทนที่จะเป็นแผนที่
      ⇒ ทำให้ .map-embed เป็น flex column ด้วย แล้วให้ iframe เป็น flex item ที่ยืดได้

   ✅ ไม่มีตัวเลขใหม่ในบล็อกนี้เลยแม้แต่ตัวเดียว — จงใจ
      iframe ใช้ flex-basis:auto ⇒ ขนาดตั้งต้นมาจาก height ที่ประกาศไว้แล้วข้างบน
      (196px · และ 280px ที่ >=768px จากบล็อก @media (min-width:768px))
      ⇒ ถ้าวันไหนไม่มีที่เหลือให้กิน (การ์ดแผนที่กลับมาเป็นตัวสูงสุดเอง) แผนที่จะหดกลับไปที่
        196/280 ตามเดิมเป๊ะโดยอัตโนมัติ **ไม่ต้องมี min-height มาไล่จูนตามให้ตกหล่น**
      ตรวจแล้ว 29 ส.ค.: บังคับให้ไม่มีที่เหลือ → ได้ 196px ที่ <768 และ 280px ที่ 768-999 ทั้งคู่

   ⚠️ กฎ .map-embed iframe{height:280px} ใน @media (min-width:768px) **ไม่ตีกับกฎนี้**
      บล็อกนั้นไม่มีขอบบน (min-width:768px เฉย ๆ) จึงกินถึงเดสก์ท็อปด้วย แต่:
      • ที่ 768-999px — selector ที่นี่คือ (1 id, 3 class/pseudo, 1 type) ชนะ (0,1,1) ของบล็อกนั้นขาด
        และ height ของ iframe ถูกใช้เป็นแค่ flex-basis ⇒ 280 กลายเป็น "พื้น" ไม่ใช่ "เพดาน"
      • ที่ >=1000px — กฎนี้อยู่ใน max-width:999px จึงไม่มีผล · ที่นั่น .map-card เป็น grid และมี
        desktop-d2.css `.map-card .map-embed iframe{height:100%;min-height:480px}` คุมอยู่แล้ว
      วัดยืนยัน 1000/1200/1440: ทุกค่าเท่าเดิมทุกพิกเซล (embed 482 · track 1016/1104/1104)
   ⚠️ scope ด้วย .tools__track:has(.steps__row) เหมือนกฎ stretch ด้านบน — สองกฎนี้เป็นคู่กัน
      ถ้าวันไหนถอด stretch ออก กฎนี้ต้องหยุดทำงานพร้อมกัน ไม่งั้นแผนที่จะยืดโดยไม่มีที่ให้ยืด */
@media (max-width:999px){
  .tools__track:has(.steps__row) #panel-map .map-embed{display:flex;flex-direction:column;flex:1 1 auto}
  .tools__track:has(.steps__row) #panel-map .map-embed iframe{flex:1 1 auto}
}

/* ---------- แท็บเล็ต ----------
   🔴 ถอดออกจากบล็อกนี้ 2 ก.ย. 2569: .hero__photo · .hero__banner img · .hero__brand-logo ·
      .hero__brand-name (ของ hero รุ่นก่อนแบนเนอร์) และ .desk-only ที่อยู่เหนือบล็อก
      — ไม่มี element ไหนในหน้าใช้แล้ว · .hero__cta ยังอยู่เพราะยังใช้จริง */
@media (min-width:768px){
  :root{--gutter:32px}
  h1{font-size:38px}
  .hero__cta{grid-auto-flow:column;justify-content:center;gap:14px;width:auto}
  .hero__cta .btn{min-width:240px}
  .seo-bands{grid-template-columns:1fr 1fr;gap:20px}
  .tools__panel{width:min(560px,100vw)}
  .services{--tile:120px;--tile-gap:28px}
  .map-embed iframe{height:280px}
}

/* ---------- ลดการเคลื่อนไหวตามการตั้งค่าเครื่อง ---------- */
@media (prefers-reduced-motion:reduce){
  *{animation-duration:.01ms!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
}

/* ---------- print ---------- */
@media print{
  .site-header,.tools__tabs,.bg-marble,.leaf-edge{display:none!important}
}
