/* app.css — สไตล์ของหน้าจอจริง (ระยะ 0 · โครงเดินได้)
   🔴 ค่าสีทุกค่าต้องมาจากโทเคนใน tims-ds.css เท่านั้น ห้ามเขียนเลขสีตรงนี้
      (กติกาเดียวกับตัวตรวจข้อ 19 ของต้นแบบ)
   🔴 ห้ามมี style="" ในหน้า — CSP ของระบบเป็น style-src 'self' ไม่มี unsafe-inline */

*,*::before,*::after{box-sizing:border-box}a:hover{text-decoration:underline}

/* ── สีประจำบทบาท (ระลอก 3 แบบ 1B · เจ้าของงานเลือก 5 ต.ค. 69) ─────────────────────
   ปุ่มหลักน้ำเงินเดียวทั้งระบบ · สีบทบาทอยู่ที่แถบบนสุด ป้ายบทบาท เมนูที่เลือก หัวเรื่องย่อย
   🔴 ต้นแบบปิดงานแล้ว (24 ก.ย. 69) จึงไม่ดึง tims-ds.css ใหม่อีก ⇒ โทเคนสีที่เพิ่มหลังจากนั้นอยู่ที่นี่
   🔴 ชื่อโทเคนต้องตรงค่า — ใน tims-ds.css `--amber` ถือค่าน้ำเงินคราม และ `--plum` ถือค่าน้ำเงินเข้ม (ชื่อหลอก · ผลตรวจ D4)
      ⇒ โค้ดใหม่ใช้ชื่อที่ตรงค่าด้านล่างเท่านั้น: --indigo (= ค่าของ --amber) · --navy-ink (= ค่าของ --plum)
   ผลตรวจ D3 — เดิมนักศึกษา/กรรมการ/ฝ่ายฝึกเป็นน้ำเงินเกือบเหมือนกัน · ปุ่มหลักของอาจารย์เป็นส้มอิฐใกล้ปุ่มลบ */
:root{
  --indigo:#5b6aa0; --navy-ink:#2b3759;
  --teal:#2a7a6b; --teal-dark:#1e5c50; --teal-soft:#dcefe9;
  --violet:#6b3f7a; --violet-dark:#56305f; --violet-soft:#f0e6f3;
}
.theme-student,.theme-supervisor,.theme-committee,.theme-admin{--accent-btn:var(--pine);--accent-btn-h:var(--pine-dark)}
.theme-committee{--accent:var(--teal);--accent-dark:var(--teal-dark);--accent-soft:var(--teal-soft)}
.theme-admin{--accent:var(--violet);--accent-dark:var(--violet-dark);--accent-soft:var(--violet-soft)}
.btn-pri{box-shadow:0 4px 12px color-mix(in srgb,var(--accent-btn) 28%,transparent)}
/* แถบสีบทบาทบนสุดของทุกหน้า — เงาด้านใน ไม่ใช่ขอบ (ไม่ดันความสูงแถบบน) */
.theme-student .topbar,.theme-supervisor .topbar,.theme-committee .topbar,.theme-admin .topbar{box-shadow:inset 0 4px 0 var(--accent)}
.role-committee{background:var(--teal-soft);color:var(--teal-dark)} .role-committee .dot{background:var(--teal)}
.role-admin{background:var(--violet-soft);color:var(--violet-dark)} .role-admin .dot{background:var(--violet)}
/* กล่องคำแนะนำเป็นสีกลางทุกบทบาท — สีส้ม/แดงสงวนไว้ให้ .tip-warn / ข้อผิดพลาด (ผลตรวจ D3) */
.tip:not(.tip-warn):not(.tip-ok){background:var(--pine-soft);color:var(--pine-dark)}
.tip:not(.tip-warn):not(.tip-ok)>svg{color:var(--pine)}

/* ── หน้าเดี่ยว (เข้าสู่ระบบ) ─────────────────────────────── */

.org{font-size:var(--fs-2);color:var(--ink-2);line-height:1.5}
.org strong{display:block;color:var(--ink);font-size:var(--fs-3)}
.hr{height:1px;background:var(--line);border:0;margin:18px 0}

/* ── ฟอร์ม ────────────────────────────────────────────────── */
.field label{display:block;font-size:var(--fs-2);color:var(--ink-2);margin-bottom:6px;font-weight:500}.inp[aria-invalid="true"]{border-color:var(--bad)}
/* ── มาตรฐานปุ่ม (แผนแก้ UI รอบ 4 · ข้อ 1 + 4 · เจ้าของงานเคาะ 25 ก.ย. 69) ──────────
   เขียว  .btn-ok    = กดแล้วข้อมูลถูกบันทึก/ยืนยัน (บันทึก อนุมัติ ผ่าน เผยแพร่ ส่ง นำเข้า เพิ่ม)
   แดง    .btn-bad   = ลบ ถอน ตีกลับ ยกเลิก ปิด
   สีบทบาท .btn-pri  = ปุ่มหลักของหน้า (เพิ่ม/สร้าง ที่พาไปหน้าฟอร์ม · เข้าสู่ระบบ · ค้นหา)
   ธรรมดา .btn-ghost = ขาวมีขอบ (ดู ดาวน์โหลด ส่งออก กลับ ยกเลิก)
   🔴 เดิมมี .btn:hover{background:...} บรรทัดเดียวครอบปุ่มทุกชนิด และ app.css โหลดทีหลัง
      ⇒ ทับ hover ของแต่ละชนิด เปลี่ยนแต่พื้นเป็นสีเข้ม ตัวหนังสือยังเข้มเท่าเดิม อ่านไม่ออก
      ⇒ ทุกชนิดต้องประกาศ hover ครบคู่ "พื้น + สีตัวหนังสือ" (StyleContractTest เฝ้าอยู่)
   🔴 ปุ่มที่ไม่มีชนิด (class="btn" เปล่า) ไม่มีพื้นไม่มีขอบ ดูไม่ออกว่าเป็นปุ่ม — ห้ามใช้ */
.btn:disabled{opacity:.55;cursor:not-allowed}
a.btn:hover,a.icon-btn:hover{text-decoration:none}
.btn-ghost:not(:disabled):hover,.icon-btn:hover,.act:not(:disabled):hover,.btn-soft:not(:disabled):hover{
  background:var(--accent-btn);border-color:var(--accent-btn);color:var(--on-accent)}
.btn-pri:not(:disabled):hover,.btn-terra:not(:disabled):hover{color:var(--on-accent)}
.btn-ok:not(:disabled):hover{background:var(--ok-ink);color:var(--on-accent);transform:translateY(-1px)}
.btn-bad:not(:disabled):hover{background:var(--bad-ink);color:var(--on-accent);transform:translateY(-1px)}
.btn:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.btn-wide{width:100%}

.err{
  background:var(--bad-soft);border:1px solid var(--bad);color:var(--bad-ink);
  border-radius:var(--r2);padding:10px 12px;font-size:var(--fs-2);margin-bottom:16px;
}

/* ── เปลือกหลังบ้าน (ระยะ 0 ยังเป็นโครง) ──────────────────── */.who{font-size:var(--fs-2);color:var(--ink-2)}.card h2{font-size:var(--fs-5);margin-bottom:12px}
.kv{display:grid;grid-template-columns:auto 1fr;gap:8px 18px;font-size:var(--fs-3)}
.kv dt{color:var(--ink-3)}
.kv dd{margin:0;color:var(--ink)}
.tag{
  display:inline-block;padding:2px 10px;border-radius:var(--pill);
  font-size:var(--fs-1);font-weight:600;background:var(--accent-soft);color:var(--accent-dark);
}

/* ── จอแคบ — ใช้จุดเปลี่ยนชุดเดียวกับต้นแบบ (900/700/560) ── */
@media (max-width:900px){
  .login-left{border-right:0;border-bottom:1px solid var(--line)}
}
/* ผลตรวจ M3 (5 ต.ค. 69) — มือถือเห็นตรา/มาสคอตเต็มจอก่อน ต้องเลื่อนหาช่องกรอก ⇒ จอโทรศัพท์วางฟอร์มก่อน
   (ลำดับภาพเท่านั้น ลำดับในโค้ดคงเดิม · แท็บเล็ตยังเป็นตราก่อนตามต้นแบบ) */
@media (max-width:700px){
  .login-right{order:-1}
  .login-left{border-bottom:0;border-top:1px solid var(--line);padding-top:22px}
}
@media (max-width:560px){
  .login-left,.login-right{padding:24px 20px}
}

/* ═══ เปลือกแอป — เมนูซ้าย · แถบบน · แถบบริบท ═══════════════ */

/* 🔴 แถบบริบทเป็น sticky และ **ต้องสูงเท่ากันทุกบทบาท**
   ยืด/หดเมื่อไรหน้าจะกระโดดตอนสลับบทบาท (มีเทสต์เฝ้าความสูงอยู่ในต้นแบบ) *//* 🔴 ไม่มีกฎจอแคบของเมนูซ้ายที่นี่ — ต้นแบบจัดการเองครบแล้ว
   (≤900px เหลือแถบไอคอน 72px · ≤700px เป็นลิ้นชักที่กดเปิดจากปุ่มบนแถบบน)
   ของเดิมที่เขียนทับไว้ตอนยังไม่ได้พอร์ต UI ทำให้เมนูกลายเป็นแถวนอนที่ **ยุบไม่ลง**
   ⇒ ทั้งหน้าเลื่อนแนวนอนบนมือถือ โดยที่ตัวหน้าเองไม่ได้ผิดอะไรเลย
   เจอจาก tools/check_widths.cjs ซึ่งไล่ดูว่า "ชั้นไหนกว้างเกิน" ทีละชั้นจนถึง .shell */

/* ═══ ตาราง · แท็บ · ค้นหา · แจ้งเตือน ═════════════════════ */
.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;
}
/* 🔴 แท็บที่เป็นลิงก์ (<a>) ต้องหน้าตาเดียวกับแท็บของต้นแบบ (.tabs button · ขีดเส้นใต้)
      เดิมวาดเป็นปุ่มแคปซูลพื้นทึบ — เจ้าของงานเห็นว่าไม่เหมือนหน้าบัญชีผู้ใช้ (แผนแก้ UI รอบ 3 · ก3)
      ใช้ลิงก์ไม่ใช่ปุ่ม เพราะแท็บพวกนี้เปลี่ยนหน้าด้วย ?tab= (คัดลอกลิงก์ส่งต่อได้ ไม่ต้องใช้ JS) */
.tabs a{font-family:var(--sans);font-size:var(--fs-3);font-weight:600;color:var(--ink-3);padding:.6rem 1rem;
  border-bottom:2.5px solid transparent;margin-bottom:-1.5px;transition:.15s;display:inline-flex;align-items:center;
  gap:7px;text-decoration:none;min-height:40px}
.tabs a:hover{color:var(--ink);text-decoration:none}
.tabs a.on{color:var(--accent-dark);border-bottom-color:var(--accent)}
.tabs a.on .cnt{background:var(--accent-soft);color:var(--accent-dark)}
/* แท็บบรรทัดเดียว ไม่พอให้เลื่อนแนวนอน (ศูนย์ดาวน์โหลด · เจ้าของงานสั่ง 23 ต.ค. 69)
   overflow ตัดส่วนที่ล้นเข้าเส้นขอบล่าง ⇒ ขีดใต้แท็บที่เลือกหาย — ใช้เงาด้านในแทนเส้นขอบ และเลิกดึงแท็บลง 1.5px
   แถบเลื่อนกำหนดหน้าตาเอง ⇒ Safari/Chrome บน Mac แสดงตลอด ไม่ซ่อนจนกว่าจะเอาเมาส์ไปวาง */
.tabs.tabs-scroll{flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;border-bottom:0;box-shadow:inset 0 -1.5px 0 var(--line);
  scrollbar-width:thin;scrollbar-color:var(--line) transparent;-webkit-overflow-scrolling:touch;padding-bottom:2px}
.tabs.tabs-scroll > a{flex:0 0 auto;white-space:nowrap;margin-bottom:0}
.tabs.tabs-scroll::-webkit-scrollbar{height:6px}
.tabs.tabs-scroll::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px}
.tabs.tabs-scroll::-webkit-scrollbar-track{background:transparent}

/* 🔴 ตารางกว้างต้องเลื่อนแนวนอน ไม่ใช่บีบคอลัมน์จนอ่านไม่ออก
   หัวตารางตรึงไว้ ไม่งั้นเลื่อนลงแล้วไม่รู้ว่าคอลัมน์ไหนคืออะไร */

/* ต้นแบบตั้ง .tbl เป็น table-layout:fixed เพื่อให้ <colgroup> ที่ table() ส่งมามีผล
   ฝั่ง Laravel เขียนมาร์กอัปเอง ตารางเกือบทั้งหมดจึงไม่มี colgroup
   ⇒ ปล่อย fixed ไว้ = ทุกคอลัมน์กว้างเท่ากันหมด คอลัมน์ "ชื่องาน" ถูกบีบจนตัด
     4–5 บรรทัด ส่วนคอลัมน์ "ลำดับ" เหลือที่ว่างเปล่า
   ⇒ ตารางที่ไม่ประกาศความกว้างเอง ให้ไหลตามเนื้อหา · ตารางที่ประกาศยังได้ fixed
   ⚠️ เบราว์เซอร์ที่ไม่รู้จัก :has() จะทิ้งกฎนี้ทั้งข้อ = กลับไปเป็น fixed ซึ่งยังใช้ได้ */
.tbl-wrap .tbl:not(:has(> colgroup)){table-layout:auto}

.tbl thead th{
  position:sticky;top:0;z-index:1;
  background:var(--surface-2);color:var(--ink-2);font-weight:600;white-space:nowrap;
}

/* เป้ากดของลิงก์ในเซลล์ต้องถึง 40px โดยไม่ดันความสูงแถว */

/* ═══ ใบแจ้งรหัสเปิดใช้งาน ═════════════════════════════════ */.slip-org{margin:0 0 4px;font-size:var(--fs-1);color:var(--ink-3)}
.slip-one{max-width:640px;display:flex;flex-direction:column;gap:12px}
.slip-one > *{margin:0}
.slip-one h2{font-size:var(--fs-5)}
.slip-code{
  display:inline-block;padding:8px 16px;border-radius:var(--r2);
  background:var(--accent-soft);color:var(--accent-dark);
  font-family:var(--serif);font-size:var(--fs-5);letter-spacing:.18em;
}
.req{color:var(--bad);font-weight:600;font-size:var(--fs-1)}

/* ═══ รายวิชา + ผังขั้นตอน 10 ขั้น ═════════════════════════ */
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px}

/* ── แถบความคืบหน้าในตาราง — ความกว้างเป็นคลาส ไม่ใช่ style="" (CSP) ───────
   🔴 ห้ามตั้งชื่อว่า .w-NN เด็ดขาด — ต้นแบบใช้ชื่อชุดนั้นเป็น **พิกเซล** อยู่แล้ว
      (.w-100 = width:100px) ⇒ ทับกันแล้วแถบของคนที่ทำครบ 100% จะกว้าง 100px
      ซึ่ง "สั้นกว่า" แถบ 80% บนคอลัมน์กว้าง ๆ โดยไม่มีอะไรฟ้อง
      เจอจากการกวาดคลาสเทียบสองไฟล์ · มีเทสต์เฝ้าไว้แล้วว่า app.css
      ห้ามประกาศคลาสในกลุ่มยูทิลิตี้ของต้นแบบ */
/* ⚠️ ตัวแถบ .pbar เดิมถูกแทนด้วย .mbar ของต้นแบบแล้ว (แดชบอร์ดโรงเรียน) — เหลือแต่คลาสความกว้างที่ใช้ร่วม */
.pbar-p0>i{width:0}
.pbar-p10>i{width:10%}
.pbar-p20>i{width:20%}
.pbar-p30>i{width:30%}
.pbar-p40>i{width:40%}
.pbar-p50>i{width:50%}
.pbar-p60>i{width:60%}
.pbar-p70>i{width:70%}
.pbar-p80>i{width:80%}
.pbar-p90>i{width:90%}
.pbar-p100>i{width:100%}
/* สีแถบภาระงานอาจารย์ (หน้าจัดสรร) — ต้นแบบตั้งสีด้วย style ในแท็ก ซึ่ง CSP ไม่ยอม
   เกณฑ์จริง: อาจารย์ 1 คนดูแล 5–10 คนต่อรุ่น · ต่ำกว่า = เหลือง · ในเกณฑ์ = เขียว · เกิน = แดง */
.load-ok>i{background:var(--ok)}
.load-warn>i{background:var(--warn)}
.load-bad>i{background:var(--bad)}
/* สีแถบกราฟเล็กของแดชบอร์ดทะเบียนโรงเรียน (miniBar ของต้นแบบใส่สีด้วย style ในแท็ก)
   ใช้คู่กับ .mbar (ตัวแถบของต้นแบบ) + .pbar-pNN (ความกว้าง) + ลูก <i class="mbar-f">
   ⇒ ที่นั่งรายสาขา = เขียวสน · จังหวัด = ม่วง · ที่นั่งเทียบความต้องการ = load-ok/load-bad */
.mbar-pine>i{background:var(--pine)}
.mbar-plum>i{background:var(--navy-ink)}

/* ── เส้นคั่น "หรือ" + ปุ่ม Google (หน้าเข้าสู่ระบบ · หน้าผูกบัญชี) ─────────── */

/*
 * 🔴 ปุ่ม Google ใช้สีของตัวเอง ไม่ผูกกับ --accent ของระบบ
 *    สีเน้นของเราเปลี่ยนตามบทบาทผู้ใช้ ถ้าผูกไว้ ปุ่มของผู้ให้บริการภายนอก
 *    จะเปลี่ยนสีตามบทบาทคนที่เปิดดู ซึ่งอ่านแล้วสับสนว่าเป็นคนละปุ่มกัน
 *    (กติกาเดียวกับสีแบรนด์ภายนอกในต้นแบบ: --g-* · --seo-link)
 */

/* การ์ดขั้นตอนของการเปิดใช้บัญชีครั้งแรก */
.steps{display:flex;gap:8px;margin:0 0 16px;padding:0;list-style:none}
/* 🔴 min-width ต้องเป็น 0 — การ์ดของหน้าผูกบัญชีแคบ (login-narrow) ถ้าตั้ง 120px ช่องที่ 3 ตกบรรทัดเดี่ยว */
.steps li{
  flex:1 1 0;min-width:0;padding:8px 6px;border-radius:8px;line-height:1.35;
  background:var(--surface-2);color:var(--ink-3);font-size:var(--fs-1);text-align:center
}
.steps li.done{background:var(--ok-soft);color:var(--ok-ink)}
.steps li.now{background:var(--accent-soft);color:var(--pine-deep);font-weight:600}

/* หนังสือส่งตัว — หน้าเอกสารราชการสำหรับพิมพ์ */
.letter-to{margin:16px 0 8px}
.letter-body{text-indent:2.5em;line-height:1.9;margin:12px 0}
.letter-sign{margin-top:40px;text-align:center}
.letter-sign p{margin:2px 0}
@media print{
  .rail,.topbar,.ctxbar{display:none}
}

/* ── งานที่ต้องส่ง · ส่งงาน · ตรวจงาน · ตีกลับ (ขั้นที่ 7) ──────────────── */

/* คอลัมน์ติ๊กเลือกในตาราง — เดิมเขียนเป็น style="width:48px" ซึ่ง CSP บล็อกทิ้งเงียบ ๆ */
.col-pick{width:48px}

/* ── ตารางกรอกคะแนนรวม (เห็นทุกคนทุกข้อในจอเดียว) ─────────────── */

/*
 * 🔴 กรรมการสาขากรอกคะแนนของทั้งสาขาคนเดียว ~1,026 ชุด/ปี
 *    ตารางที่ต้องเลื่อนหาหัวคอลัมน์ทุกครั้ง = กรอกผิดช่องแน่นอน
 *    ⇒ ตรึงหัวตารางและคอลัมน์ชื่อไว้ทั้งคู่
 */

/* ── หน้าสาธารณะ ───────────────────────────────────────────────── */

/*
 * 🔴 ต้องยกเว้น .btn — `.lp-nav a` เจาะจงกว่า `.btn` (คลาส+อิลิเมนต์ vs คลาสเดียว)
 *    ⇒ ทับสีตัวอักษรของปุ่มจนเป็นสีเข้มบนพื้นเข้ม **อ่านไม่ออกเลย**
 *    ปุ่มยังอยู่ครบ กดได้ปกติ และไม่มี error อะไรทั้งสิ้น — เห็นได้จากการเปิดหน้าจริงดูเท่านั้น
 */
.lp-nav a:not(.btn){color:var(--ink-2);text-decoration:none}
.lp-nav a:not(.btn):hover{color:var(--accent)}.lp-foot p{margin:2px 0}
/* 🔴 จอแคบ: ชื่อหน่วยงานใต้คำว่า TIMS ตัดเป็น 6 บรรทัด แถบบนสูง 159px และติดจอตลอด (sticky) — เจ้าของงานส่งภาพ 28 ก.ย. 69
   ชื่อหน่วยงานอยู่ในหัวเรื่องและส่วนท้ายของหน้าอยู่แล้ว ⇒ จอแคบซ่อนบรรทัดนี้ (check_widths ด่าน "แถบบนสูงเกิน") */
@media(max-width:700px){.lp-nav .brand-sub{display:none}}

/* วิดีโอ YouTube — อัตราส่วน 16:9 โดยไม่ต้องใช้สคริปต์ (CSP ไม่มี unsafe-inline) */
.lp-video{position:relative;padding-top:56.25%;margin:12px 0;border-radius:var(--r2);overflow:hidden}
.lp-video iframe{position:absolute;inset:0;width:100%;height:100%;border:0}

/* ═══════════════════════════════════════════════════════════════════════
   หักล้างระยะที่ต้นแบบเผื่อไว้ให้ "แถบควบคุมโปรโตไทป์" (สูง 47px)
   🔴 ระบบจริงไม่มีแถบนั้น — ถ้าไม่หักล้าง เมนูซ้ายจะถูกดันลง 47px
      และสูงขาดไป 47px โดยหน้ายังเรนเดอร์ปกติ ไม่มีอะไรฟ้อง
   🔴 แก้ที่นี่ ไม่ใช่ที่ tims-ui.css — ไฟล์นั้นเป็นผลลัพธ์จากตัวดึง
      แก้แล้วจะถูกเขียนทับทุกครั้งที่รัน node design/extract_ui.js
   ═══════════════════════════════════════════════════════════════════════ */
.shell{
  min-height:100vh;
  /* 🔴 เปลือกแอปต้องไม่เลื่อนแนวนอนเด็ดขาด
     ตารางกว้างมีกล่องของตัวเองที่เลื่อนได้อยู่แล้ว (.tbl-wrap / .xgrid-wrap)
     แต่ Chrome ยังนับความกว้างของตารางที่ถูกครอบไว้เข้าไปใน scrollWidth ของเอกสาร
     ⇒ หน้าลากไปทางขวาได้ 9px บนมือถือ โดยไม่มีอะไรโผล่ออกมาให้เห็นเลย
     ใช้ `clip` ไม่ใช่ `hidden` — `hidden` ทำให้ชั้นนี้กลายเป็นตัวเลื่อน
     แล้ว sticky ของแถบบน/แถบบริบทจะหยุดทำงานทันที
     เจอด้วย tools/check_widths.cjs (เปิด Chrome จริง · ไล่ทีละชั้นจนถึง .shell) */
  overflow-x:clip;
}
/* 🔴 ต้นแบบมี "แถบเครื่องมือของโปรโตไทป์" สูง 47px อยู่บนสุดของหน้า
   ค่า `top:` ของทุกชั้นที่เป็น sticky จึงบวก 47 ไว้หมด — ระบบจริงไม่มีแถบนั้น
   ⇒ ต้องลบ 47 ออกทุกตัว ไม่งั้นแถบจะหยุดต่ำกว่าขอบจอ แล้วมีแถบเนื้อหา
     เลื่อนผ่านช่องว่างข้างบนให้เห็น · **ไม่มีอะไรพัง ไม่มี error**
   🔴 เคยแก้ .rail/.lp-nav/.ctxbar แล้ว แต่ **ลืม .topbar** ไปตัวเดียว
     ⇒ แถบบนหยุดที่ 47px ทุกหน้าหลังล็อกอิน · มีเทสต์เฝ้าครบทุกตัวแล้ว */
.rail{top:0;height:100vh}
.lp-nav{top:0}
.topbar{top:0}
/* ต้นแบบตั้ง 111px = 47 (แถบ mockup) + 64 (แถบบน) · ของเราไม่มี 47 */
.ctxbar{top:64px}

/* ── สีพื้นของไอคอนบนการ์ดตัวเลข ─────────────────────────────────────────
   🔴 ต้นแบบใส่ด้วย style="background:var(--ok)" ผ่าน CSSOM หลังเรนเดอร์
      (มันเลี่ยง CSP ด้วยวิธีนั้นได้เพราะไม่ใช่แอตทริบิวต์ในมาร์กอัป)
      ระบบจริงเรนเดอร์ที่เซิร์ฟเวอร์ ⇒ เขียน style="" ในหน้าไม่ได้เลย
      **เบราว์เซอร์จะทิ้งกฎนั้นเงียบ ๆ หน้ายังเรนเดอร์ปกติ ไม่มีอะไรฟ้อง**
   ⇒ ทำเป็นคลาสแทน แล้วให้ฝั่ง PHP เลือกโทนสีมาให้ */
.stat .ic{background:var(--pine)}
/* ระลอก 3 แบบ 4A — สีไอคอนบนไทล์ตามความหมาย ไม่ใช่ตามตำแหน่ง (ผลตรวจ D13)
   info = ข้อมูลทั่วไป · ok = เรียบร้อย · warn = รอคนทำ · bad = ต้องแก้ · เลือกโทนที่ Dashboard::tile() */
.stat .ic.info{background:var(--pine)}
.stat .ic.ok{background:var(--ok)}
.stat .ic.warn{background:var(--warn)}
.stat .ic.bad{background:var(--bad)}

/* ต้นแบบผูก hover ไว้กับ [data-page] · ของเราใช้ <a href> จริง */
a.stat{cursor:pointer}
a.stat:hover{transform:translateY(-3px);box-shadow:var(--sh-2);
  border-color:color-mix(in srgb,var(--accent) 26%,var(--line))}

/* ── การ์ดงานในหน้ารายวิชา ────────────────────────────────────────────────
   ต้นแบบ **ไม่ได้ตั้งกฎ** ให้สองชั้นนี้ — ปล่อยเป็นค่าปริยายของ <span>
   ที่เป็นลูกของกล่อง flex แนวตั้ง (ไอคอนกับป้ายจึงเรียงลงมา ไม่ได้อยู่บรรทัดเดียวกัน)
   เขียนให้ตรงกับผลที่ต้นแบบเรนเดอร์จริง เพื่อให้ทุกคลาสที่หน้าจอใช้มีกฎของตัวเอง
   และคนอ่านจะได้ไม่เข้าใจผิดว่าลืมใส่สไตล์ไว้ */
.job-head{display:block}
.job-n{font-size:var(--fs-3);color:var(--ink)}

/* กล่องบอก "ผ่านแล้ว/อนุมัติแล้ว" — ต้นแบบมี .tip-warn แต่ยังไม่มีคู่ฝั่งบวก
   ⇒ เขียนตามแพตเทิร์นเดียวกันเป๊ะ (โทเคนสีของต้นแบบ) ไม่ได้ตั้งศัพท์ใหม่
   จำเป็นเพราะกติกาของโปรเจกต์คือ **สถานะต้องอ่านออกด้วยสีก่อนอ่านตัวหนังสือ** */
.tip-ok{background:var(--ok-soft);color:var(--ok-ink)}
.tip-ok svg{color:var(--ok-ink)}

/* เส้นคั่นบนของฟอร์ม "นัดนิเทศ" ที่ต่อท้ายรายการเที่ยวนิเทศ
   ต้นแบบใช้ card แยกใบ ฝั่งนี้รวมอยู่ใบเดียวกับรายการเพื่อไม่ให้การ์ดยาวเป็นพรืด */
.bd-t-line{border-top:1px solid var(--line-2)}

/* แจ้งปัญหาการใช้งาน — ภาพหน้าจอที่แนบมา (ฟีเจอร์ใหม่ ไม่มีในต้นแบบ) · กดเปิดขนาดเต็มในแท็บใหม่ */
.issue-shots{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-bottom:10px}
.issue-shots a{display:block;border:1px solid var(--line);border-radius:10px;overflow:hidden;background:var(--surface-2,var(--surface))}
.issue-shots img{display:block;width:100%;height:180px;object-fit:contain}

/* ความสูงตราระบบบนแถบนำทางหน้าสาธารณะ (C2) — ค่าเดียวกับ Brand::HEIGHTS · ใช้คลาสแทน style="" (CSP) */
.lp-nav .logo-h-28{height:28px;width:auto;}
.lp-nav .logo-h-32{height:32px;width:auto;}
.lp-nav .logo-h-38{height:38px;width:auto;}
.lp-nav .logo-h-42{height:42px;width:auto;}
.lp-nav .logo-h-48{height:48px;width:auto;}
.lp-nav .logo-h-56{height:56px;width:auto;}
/* ผลค้นหาทั้งระบบ (ข1) — แถวทั้งแถวเป็นลิงก์ · ไม่ขีดเส้นใต้ทั้งแถว */
a.tl-row{color:inherit;text-decoration:none}
a.tl-row:hover{background:var(--surface-2);text-decoration:none}
/* 🔴 การ์ด "เข้าด้วยรหัสผ่านของตัวเอง" ต้องยุบได้ (แผนแก้ UI รอบ 3 · ค2)
      ต้นแบบไม่วาด .pw-body เลยตอนยุบ — ฝั่ง Laravel วาดไว้เสมอ (ต้องให้ฟอร์มส่งได้เมื่อกางด้วย JS)
      ⇒ ต้องซ่อนด้วย CSS ไม่งั้นช่องกรอกกางค้างตลอด ลูกศรกดแล้วดูเหมือนไม่มีผล */
.pw-fold:not(.on) .pw-body{display:none}
/* 🔴 ตราที่เป็น <img class="brand-logo"> ต้องคงสัดส่วนจริง (แผนแก้ UI รอบ 3 · ค4)
      แอตทริบิวต์ width/height สี่เหลี่ยมจัตุรัสบังคับตรามหาวิทยาลัย (154×192) ให้บิดเบี้ยว
      ⇒ ใช้ความสูงจากแอตทริบิวต์ ความกว้างตามสัดส่วนไฟล์ (ตราที่ฝ่ายฝึกอัปโหลดเองก็สัดส่วนไหนก็ได้) */
img.brand-logo{width:auto;object-fit:contain}
/* 🔴 ตราในชุดวางตราสูงเท่ากันทุกใบ (เจ้าของงานสั่ง 25 ก.ย. 69) — คุมที่นี่จุดเดียว ใช้กับ <x-seal-lock> */
.seal-row img.brand-logo{height:64px}
.seal-sm .seal-row img.brand-logo{height:44px}

/* แถบบนแน่นที่จอ ~1280px — ป้ายบทบาทกับชื่อผู้ใช้ห้ามตกบรรทัด (ตรวจบนซับทดสอบจริง 25 ก.ย. 69)
   ชื่อยาวตัดด้วย … แทนการดันแถบให้สูงขึ้น · ชื่อเต็มยังอยู่ใน alt ของรูปและหน้าข้อมูลส่วนตัว */
.rolepill{white-space:nowrap;flex-shrink:0}
.usr-meta{min-width:0;max-width:240px}
.usr-meta b,.usr-meta small{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@media (max-width:1320px){.usr-meta{max-width:170px}}

/* ── ระยะห่างชั้นใน (แผนแก้ UI รอบ 4 · ข้อ 7 · กฎ ป5) ────────────────────────
   กฎเว้นระยะ 16px ของต้นแบบ ([data-view] > * + *) ครอบแค่ลูกชั้นแรกของหน้า
   ⇒ การ์ดที่อยู่ในฟอร์ม (หน้าประกาศระบบ · นโยบาย · ข้อความช่วยเหลือ · เทมเพลตแจ้งเตือน) ชนกัน
   ⇒ ปุ่มที่อยู่คนละฟอร์มซ้อนกันแนวตั้งในการ์ดเดียวกันติดกัน (เผยแพร่ ↕ ย้ายเข้าถังขยะ)
   tools/check_widths.cjs วัดทุกคู่ทั้งหน้าแล้ว — ต่ำกว่า 8px = ไม่ผ่าน */
main.page form > :not(input[type="hidden"]) + .card,
main.page form > .card + :not(input[type="hidden"]){margin-top:16px}
/* แถบค้นหา/กรองของตารางที่อยู่ในการ์ดไม่มีระยะใน — ข้อความชิดขอบการ์ด */
.card:not(.card-pad) > .tbl-tools{padding:12px 16px 0}
/* แถบค้นหาตารางต่อจากกล่องข้อความ/แถบสรุป ต้องเว้นช่อง (หน้านำเข้าบัญชี: กล่องเตือนไฟล์ใหญ่ติดแถบค้นหา 2px · เจ้าของงานทัก 22 ต.ค. 69) */
* + .tbl-tools{margin-top:14px}
/* กล่องข้อความต้นส่วน (.vsec-b) ห่างแถบหัวส่วนแค่ 6px — ตัวตรวจ "กล่องติดกัน" จับได้ (หน้าแก้ข้อมูลส่วนตัว › ผลการทดสอบ · 22 ต.ค. 69) */
.vsec-b > .tip:first-child{margin-top:10px}
/* ปุ่มออกจากระบบเป็น <button> ไม่ยืดเต็มแถวเหมือนลิงก์เมนู ⇒ ไอคอนเยื้องซ้ายตอนเมนูเหลือแต่ไอคอน (ข้อ 8) */
button.nav-i{width:100%;cursor:pointer}
/* ฟอร์มปุ่มเดี่ยวที่วางซ้อนกันในการ์ด/ช่องตาราง (แต่ละปุ่มต้องเป็นฟอร์มของตัวเองเพราะส่งคนละที่) */
main.page .card-pad > form + form,main.page .card-pad > form + .btn,main.page .card-pad > .btn + form,
main.page td > form + form{margin-top:10px}
/* แถบเลื่อนหน้าของตารางในการ์ดไม่มีระยะใน (ต้นแบบวางใต้ตารางเปล่า ๆ ไม่ได้อยู่ในการ์ด) */
.card:not(.card-pad) > .pager{margin-top:0;padding:10px 16px;border-top:1px solid var(--line-2)}
/* แถบบริบทตกเป็นสองแถวบนจอโน้ตบุ๊ก ~1280px (ผ1 · ตรวจบนซับทดสอบจริง) — ชื่อสาขายาวดันชิปสุดท้ายลงบรรทัดใหม่
   ⇒ ช่วงจอกลางให้ชิปอยู่แถวเดียว ตัดชื่อยาวด้วย … (ชื่อเต็มยังอยู่ในตัวเลือกเมื่อกดเปิด) */
@media (min-width:901px) and (max-width:1440px){
  /* หน้าต่าง ~1000px (เมนูซ้ายเต็ม 268px) ชิปกว้าง ~670 + สรุปด้านขวา ~320 ไม่พอแถวเดียว ⇒ ชิปถูกบีบจนข้อความซ้อนกัน
     (ตรวจบนซับทดสอบ 6 ต.ค. 69) ⇒ ให้สรุปด้านขวาลงแถวสองเมื่อไม่พอ ชิปไม่ถูกบีบ */
  .ctxbar-in{flex-wrap:wrap;row-gap:8px}
  .ctx-chips{flex-wrap:nowrap;min-width:0}
  .ctx-chip{min-width:0}
  .ctx-sel{max-width:170px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
}
/* แถวปุ่ม — ต้นแบบประกาศ .acts เฉพาะในแถวรายการ (.lg .acts) ที่อื่นไม่มีระยะห่างระหว่างปุ่มเลย */
.acts{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
/* ปุ่มท้ายแถวตาราง (แก้ · ลบ) อยู่บรรทัดเดียว — คอลัมน์ ck-act กว้าง 1% ถ้าปล่อยตัดบรรทัด ปุ่มจะซ้อนเป็นสองแถว */
.tbl td>.acts{flex-wrap:nowrap}
.inp[readonly]{background:var(--surface-2);color:var(--ink-2)}
.auth-demo{max-width:360px;pointer-events:none}
/* ── มาตรฐานความกว้างคอลัมน์ (แผนแก้ UI รอบ 4 · ข้อ 9 · กฎ ป7 · App\Support\TableView::KINDS) ──
   ชนิดเดียวกันกว้างเท่ากันทั้งเมนู · เป็นความกว้าง "ขั้นต่ำ" ตารางยังไหลตามเนื้อหา (ไม่ล็อก — บทเรียน ก4)
   ตัวเลข/ปุ่ม = กว้างเท่าเนื้อหา (width:1%) ให้ที่เหลือไปอยู่กับชื่อและข้อความ */
.tbl th.ck-act,.tbl th.ck-num{width:1%;white-space:nowrap}
.tbl th.ck-code{min-width:88px}
.tbl th.ck-name{min-width:200px}
.tbl th.ck-place{min-width:180px}
.tbl th.ck-major{min-width:150px}
.tbl th.ck-date{min-width:118px}
.tbl th.ck-status{min-width:110px}
.tbl th.ck-text{min-width:220px}
.tbl th.ck-wide{min-width:340px}
/* ดรอปดาวน์ในตาราง (ตารางครั้งที่ฝึก) — ตาราง auto ให้คอลัมน์ข้อความกินที่หมด ดรอปดาวน์เหลือ ~60px ตัวหนังสือขาด (1 ต.ค. 69) */
.tbl select.sel-round{min-width:170px}
/* 🔴 ต้นแบบตั้ง .tbl td{overflow-wrap:anywhere} ไว้สำหรับตารางแบบ fixed — ระบบจริงใช้ตาราง auto (ขั้นต่ำตามชนิดคอลัมน์)
   anywhere ทำให้ "ความกว้างขั้นต่ำ" ของเซลล์เหลือตัวอักษรเดียว ⇒ เบราว์เซอร์เลือกหักรหัส 661000/01 และป้ายสถานะ
   เป็นก้อนหลายบรรทัด แทนที่จะขยายคอลัมน์ (แผนแก้ UI รอบ 4 ระยะ 5 · check_widths จับ "ตกบรรทัดในตาราง")
   break-word ยังหักคำยาวผิดปกติ (ลิงก์) เมื่อล้นจริง แต่ไม่ลดขั้นต่ำของคอลัมน์ · ตารางที่กว้างเกินกรอบเลื่อนใน .tbl-wrap เอง */
.tbl td{overflow-wrap:break-word;word-break:normal}
.tbl .badge,.tbl .tag{white-space:nowrap}
/* ป้ายหลายอันในกล่องเดียว — เรียงด้วยช่องว่างของข้อความแล้วตกบรรทัดชนกัน ⇒ ใช้ gap (เจ้าของงานทัก 28 ก.ย. 69 · check_widths เฝ้า) */
.badge-list{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
/* ดรอปดาวน์ในแถบค้นหา — .inp กว้าง 100% ดันลงบรรทัดใหม่ ⇒ แถบค้นหากับตัวเลือกแยกเป็นสองแถว (หน้าสถานศึกษา 28 ก.ย. 69) */
.tbar > .tbar-sel{width:auto;flex:0 1 240px;min-width:0}

/* บล็อกรูปภาพ/ไฟล์แนบในข่าว (แผนแก้ UI รอบ 4 · ของค้าง) */
.nb-fig{margin:16px 0}
.nb-fig img{display:block;max-width:100%;height:auto;border-radius:var(--r2)}
.nb-fig figcaption{font-size:var(--fs-2);color:var(--ink-3);margin-top:6px;text-align:center}

/* บุคลากรหลายบทบาท — การ์ดเลือกบทบาท (ร่าง 1B) + กล่องพื้นที่ทำงานบนเมนูซ้าย (ร่าง 2B) */
.role-pick{width:100%;max-width:720px;justify-self:stretch;margin-inline:auto}
.role-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-top:18px}
.role-tile{width:100%;display:flex;flex-direction:column;align-items:center;gap:6px;text-align:center;padding:18px 12px;
  border:1.5px solid var(--line);border-radius:var(--r3);background:var(--surface);color:var(--ink);font:inherit;cursor:pointer;transition:.15s}
.role-tile:hover{border-color:var(--accent);box-shadow:var(--sh-2)}
.role-tile.last{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.role-tile .ic{width:46px;height:46px;border-radius:12px;display:grid;place-items:center;color:var(--on-accent);font-size:20px}
.role-tile[data-role="supervisor"] .ic,.ws-i[data-role="supervisor"] svg{background:var(--terra)}
.role-tile[data-role="committee"] .ic,.ws-i[data-role="committee"] svg{background:var(--teal)}
.role-tile[data-role="admin"] .ic,.ws-i[data-role="admin"] svg{background:var(--violet)}
.role-tile b{font-size:var(--fs-3)}
.role-tile small{font-size:var(--fs-1);color:var(--ink-3)}
.rail-ws{padding:0 12px 6px;border-bottom:1px solid var(--line-2);margin-bottom:6px}
.rail-ws form{margin:0}
.ws-i svg{color:var(--on-accent);border-radius:6px;padding:3px;box-sizing:content-box}
.ws-lock{margin-left:auto;color:var(--ink-3);display:inline-flex}
.role-row{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;padding:10px 0;border-bottom:1px solid var(--line-2)}
.role-row:last-of-type{border-bottom:none}
.role-chk{display:inline-flex;align-items:center;gap:8px;cursor:pointer}
.role-row .inp-sm{max-width:220px;padding-top:6px;padding-bottom:6px}
.role-save{justify-content:flex-end}

/* ประวัติการฝึกก่อนเปิดระบบ — ช่องติ๊กรายครั้งที่ในเซลล์เดียว (เป้ากด 40px บนมือถือ) */
.prior-cks{display:flex;flex-wrap:wrap;gap:2px 16px}
.prior-ck{display:inline-flex;align-items:center;gap:6px;min-height:40px;white-space:nowrap;cursor:pointer}
.prior-note{font-size:12.5px;color:var(--muted);margin-top:4px}
/* ปุ่มเหตุผลสำเร็จรูปใต้ช่องเหตุผลตีกลับ (ระลอก ฉ · 6.10) */
.reason-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.reason-chips .chip{min-height:32px;cursor:pointer}
/* หน้าตั้งรหัสครั้งแรก: บอกว่ากำลังตั้งรหัสของบัญชีไหน + คำแนะนำให้เครื่องจำรหัส (26 ก.ย. 69) */
.pw-acct{display:flex;gap:10px;align-items:flex-start;padding:10px 12px;margin-bottom:14px;border:1px solid var(--line);border-radius:var(--r2);background:var(--surface-2)}
.pw-acct>svg{flex-shrink:0;margin-top:2px;color:var(--ink-2)}
.pw-acct .mono{overflow-wrap:anywhere}
.pw-save-list{margin:6px 0 6px 18px;padding:0;display:grid;gap:3px}
.pw-acct>div{display:grid;gap:2px;min-width:0}
.pw-save-more{margin-top:4px}
.pw-save-more>summary{cursor:pointer;font-weight:600;min-height:32px;display:flex;align-items:center}

/* กล่องเลือกไฟล์: เลือกแล้ว / กำลังลากไฟล์มาวาง (26 ก.ย. 69 · app.js uplShow) */
.upl.upl-has{border-style:solid;border-color:var(--ok-ink);background:var(--ok-soft);color:var(--ok-ink)}
.upl.upl-has svg{color:var(--ok-ink)}
.upl.upl-has strong{word-break:break-all}
.upl.upl-drag{border-color:var(--accent);background:var(--accent-soft)}
.t-nowrap{white-space:nowrap}

/* สุขภาพงานอัตโนมัติ (26 ก.ย. 69 · dashboard) — แถบซ้ายบอกสถานะ ไม่พึ่งสีอย่างเดียว (มีไอคอน+ข้อความกำกับ) */
.jh-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px}
.jh-card{display:flex;flex-direction:column;gap:6px;padding:14px 16px;border:1px solid var(--line);border-left:4px solid var(--ok);border-radius:var(--r2);background:var(--surface)}
.jh-card b{display:flex;align-items:center;gap:6px}
.jh-msg{display:flex;align-items:flex-start;gap:6px;font-weight:600;font-size:var(--fs-2);color:var(--ok-ink)}
.jh-card.jh-warn{border-left-color:var(--warn)} .jh-card.jh-warn .jh-msg{color:var(--warn-ink)}
.jh-card.jh-bad{border-left-color:var(--bad)} .jh-card.jh-bad .jh-msg{color:var(--bad-ink)}
.jh-card.jh-wait{border-left-color:var(--ink-4)} .jh-card.jh-wait .jh-msg{color:var(--ink-3)}

/* การ์ดขอบเขตที่ยังไม่มีข้อมูล (มาตรฐาน "แสดงขอบเขตก่อนมีข้อมูล" · 26 ก.ย. 69)
   หน้าตาเดียวกับ drill-card แต่ไม่ใช่ลิงก์ — เส้นประบอกว่ายังว่าง · ไม่ยกตัวตอนชี้เพราะกดไม่ได้ */
.drill-card.drill-empty{cursor:default;border-style:dashed;box-shadow:none;background:var(--surface-2,var(--surface))}
.drill-card.drill-empty:hover{border-color:var(--line);transform:none;box-shadow:none}

/* ข่าวบนหน้าหลักนักศึกษากดเปิดได้ (26 ก.ย. 69) — แถวเดิมเป็น div ⇒ ลิงก์ต้องไม่เปลี่ยนสี/ขีดเส้นใต้ */
a.side-item{color:inherit;text-decoration:none}
a.side-item:hover b{color:var(--accent)}

/* แถว "ช่องกรอก + ปุ่ม" (เจ้าของงานทัก 26 ก.ย. 69 · ปุ่มลอยสูงกว่าช่องกรอก "เจอเยอะมาก")
   ชิดล่างเสมอ — ป้ายกำกับอยู่เหนือช่อง ถ้าชิดบนหรือกึ่งกลาง ปุ่มจะไปตรงกับป้ายแทนช่อง
   และปุ่มสูงเท่าช่องกรอก ⇒ ขอบบน-ล่างตรงกันทั้งคู่ · tools/check_widths.cjs วัดทุกหน้า */
.fieldrow{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end}
.fieldrow > .field{margin-bottom:0}
.fieldrow > .btn, .fieldrow > button{min-height:42px}
/* ป้ายช่องติ๊กในแถวเดียวกับช่องกรอก ต้องอยู่กึ่งกลางช่องกรอก (หน้ารายงาน "แชร์ให้คนบทบาทเดียวกัน" · 22 ต.ค. 69) */
.fieldrow > label:has(> input[type=checkbox]){min-height:42px;display:flex;align-items:center;gap:6px}
.fieldrow > .inp{flex:1 1 260px;min-width:0}
/* แถว "ช่องกรอก + ปุ่ม" ที่ช่องมีบรรทัดทวนใต้ช่อง (ช่องวันที่ได้บรรทัด "ตรงกับวันที่ … (พ.ศ.)" จาก app.js เสมอ) — ป้ายอยู่เหนือแถว (นอก .fieldrow) ·
   ชิดบน: ถ้าชิดล่างตามปกติ ปุ่มจะไประนาบเดียวกับบรรทัดทวนใต้ช่อง ต่ำกว่าช่องกรอกเกือบ 50px (หน้าออกหนังสือ · ห่อ S1) ·
   ช่องกรอกกับปุ่มสูง 42 เท่ากัน ⇒ ชิดบนแล้วขอบบน-ล่างตรงกันทั้งคู่ · บรรทัดทวนห้อยใต้ช่องเอง */
.fieldrow.fr-start{align-items:flex-start}

/* หัวหน้าที่มีปุ่มด้านขวา — ข้อความต้องยอมหดให้ปุ่มอยู่มุมขวาบนแถวเดียวกัน
   (เดิมคำอธิบายยาวกินทั้งแถว ปุ่มตกลงไปใต้หัวเรื่อง · เจ้าของงานทัก 26 ก.ย. 69) */
/* basis 160 (เดิม 200): ปุ่มเต็ม 70% ที่จอ 1024 + ข้อความ 200 เกินแถว ⇒ ปุ่มตกใต้หัวเรื่อง (บัญชีผู้ใช้ · check_widths 6 ต.ค. 69) */
.ph-head > div:first-child:not(:last-child){flex:1 1 160px;min-width:0}
.ph-head > :last-child:not(:first-child){flex:0 1 auto;margin-left:auto;align-self:flex-start}
@media (min-width:701px){
  /* ปุ่มหลายปุ่มยาวเกินที่เหลือ ⇒ ตัดบรรทัดภายในกลุ่มเอง ชิดขวา ไม่ตกลงใต้หัวเรื่องทั้งกลุ่ม */
  .ph-head > :last-child:not(:first-child){max-width:70%;justify-content:flex-end}
}

/* ส่วนท้ายสาธารณะ (เจ้าของงานสั่ง 26 ก.ย. 69) — คอลัมน์แรก: ชุดวางตรา → ตราระบบ + ชื่อระบบไทย/อังกฤษ · คอลัมน์ที่ 4: ติดตามเรา */
.foot-brand{display:flex;flex-direction:column;gap:16px;align-items:flex-start}
.foot-sys{display:flex;align-items:center;gap:10px}
.foot-sys .brand-logo{width:40px;height:40px;flex:0 0 auto}
.foot-sys-th{display:block;color:var(--on-accent);font-family:var(--serif);font-size:var(--fs-3);line-height:1.45}
.lp-foot .foot-sys-th b{display:inline;margin:0;font-size:inherit}
.foot-sys-en{display:block;font-size:var(--fs-1);color:rgba(255,255,255,.6);letter-spacing:.02em}
.lp-foot .cols.cols-4{grid-template-columns:1.5fr 1fr 1fr 1fr}
@media (max-width:900px){.lp-foot .cols.cols-4{grid-template-columns:1fr}}
.lp-foot .foot-follow a{display:flex;align-items:center;gap:8px}
/* เครดิตท้ายหน้า (15 ต.ค. 69) — หน่วยงานที่จัดทำ แล้วชื่อผู้พัฒนา ย้ายขึ้นมาจากแถวล่างสุด */
.foot-credit{display:flex;flex-direction:column;gap:6px;padding-top:14px;border-top:1px solid rgba(255,255,255,.14);max-width:46ch}
.foot-credit-by{font-size:var(--fs-1);color:rgba(255,255,255,.72);line-height:1.6}
/* ชื่อผู้พัฒนาขนาดเท่าบรรทัดเครดิตของฝ่าย (เจ้าของงาน: เด่นไป ให้เท่ากับฝ่าย) */
.foot-dev{font-size:var(--fs-1);color:rgba(255,255,255,.72);line-height:1.6}
.lp-foot .foot-dev a{display:inline;font-size:inherit;color:inherit;text-decoration:none}
.lp-foot .foot-dev a:hover{text-decoration:underline}
.soc-ic{width:15px;height:15px;flex:0 0 auto;opacity:.85}
/* ฟอร์มลิงก์โซเชียลในศูนย์ตั้งค่า › ข้อมูลหน่วยงาน */
.soc-rows{display:flex;flex-direction:column;gap:8px}
.soc-row{display:grid;grid-template-columns:170px minmax(0,1fr) minmax(0,2fr);gap:10px;align-items:center}
.soc-head{font-size:var(--fs-1)}
@media (max-width:700px){.soc-row{grid-template-columns:1fr}.soc-head{display:none}}
/* ภาพปกวิดีโอแนะนำ (26 ก.ย. 69) — รูปอยู่ชั้นล่าง ป้าย/ปุ่มเล่น/ความยาวอยู่ชั้นบน · hq เป็น 4:3 มีแถบดำ ⇒ cover ตัดทิ้ง */
/* การ์ดวิดีโอแนะนำ — ไฟล์ของเราเล่นในการ์ด (3 ต.ค. 69 · เลิกใช้ YouTube)
   ป้าย "วิดีโอแนะนำ" อยู่มุมบน ต้องไม่บังการกดเล่น · การ์ดไม่ยกตัวตอนชี้ (ปุ่มควบคุมวิดีโอขยับตามแล้วกดพลาด) */
.lp-feat-poster{overflow:hidden;background:var(--ink)}
.lp-feat-video{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:var(--ink)}
.lp-feat-poster .lp-feat-cat{z-index:2;pointer-events:none}
.lp-feat:hover{transform:none}
/* ขั้นในการ์ดวิดีโอแนะนำ (หน้าตั้งค่า) — เลขวงกลมสีบทบาท บอกลำดับ อัปโหลดไฟล์ → รายละเอียด */
.vstep{display:flex;align-items:center;gap:9px;font-size:var(--fs-3);font-weight:700;margin-bottom:10px}
.vstep .n{display:inline-grid;place-items:center;width:28px;height:28px;border-radius:50%;background:var(--accent);color:var(--on-accent);font-size:var(--fs-2)}
.set-video{display:block;width:100%;max-width:720px;aspect-ratio:16/9;background:var(--ink);border-radius:var(--r1)}

/* แถบบันทึก (ผลตรวจ 27 ก.ย. 69 · L1) — 🔴 ปุ่มบันทึกสีเต็มเสมอ
   เดิมจางปุ่มตอนยังไม่แก้อะไร ⇒ ผู้ดูแลที่ทดสอบจริงเข้าใจว่ากดไม่ได้ (เจ้าของงานทัก 28 ก.ย. 69)
   ⇒ บอกสถานะที่ข้อความแทน: มีการแก้ไขแล้วข้อความเด่นขึ้น */
.save-bar:not(.is-clean) [data-dirty-label]{color:var(--warn-ink);font-weight:600}

/* ── คู่มือการใช้งานฉบับเต็ม (27 ก.ย. 69 · manual/book.blade.php) ──────────────────────────────
   จอ: แผ่นกระดาษขาวกว้างเท่า A4 เรียงลงมา · พิมพ์: A4 จริง ขึ้นหน้าใหม่ทุกปก/สารบัญ/บท · มีเลขหน้า
   🔴 เลขหน้าใช้ @page margin box (Chrome 131+) · เบราว์เซอร์ที่ไม่รองรับพิมพ์ได้ครบแค่ไม่มีเลขหน้า
   🔴 ข้อความท้ายหน้าเป็นค่าคงที่ใน CSS (CSP ไม่ให้ใส่สไตล์ในหน้า) จึงเขียนกลาง ๆ ใช้ได้ทุกเล่ม */
.man{background:var(--paper);min-height:100vh;padding:0 16px 48px}
/* 26 ต.ค. 69: เดิม padding:14px 0 ทับช่องซ้ายขวาของ .print-bar ⇒ ปุ่มกลับ/สั่งพิมพ์ชิดขอบกรอบ */
.man-bar{max-width:210mm;margin:14px auto;padding:12px 16px}
.man-bar-note{flex:1 1 220px;font-size:var(--fs-1);color:var(--ink-3)}
.man-doc{max-width:210mm;margin:0 auto;font-family:var(--sans);font-size:15px;line-height:1.75;color:var(--ink)}
.man-sheet,.man-chap{background:var(--surface-plain);border:1px solid var(--line);border-radius:var(--r2);
  box-shadow:var(--sh-1);padding:22mm 18mm;margin-bottom:18px}
.man-cover{min-height:260mm;display:flex;flex-direction:column;text-align:center}
.man-cover-seals{display:flex;justify-content:center}
.man-cover-vol{margin:48px 0 8px;color:var(--terra-dark);font-weight:600;letter-spacing:.06em}
.man-cover-title{font-family:var(--serif);font-size:var(--fs-8);line-height:1.3;margin:0 0 12px;text-wrap:balance}
.man-cover-for{font-size:var(--fs-4);color:var(--ink-2);margin:0}
.man-cover-sys{display:flex;gap:18px;align-items:center;justify-content:center;text-align:left;margin:auto 0;padding:32px 0}
.man-cover-sys p{margin:0}
.man-cover-name{font-family:var(--serif);font-size:var(--fs-7);font-weight:700;color:var(--pine)}
.man-cover-en{color:var(--ink-3);font-size:var(--fs-2)}
.man-cover-foot{border-top:1px solid var(--line);padding-top:16px}
.man-cover-foot p{margin:2px 0}
.man-h2{font-family:var(--serif);font-size:var(--fs-6);margin:0 0 14px}
.man-h2:not(:first-child){margin-top:28px}
.man-meta{border-collapse:collapse;width:100%}
.man-meta th,.man-meta td{text-align:left;vertical-align:top;padding:7px 10px;border-bottom:1px solid var(--line-2)}
.man-meta th{width:34%;color:var(--ink-3);font-weight:600}
.man-copy{margin-top:22px;padding:14px 16px;border:1px solid var(--line);border-radius:var(--r1);background:var(--surface-2);font-size:var(--fs-2)}
.man-copy p{margin:0 0 8px}
.man-p{margin:0 0 12px;text-align:justify;text-justify:inter-character}
.man-lead{font-size:var(--fs-4);color:var(--ink-2)}
.man-sign{text-align:right;margin-top:20px;color:var(--ink-2)}
.man-toc-list,.man-toc-list ol{list-style:none;margin:0;padding:0}
.man-toc-list>li{margin-bottom:10px}
.man-toc-list ol{padding-left:26px;margin-top:4px}
.man-toc-list a{color:var(--ink);text-decoration:none}
.man-toc-list ol a{color:var(--ink-2);font-size:var(--fs-2)}
.man-toc-list ol span{display:inline-block;min-width:3em;color:var(--ink-3)}
.man-chap-no{margin:0;color:var(--terra-dark);font-weight:600;letter-spacing:.06em}
.man-chap-title{font-family:var(--serif);font-size:var(--fs-7);line-height:1.3;margin:0 0 18px;padding-bottom:12px;border-bottom:2px solid var(--pine)}
.man-sec{margin-top:22px}
.man-h3{font-family:var(--serif);font-size:var(--fs-5);margin:0 0 10px}
.man-h3 span{color:var(--pine);margin-right:6px}
.man-steps,.man-list{margin:0 0 14px;padding-left:24px}
.man-steps li,.man-list li{margin-bottom:6px}
.man-box{border-radius:var(--r1);padding:10px 14px;margin:0 0 14px;font-size:var(--fs-2);border-left:4px solid}
.man-note{background:var(--pine-soft);border-color:var(--pine)}
.man-warn{background:var(--warn-soft);border-color:var(--warn)}
.man-box b{margin-right:6px}
.man-ui{display:inline-block;padding:0 6px;border:1px solid var(--line);border-radius:6px;background:var(--surface-2);
  font-size:.92em;font-weight:600;white-space:nowrap}
.man-fig{margin:6px 0 18px;break-inside:avoid}
.man-fig img{display:block;max-width:100%;height:auto;border:1px solid var(--line);border-radius:var(--r1)}
.man-fig-missing{padding:28px;text-align:center;border:1px dashed var(--line);border-radius:var(--r1);color:var(--ink-3)}
.man-fig figcaption{text-align:center;font-size:var(--fs-2);color:var(--ink-3);margin-top:6px}
.man-tbl{overflow-x:auto;margin:0 0 14px}
.man-tbl table{border-collapse:collapse;width:100%;font-size:var(--fs-2)}
.man-tbl th,.man-tbl td{border:1px solid var(--line);padding:6px 9px;text-align:left;vertical-align:top}
.man-tbl th{background:var(--surface-2)}
.man-end{text-align:center;color:var(--ink-3);margin:24px 0}
@media (max-width:700px){
  .man{padding:0 0 32px}
  .man-sheet,.man-chap{padding:22px 16px;border-radius:0}
  .man-cover{min-height:0}
  .man-cover-title{font-size:var(--fs-7)}
  .man-cover-sys{flex-direction:column;text-align:center}
}
/* หน้ากระดาษตอนพิมพ์คู่มือ (@page) อยู่ที่ css/print-manual.css — โหลดเฉพาะหน้าคู่มือ */
@media print{
  .man{background:none;padding:0}
  .man-doc{max-width:none;font-size:11.5pt}
  .man-sheet,.man-chap{border:0;border-radius:0;box-shadow:none;padding:0;margin:0}
  .man-sheet,.man-chap{break-before:page}
  .man-cover{break-before:auto;min-height:250mm}
  .man-h3{break-after:avoid}
  .man-box,.man-tbl tr{break-inside:avoid}
  .man-toc-list a{color:inherit}
}

/* บันทึกระบบ (Master Admin · 27 ก.ย. 69) — ข้อความยาวเลื่อนในกล่องของตัวเอง ไม่ดันหน้าให้เลื่อนแนวนอน */
.logbox{margin:0;max-height:70vh;overflow:auto;padding:12px 14px;border:1px solid var(--line);border-radius:10px;
  background:var(--surface-2);color:var(--ink);font:12px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace;white-space:pre-wrap;word-break:break-word}
/* รายการบันทึก — ใหม่สุดขึ้นก่อน · หัวรายการอ่านได้ทันที · รายละเอียดพับไว้ */
.log-sum{display:flex;flex-wrap:wrap;gap:6px;align-items:baseline}
.log-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}
.log-item{border:1px solid var(--line);border-radius:10px;padding:10px 12px;min-width:0}
.log-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:4px}
.log-head{font:12.5px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace;word-break:break-word}
.log-more{margin-top:6px}
.log-more>summary{cursor:pointer;font-weight:600;min-height:32px;display:flex;align-items:center}
.log-more .logbox{max-height:50vh;margin-top:6px}

/* ── 28 ก.ย. 69 ── */
/* แถบเตือนสภาพระบบของฝ่ายฝึก (SystemAlerts) — ปุ่มพาไปแก้ อยู่ท้ายแถบ */
.ann-bar.bad{background:linear-gradient(90deg,var(--bad),var(--bad-ink))}
.ann-bar .ann-go{flex-shrink:0;color:inherit;font-weight:700;text-decoration:underline;white-space:nowrap}
/* ป๊อบอัปประกาศ 80% ของจอ (เจ้าของงานสั่ง) · ภาพเต็มความกว้าง ไม่ครอบตัด · ยาวเกินเลื่อนในกล่อง */
.ann-pop{width:80vw;max-width:80vw;max-height:80vh;display:flex;flex-direction:column;overflow:auto}
.ann-pop-img{display:block;width:100%;height:auto;max-height:60vh;object-fit:contain;background:var(--surface-2);flex-shrink:0}
/* มีภาพ ⇒ กล่องกว้างเท่าภาพ (ไม่เกิน 80% ของจอ) ไม่เหลือแถบเทาสองข้าง (เจ้าของงานเลือกแบบ ก · 28 ก.ย. 69)
   min-content ของกล่อง = ความกว้างภาพหลังถูกจำกัดสูง 60vh · ข้อความตัดบรรทัดตามภาพ */
.ann-pop.has-img{width:min-content;min-width:min(420px,100%);max-width:80vw}
.ann-pop.has-img .ann-pop-img{width:auto;max-width:80vw;background:none}
@media (max-width:700px){.ann-pop,.ann-pop.has-img{width:100%;max-width:100%;max-height:88vh}
  .ann-pop.has-img .ann-pop-img{width:100%;max-width:100%}}
/* ภาพปกข่าว 16:9 ทุกที่ (เจ้าของงานสั่ง · เดิมหน้าอ่านข่าวตัดเป็น 1200:340) */
.news-hero{aspect-ratio:16 / 9}
.ctl-ic.news-thumb{width:96px;height:54px}
/* ลิงก์ในเนื้อข่าว/ป๊อบอัป (RichText) — ต้องดูออกว่ากดได้ */
.rt-link{color:var(--accent);text-decoration:underline;text-underline-offset:2px;word-break:break-word}
.rt-link:hover{color:var(--ink)}
/* การ์ดข่าว — ไม่เกิน 3 คอลัมน์ และไม่ยืดใบเดียวเต็มหน้า (เจ้าของงานทัก 28 ก.ย. 69 · auto-fit ยืดใบเดียวเต็มแถว ภาพปก 16:9 จึงสูงเกินจอ)
   auto-fill เก็บช่องว่างไว้ ⇒ ใบเดียวกว้างเท่า 1 ใน 3 ของแถว */
.lp-grid.news-grid{grid-template-columns:repeat(auto-fill,minmax(max(260px,calc((100% - 36px) / 3)),1fr))}
/* ภาพปกบนหน้าอ่านข่าว — 16:9 เต็มกว้างจอใหญ่สูงเกินจอ ⇒ ไม่เกิน 960px กลางหน้า */
article .news-hero{max-width:960px;margin-inline:auto}
/* การ์ดกำหนดการหน้าปฏิทินสาธารณะ (ห่อ CAL · 30 ต.ค. 69) — โครง .lc เดียวกับการ์ดข่าว
   กล่องวันที่วางมุมขวาล่างของภาพ (ป้ายหมวดอยู่มุมซ้ายบน · ชื่อ/คำอธิบายอยู่ใต้ภาพ ⇒ ไม่ทับกัน)
   meta ตกบรรทัดได้ (ช่วงวันที่ + เวลา + สถานที่ + ป้ายรายวิชา ยาวกว่าวันที่ข่าววันเดียว) · ที่ผ่านแล้วหม่นเล็กน้อยแต่ยังกดได้ */
.lc-thumb .lc-datebox{position:absolute;right:12px;bottom:12px;padding:6px 12px;background:var(--surface);box-shadow:var(--sh-2)}
.lc-meta.lc-meta-wrap{flex-wrap:wrap;gap:4px 12px}
/* บรรทัดรายวิชา (CAL2) — ขึ้นบรรทัดของตัวเองใต้วันที่/สถานที่ · ชื่อรายวิชายาวตัดบรรทัดได้ ไม่ดันการ์ดล้นแนวนอน (360 px) */
.lc-meta .lc-meta-course{flex:1 1 100%;min-width:0;align-items:flex-start;overflow-wrap:anywhere}
.lc-meta .lc-meta-course svg{flex:none;margin-top:3px}
.lc.lc-past{opacity:.72}
.lc.lc-past:hover{opacity:1}
/* รายละเอียดกำหนดการ — ฝ่ายฝึกพิมพ์ขึ้นบรรทัดใหม่เอง คงไว้ตามที่พิมพ์ (RichText::inline ใส่ <br> ให้แล้ว · escape ปกติ ไม่ใช้ HTML ดิบ)
   🔴 ต้อง normal ไม่ใช่ pre-line — RichText เก็บ \n ไว้หลัง <br> ด้วย pre-line จะขึ้นบรรทัดซ้ำสองเท่า (CAL2) */
.ev-detail{white-space:normal;overflow-wrap:anywhere;min-width:0}
/* การ์ดรายวิชา — กติกาเดียวกับการ์ดข่าว (เจ้าของงานทัก 28 ก.ย. 69 · ทับ CR-21 เฉพาะกริดนี้)
   ไม่เกิน 3 คอลัมน์ · รายวิชาเดียวกว้าง 1 ใน 3 ไม่ยืดเต็มหน้า */
.crs-grid{grid-template-columns:repeat(auto-fill,minmax(max(310px,calc((100% - 28px) / 3)),1fr))}
/* ── ตัวเลือกไฟล์กลาง <x-file-pick> (28 ก.ย. 69) — แถว [ภาพ/ไอคอน · ชื่อไฟล์ · สถานะ | เปลี่ยนไฟล์ · ลบ] ── */
.fp{display:flex;flex-direction:column;gap:8px;min-width:0}
.fp.fp-drag{outline:2px dashed var(--accent);outline-offset:4px;border-radius:10px}
.fp-rows{display:flex;flex-direction:column;gap:8px}
.fp-rows:empty{display:none}
.fp-row{display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap;padding:8px 10px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}
.fp-row.fp-bad{border-color:var(--bad);background:var(--bad-soft)}
.fp-row.fp-bad .fp-meta{color:var(--bad-ink)}
.fp-thumb{width:56px;height:42px;object-fit:cover;border-radius:6px;flex-shrink:0;background:var(--surface-2)}
.fp-ic{width:42px;height:42px;border-radius:8px;display:grid;place-items:center;flex-shrink:0;background:var(--surface-2);color:var(--ink-2)}
.fp-main{flex:1 1 220px;min-width:0;display:flex;flex-direction:column;gap:4px}
.fp-name{max-width:100%}
.fp-fname{font-weight:600;word-break:break-word;min-height:42px;display:flex;align-items:center}
.fp-meta{font-size:var(--fs-1);color:var(--ink-3)}
.fp-acts{display:flex;gap:8px;flex-wrap:wrap;flex-shrink:0}
/* 🔴 บรรทัดแรกของแถวสูง 42px เท่ากันทุกชิ้น (ไอคอน · ช่องชื่อ/ชื่อไฟล์ · ปุ่ม) แล้วชิดบน — บรรทัดสถานะใต้ช่องชื่อไม่ดันปุ่มลงอีก
   (เจ้าของงานทักซ้ำ 4 ต.ค. 69 · check_widths เฝ้า "แถวไฟล์") */
.fp-acts .btn{min-height:42px}
.fp-add{align-self:flex-start;cursor:pointer}

/* หน้า "มีอะไรใหม่" (บันทึกรุ่น · 28 ก.ย. 69) — ป้ายชนิด+กลุ่มผู้ใช้ชิดซ้าย ข้อความตามหลัง · จอแคบป้ายขึ้นบรรทัดของตัวเอง */
.chg-list{list-style:none;margin:0;padding:0;display:grid;gap:10px}
.chg-list li{display:flex;gap:10px;align-items:flex-start;padding-top:10px;border-top:1px solid var(--line)}
.chg-list li:first-child{border-top:0;padding-top:0}
.chg-tags{display:flex;gap:6px;flex:0 0 auto;min-width:190px}
@media(max-width:700px){.chg-list li{flex-direction:column;gap:6px}.chg-tags{min-width:0}}

/* การ์ดตัวเลขสถานศึกษาบนหน้าแรกกดได้ (28 ก.ย. 69) — พาไปหน้าสถานศึกษาเครือข่ายที่กรองระดับนั้นไว้ */
a.lp-num-link{display:block;text-decoration:none;transition:border-color .15s,background .15s}
a.lp-num-link:hover{border-color:rgba(255,255,255,.4);background:rgba(255,255,255,.12)}

/* ตารางโควตารายสาขา (ดูอย่างเดียว · 28 ก.ย. 69) — ตัวเลขกึ่งกลางใต้หัวสาขา · หัวโรงเรียน/สถานะกดเรียงได้ */
.xgrid th.xg-n,.xgrid td.xg-n{text-align:center}
/* หัวคอลัมน์ที่กดเรียงได้ + จัดกลาง: ป้ายชื่อกับลูกศรอยู่กลางด้วยกัน (ปกติลูกศรดันไปชิดขวา) */
.xgrid th.xg-n .th-sort{justify-content:center}
.xgrid th.xg-n .th-sort::after{margin-left:0}
.xgrid th.th-s.on{background:var(--accent-soft)}
.xgrid th.th-s.on .th-sort::after{opacity:1;color:var(--accent-dark)}
.xgrid th.th-s.on.asc .th-sort::after{content:'\25B2'}
.xgrid th.th-s.on.desc .th-sort::after{content:'\25BC'}

/* เมนูบนหน้าสาธารณะมี 8 รายการแล้ว (เพิ่ม "สถานศึกษา" 28 ก.ย. 69) — จอกลาง (701–1180px) คำในเมนูตกบรรทัดเป็นสองชั้น
   ⇒ ห้ามตัดคำในเมนู · ซ่อนชื่อหน่วยงานใต้ TIMS เมื่อจอไม่กว้างพอ · ต่ำกว่า 1180px ใช้ปุ่มสามขีดแบบเดียวกับมือถือ */
.lp-links a{white-space:nowrap}
@media(max-width:1400px){.lp-nav .brand-sub{display:none}}
@media(max-width:1180px){
  .lp-nav .lp-wrap{position:relative;}
  .lp-burger{display:inline-flex;margin-left:auto;}
  .lp-links{display:none;position:absolute;left:0;right:0;top:100%;z-index:41;
    flex-direction:column;gap:2px;background:var(--surface);
    border-bottom:1px solid var(--line);box-shadow:var(--sh-2);padding:8px 28px 14px;}
  .lp-nav.open .lp-links{display:flex;}
  .lp-links a{padding:11px 13px;}
}

/* แท็ก "เอกสารนี้สำหรับใคร" ในศูนย์ดาวน์โหลด (29 ก.ย. 69) */
fieldset.field{border:0;padding:0;min-width:0}
fieldset.field > legend{padding:0;margin-bottom:7px;font-size:var(--fs-2);font-weight:600;color:var(--ink-2)}
.aud-picks{display:flex;flex-wrap:wrap;gap:8px}
.aud-pick{display:inline-flex;align-items:center;gap:8px;min-height:40px;padding:6px 14px;border:1px solid var(--line);
  border-radius:var(--pill);background:var(--surface);font-size:var(--fs-2);cursor:pointer}
.aud-pick input{flex:none;align-self:center;width:18px;height:18px;margin:0;accent-color:var(--accent)}
.aud-pick:has(input:checked){border-color:var(--accent);background:var(--accent-soft);color:var(--accent-dark);font-weight:600}
.badge.aud{background:var(--accent-soft);color:var(--accent-dark);border-color:transparent}
.lp-rolebar{align-items:center}

/* ปุ่มรูปตาเปิด/ปิดดูรหัสผ่าน (app.js initPwEye · 30 ก.ย. 69) */
.pw-eye-wrap{position:relative;display:block}
.pw-eye-wrap>input{padding-right:46px;width:100%}
.pw-eye{position:absolute;right:4px;top:50%;transform:translateY(-50%);width:38px;height:38px;display:grid;place-items:center;
  border:0;background:transparent;color:var(--ink-3);border-radius:8px;cursor:pointer}
.pw-eye:hover,.pw-eye:focus-visible{color:var(--accent);background:var(--surface-2)}
.pw-eye .i{width:18px;height:18px}

/* ตัวกรองโรงเรียนชุดกลาง + ดรอปดาวน์พิมพ์ค้นได้ (30 ก.ย. 69) */
.school-filter .btn{white-space:nowrap}
.check-inline{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;font-size:.92rem}
.ss-q{display:block;margin-bottom:6px;min-width:250px}

/* หน้ารอคิวเลือกโรงเรียน (30 ก.ย. 69) */
.pick-wait{border-left:4px solid var(--warn)}
.pick-ahead{display:flex;align-items:baseline;gap:10px;margin:6px 0 8px}
.pick-ahead .n{font-family:var(--serif);font-size:2.4rem;line-height:1;color:var(--ink);font-variant-numeric:tabular-nums}

/* ผังรายวิชา · ขั้นของท่านตอนนี้ (ตรวจวงจรรายวิชา 1 ต.ค. 69 · แบบรวม ก+ข+ค+ง) */
.next-card{display:flex;flex-direction:column;gap:4px;align-items:flex-start;margin:14px 0 16px;padding:14px 17px;
  border:1px solid var(--line);border-left:4px solid var(--accent);border-radius:var(--r2);background:var(--surface)}
.next-card.is-wait{border-left-color:var(--line-2);background:var(--surface-2)}
.next-k,.nt-home-k{font-size:var(--fs-1);font-weight:600;color:var(--ink-3);letter-spacing:.02em}
.nt-home-k{display:block}
.next-act{font-size:var(--fs-4);color:var(--ink)}
.next-why{font-size:var(--fs-2);color:var(--ink-2)}
.next-card .btn{margin-top:6px}
.next-sub{margin-top:8px;font-size:var(--fs-1);font-weight:700;color:var(--ink-2)}
.nt-more{margin:4px 0 0;padding-left:18px;font-size:var(--fs-2);color:var(--ink-2)}
.nt-more li{margin:2px 0}
.step-own{font-size:var(--fs-1);font-weight:700;padding:1px 8px;border-radius:var(--pill);background:var(--accent-soft);color:var(--accent-dark)}
.step-warn{margin:8px 0 0;padding:7px 10px;border-radius:8px;background:var(--warn-soft);color:var(--warn-ink);
  font-size:var(--fs-2);display:flex;gap:6px;align-items:flex-start}
.step-warn .i{flex:0 0 auto;margin-top:2px}
.step-chk{list-style:none;margin:10px 0 0;padding:0;display:grid;gap:6px}
.step-chk li{display:flex;gap:7px;align-items:flex-start;font-size:var(--fs-2);color:var(--ink-2)}
.step-chk li .i{flex:0 0 auto;margin-top:2px;width:15px;height:15px}
.step-chk li.ok .i{color:var(--ok)}
.step-chk li.no .i{color:var(--bad)}
.step-chk small{display:block;color:var(--ink-3);font-size:var(--fs-1)}
.stepper .step,#next-step{scroll-margin-top:150px}

/* ── สอบถาม / แจ้งปัญหา แบบกระทู้ + การ์ด LINE OA (2 ต.ค. 69) ── */
.thread{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}
.thread-msg{border:1px solid var(--line);border-radius:12px;padding:12px 14px;background:var(--surface);max-width:92%}
.thread-msg.is-staff{align-self:flex-end;background:var(--pine-soft);border-color:var(--pine-soft)}
.thread-meta{display:flex;flex-wrap:wrap;gap:4px 10px;align-items:baseline;margin-bottom:6px;font-size:.92em}
.line-card{display:flex;gap:16px;align-items:center;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}
.line-card.is-compact{padding:12px 14px}
.line-tx{display:flex;flex-direction:column;align-items:flex-start;gap:6px;min-width:0}
.line-tx .btn{margin-top:4px}
.line-qr{width:132px;height:132px;flex:0 0 auto;object-fit:contain;border-radius:8px;background:var(--surface-plain);border:1px solid var(--line)}
@media (max-width:520px){.line-card{flex-direction:column;align-items:flex-start}}

/* ═══ บัตรประจำตัวนักศึกษาฝึกประสบการณ์วิชาชีพ (announce/card · app.js initIdCard · 2 ต.ค. 69) ═══
   canvas วาดที่ความละเอียดเต็มแล้วย่อด้วย CSS — ภาพบนจอ = ไฟล์ที่ดาวน์โหลด
   บัตรประจำตัว 1276×2032 (54 × 86 มม. ที่ 600 dpi) · ภาพโซเชียล 1080×1920 (9:16) */
.idc-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px;align-items:start;margin-top:14px}
.idc-item{margin:0;display:flex;flex-direction:column;gap:12px;align-items:stretch}
.idc-cap{display:flex;flex-direction:column;gap:2px}
.idc-canvas{display:block;width:100%;height:auto;margin-inline:auto;background:var(--pine-soft);box-shadow:var(--sh-1)}
.idc-id{aspect-ratio:1276/2032;max-width:340px;border-radius:14px}
.idc-social{aspect-ratio:1080/1920;max-width:360px;border-radius:var(--r2)}
.idc-acts{display:flex;flex-wrap:wrap;gap:10px;justify-content:center}
.idc-printsheet{display:none}
@media(max-width:760px){.idc-grid{grid-template-columns:minmax(0,1fr)}}
/* พิมพ์บัตรขนาดจริง — ซ่อนทั้งหน้า เหลือแผ่นบัตร 54 × 86 มม. + เส้นประสำหรับตัด */
@media print{
  body.idc-printing>*:not(.idc-printsheet){display:none!important}
  body.idc-printing .idc-printsheet{display:block;padding:12mm}
  .idc-cut{display:inline-block;padding:1.5mm;border:.3mm dashed #888}
  .idc-cut img{display:block;width:54mm;height:86mm}
  .idc-printsheet p{font:9pt var(--sans);color:#555;margin:3mm 0 0}
}

/* 🔴 บล็อกระดับหน้าของหน้าสาธารณะห้ามติดกัน (เจ้าของงานทัก 5 ต.ค. 69 · รอบที่ 5 ของเรื่องนี้)
   คุมที่ชั้นเดียวแบบเดียวกับหลังบ้าน — ลูกตรงของพื้นที่เนื้อหาเว้น 16px (margin ยุบกับของเดิม ไม่ทบ)
   tools/check_widths.cjs วัดหน้าสาธารณะด้วยแล้ว (เดิมวัดแค่ main.page จึงหลุดมาตลอด) */
.lp-sub main.lp-wrap > * + *{margin-top:16px}

/* ── รับสมัคร/ปรับปรุงฐานข้อมูลสถานศึกษาเครือข่าย + เกียรติบัตร (4 ต.ค. 69 · หน้าตาจากหน้าตัวอย่างที่เจ้าของงานเลือก) ── */
/* ป้ายที่มาของข้อมูลในฟอร์ม — ข้อมูลปีก่อน (เติมให้) · กรอกใหม่ทุกปี (ข้อมูลบุคคล) */
/* หน้ารับสมัคร: น้อง TIMS ชี้ทาง + การ์ดเลือก 2 ใบที่ดูกดได้ (เจ้าของงานทัก 5 ต.ค. 69) */
.rc-pick{display:flex;flex-direction:column;gap:14px}
.rc-guide{margin:0;border:1px solid var(--accent);background:var(--accent-soft)}
.rc-guide p{margin:0;line-height:1.7}
.rc-choices{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,340px),1fr));gap:14px}
.rc-choice{display:flex;flex-direction:column;gap:10px;padding:20px 22px;border-radius:var(--r3);background:var(--surface);
  border:2px solid var(--accent);box-shadow:var(--sh-2);color:var(--ink);text-decoration:none;transition:transform .15s,box-shadow .15s}
.rc-choice:hover,.rc-choice:focus-visible{text-decoration:none;transform:translateY(-2px);box-shadow:var(--sh-3, var(--sh-2));border-color:var(--accent-dark)}
.rc-choice-h{display:flex;gap:12px;align-items:center}
.rc-choice-h b{display:block;font-size:var(--fs-5);line-height:1.35}
.rc-choice-k{display:block;font-size:var(--fs-1);font-weight:700;letter-spacing:.03em;color:var(--terra-dark)}
.rc-choice small{color:var(--ink-3);line-height:1.6}
.rc-choice-go{align-self:flex-start;margin-top:auto}

/* แถบประกาศสำหรับสถานศึกษาบนหน้าแรก */
.rc-banners{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,420px),1fr));gap:14px}
.rc-banner{display:flex;align-items:center;gap:14px;padding:16px 18px;border-radius:var(--r3);background:var(--surface);
  border:1px solid var(--accent);box-shadow:var(--sh-1);color:var(--ink);text-decoration:none;flex-wrap:wrap}
.rc-banner:hover{text-decoration:none;border-color:var(--accent-dark)}
.rc-banner-tx{flex:1 1 220px;min-width:0;display:flex;flex-direction:column;gap:2px}
.rc-banner-tx small{color:var(--ink-3)}
.src-tag{display:inline-flex;align-items:center;gap:4px;margin-left:6px;padding:1px 8px;border-radius:var(--pill);font-size:var(--fs-1);font-weight:500;vertical-align:1px}
.src-prev{background:var(--surface-2);color:var(--ink-3);border:1px solid var(--line)}
.src-new{background:var(--warn-soft);color:var(--warn-ink);border:1px solid var(--warn)}
.fsec{display:flex;align-items:baseline;gap:10px;margin:0 0 14px;padding-bottom:10px;border-bottom:1px solid var(--line-2)}
.fsec-n{flex:none;width:26px;height:26px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:var(--accent);color:var(--on-accent);font-size:var(--fs-2);font-weight:700}
.fsec b{font-size:var(--fs-4)}
.fsec small{color:var(--ink-3);font-size:var(--fs-2)}
/* ฟอร์มทีละขั้น — ไม่มี JS = เห็นทุกขั้นเรียงกัน (ส่งได้เหมือนเดิม) · app.js ซ่อนขั้นที่ไม่ใช่ขั้นปัจจุบัน */
.rc-wiz{display:flex;gap:10px;flex-wrap:wrap;counter-reset:wz;margin:0;padding:0;list-style:none}
.rc-wiz li{flex:1 1 120px;display:flex;flex-direction:column;gap:6px;padding:10px 12px;border-radius:var(--r2);border:1px solid var(--line);background:var(--surface);color:var(--ink-3);font-size:var(--fs-2)}
.rc-wiz li::before{counter-increment:wz;content:'ขั้นที่ ' counter(wz);font-size:var(--fs-1);font-weight:700;letter-spacing:.02em}
.rc-wiz li.done{border-color:var(--ok);color:var(--ok-ink)}
.rc-wiz li.now{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-dark);font-weight:600}
.rc-form{display:flex;flex-direction:column;gap:16px;margin-top:16px}
.rc-step{display:flex;flex-direction:column;gap:16px}
.rc-on .rc-step:not(.on){display:none}
.rc-nav{display:flex;gap:10px;align-items:center;justify-content:space-between;flex-wrap:wrap}
.rc-nav [data-wiz-note]{color:var(--ink-3);font-size:var(--fs-2)}
.rc-only{display:none}
.rc-nav .rc-hide{visibility:hidden}
.rc-on .rc-only{display:inline-flex}
.rc-on ol.rc-only{display:flex}
.rc-on .rc-submit:not(.on){display:none}
.map-out{display:flex;gap:10px 24px;flex-wrap:wrap;align-items:center;margin-top:10px;font-size:var(--fs-2)}
.map-out b{font-variant-numeric:tabular-nums}
.map-frame{display:block;width:100%;aspect-ratio:16/7;border:1px solid var(--line);border-radius:var(--r2);background:var(--surface-2)}
.major-tbl td{vertical-align:middle}
.major-tbl td .inp{min-width:0;width:100%;padding:7px 9px}
.major-tbl td.num{width:120px}
.major-tbl .ratio-warn{color:var(--warn-ink);font-size:var(--fs-1);display:block;margin-top:4px}
.lv-picks{display:flex;flex-wrap:wrap;gap:4px 12px;font-size:var(--fs-2)}
.lv-picks label{display:inline-flex;gap:4px;align-items:center}
.must-up{display:flex;flex-direction:column;gap:10px;padding:14px 16px;border:1px dashed var(--accent);border-radius:var(--r2);background:var(--accent-soft)}
.must-up b{display:block}
.must-up small{color:var(--ink-3)}
.refbox{display:flex;gap:16px 28px;flex-wrap:wrap;align-items:flex-start;padding:18px 20px;border-radius:var(--r3);background:var(--ok-soft);border:1px solid var(--ok)}
.refbox .n{font-size:var(--fs-6);font-weight:700;letter-spacing:.04em;font-variant-numeric:tabular-nums;color:var(--ok-ink)}
.refbox .l{font-size:var(--fs-1);color:var(--ok-ink);font-weight:600}
.copy-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.link-qr .qr{display:block;width:132px;height:132px}
.copy-line code{flex:1 1 240px;min-width:0;overflow-wrap:anywhere;padding:9px 12px;border-radius:var(--r2);background:var(--surface-2);border:1px solid var(--line);font-size:var(--fs-2)}
/* หน้าตรวจของฝ่ายฝึก — เทียบทุกช่อง แถวที่เปลี่ยนเป็นสีเหลือง */
.cmp td,.cmp th{vertical-align:top}
.cmp .k{color:var(--ink-3)}
.cmp tr.chg td{background:var(--warn-soft)}
.cmp tr.chg td.k{color:var(--warn-ink);font-weight:600}
.cmp .old{color:var(--ink-3);text-decoration:line-through}
.cmp .new{font-weight:600}
.cmp .same{color:var(--ink-4)}
.cmp col.c-k{width:30%}
.cmp col.c-v{width:35%}
.review-grid{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:16px;align-items:start}
.review-side{display:flex;flex-direction:column;gap:16px}
@media (max-width:1100px){.review-grid{grid-template-columns:1fr}}
/* เอกสาร A4 (เอกสารแนบ · สรุปข้อมูลที่ส่ง · หนังสือตอบรับ · เกียรติบัตร) */
.a4-stage{display:flex;flex-direction:column;align-items:safe center;gap:24px;padding:24px 16px;background:var(--paper-2);overflow-x:auto;min-height:100vh}
.a4{flex:none;width:210mm;min-height:297mm;box-sizing:border-box;padding:18mm 18mm 16mm;background:#fff;color:#1d2b2a;box-shadow:var(--sh-2);font-size:14.5px;line-height:1.75}
.a4.land{width:297mm;min-height:210mm;padding:0}
.a4 h1{font-family:var(--serif);font-size:22px;text-align:center;margin:18px 0 6px;line-height:1.4}
.a4 h2{font-size:16px;margin:18px 0 6px}
.a4 .sub{text-align:center;color:var(--ink-3);margin:0 0 14px}
.a4 ol,.a4 ul{margin:6px 0 6px 22px;padding:0}
.a4 li{margin:3px 0}
.doc-head{display:flex;align-items:center;gap:14px;padding-bottom:12px;border-bottom:1px solid var(--line)}
.doc-head img{height:64px;width:auto}
.doc-head .t{line-height:1.45;font-size:13.5px}
.doc-head .t b{font-size:15px}
.doc-head .ref{margin-left:auto;text-align:right;font-size:12px;color:var(--ink-3);line-height:1.5}
.qr-row{display:flex;gap:18px;align-items:center;padding:14px;border:1px solid var(--line);border-radius:8px;margin:12px 0}
.qr-row .qr{flex:none;width:118px;height:118px}
.qr-row .url{font-weight:700;overflow-wrap:anywhere}
.box-line{display:inline-block;min-width:240px;border-bottom:1px dotted var(--ink-3);height:1.2em;vertical-align:-2px}
.box-line.bl-l{min-width:380px}
.box-line.bl-s{min-width:120px}
.chk{display:inline-block;width:14px;height:14px;border:1.5px solid #1d2b2a;border-radius:2px;margin:0 6px 0 2px;vertical-align:-2px}
.auto-iss{margin:14px auto 0;width:fit-content;max-width:100%;padding:8px 14px;border:1px solid var(--pine);border-radius:var(--r2);display:flex;flex-direction:column;gap:2px;text-align:center;font-size:13px;color:var(--ink-2)}
.auto-iss b{color:var(--pine-deep);font-size:14px}
.doc-foot{margin-top:24px;padding-top:10px;border-top:1px solid var(--line);font-size:12px;color:var(--ink-3);display:flex;justify-content:space-between;gap:12px}
.kv-doc{display:grid;grid-template-columns:200px minmax(0,1fr);gap:4px 14px;margin:0}
.kv-doc dt{color:var(--ink-3)}
.kv-doc dd{margin:0}
.doc-tbl{width:100%;border-collapse:collapse;font-size:13.5px;margin:6px 0}
.doc-tbl th,.doc-tbl td{border:1px solid var(--line);padding:5px 8px;text-align:left}
.doc-tbl th{background:var(--surface-2);font-weight:600}
.doc-tbl td.c,.doc-tbl th.c{text-align:center;font-variant-numeric:tabular-nums}
/* เกียรติบัตรลายดอกลำดวน แบบ ข — สีประจำคณะ (ฟ้า) · ลายพื้นเป็น SVG ขนาดเท่ากระดาษ วางหลังเนื้อหา */
.cert-v{position:relative;width:297mm;height:210mm;overflow:hidden;background:#fff;--gold:#c08a2e;--gold-soft:#f6ead2;--leaf:#4f7a46;
  --fac:#1d6fb8;--fac-dark:#164f8a;--fac-soft:#e3f0fb;--pine:var(--fac);--pine-deep:var(--fac-dark);--pine-soft:var(--fac-soft);--terra-soft:#e6f2fc;--terra-dark:#b8860b}
.cert-v .cv-bg{position:absolute;inset:0;width:100%;height:100%}
.cert-v .cv-ct{position:relative;height:100%;box-sizing:border-box;padding:24mm 32mm 25mm;display:flex;flex-direction:column;align-items:center;text-align:center}
.cv-mid{margin:auto 0;display:flex;flex-direction:column;align-items:center}
.cv-seals{display:flex;gap:16px;align-items:center;justify-content:center}
.cv-seals img{height:72px;width:auto}
.cv-fac{margin-top:12px;font-family:var(--serif);font-size:44px;line-height:1.3;font-weight:600;color:var(--fac-dark);letter-spacing:.01em}
.cv-uni{font-family:var(--serif);font-size:26px;line-height:1.35;color:var(--fac)}
.cv-give{font-size:17px;color:var(--fac)}
.cv-name{font-family:var(--serif);font-size:38px;color:var(--fac-dark);margin:6px 0 10px;padding:0 30px 6px;border-bottom:1.5px solid var(--gold)}
.cv-at{margin:-4px 0 10px;font-family:var(--serif);font-size:24px;color:var(--fac)}
.cv-why{max-width:200mm;font-size:18px;line-height:1.8;color:var(--ink-2)}
.cv-bless{max-width:210mm;margin-top:6px;font-size:16px;line-height:1.7;color:var(--ink-2)}
.cv-given{margin-top:10px;font-size:16px}
.cv-foot{width:100%;display:grid;grid-template-columns:1fr auto 1fr;align-items:end;gap:16px}
.cv-meta{display:flex;gap:8px;align-items:center;text-align:left;font-size:10.5px;color:var(--ink-3);line-height:1.5}
.cv-meta .qr{width:58px;height:58px;flex:none}
.cv-sign{display:flex;flex-direction:column;align-items:center;line-height:1.55;font-size:14.5px}
.cv-sign .sig{height:40px;width:auto}
.cv-sign .sig-gap{display:block;height:40px}
.cv-sign .auto-note{margin-top:6px;padding:2px 10px;border:1px solid var(--line);border-radius:999px;font-size:11.5px;color:var(--ink-3);line-height:1.6}
.cv-right{display:flex;justify-content:flex-end}
.cv-right .medal{width:70px;height:auto}
/* ตัวอย่างเกียรติบัตรย่อในหน้าตั้งข้อความ (297 × 210 มม. ย่อ 0.34) */
.pv-cert{width:382px;max-width:100%;aspect-ratio:297/210;overflow:hidden;border:1px solid var(--line);border-radius:var(--r2);background:#fff;box-shadow:var(--sh-1)}
.pv-cert-in{transform:scale(.34);transform-origin:0 0;width:297mm}
/* หน้ากระดาษเกียรติบัตร (@page แนวนอน ขอบ 0) อยู่ที่ css/print-cert.css — โหลดเฉพาะหน้าพิมพ์เกียรติบัตร */
/*
 * เอกสารทุกชนิดที่ระบบออก — A4 แนวตั้งเท่านั้น (ตรวจรอบ 5 ต.ค. 69 · เจ้าของงานสั่ง)
 * 🔴 .paged = app.js แบ่งแผ่นแล้ว: สูง 297 มม. พอดีทุกแผ่น (สั้นก็เต็มแผ่น) · เนื้อหาเกินไหลไปแผ่นถัดไป
 *    ไม่มี JS = สูงอย่างน้อย A4 ตามเดิม (ไม่ซ่อนเนื้อหา)
 * 🔴 หน้ากระดาษ (@page A4 แนวตั้ง ขอบ 0) อยู่ที่ css/print-a4.css — โหลดเฉพาะ layouts.a4 · ระยะขอบอยู่ใน padding ของแผ่นแล้ว
 *    ⇒ ที่พิมพ์ออกตรงกับที่เห็นบนจอทุกแผ่น
 * 🔴 ห้ามกลับไปใช้ @page ที่มีชื่อ (page: a4doc) + @page ไม่มีชื่อของคู่มือใน app.css ไฟล์เดียวกัน (เจ้าของงานเจอ 5 ต.ค. 69)
 *    ช่องพิมพ์ของ Chrome (ระยะขอบ "ค่าเริ่มต้น") ใช้ขอบของ @page ไม่มีชื่อ ⇒ ได้ขอบคู่มือ 16–18 มม. + ข้อความท้ายหน้าของคู่มือ
 *    แผ่นสูง 297 มม. ล้นไปแผ่นใหม่ ⇒ 1 แผ่นพิมพ์ออกมา 2 หน้า (หน้าหลังว่าง) · ตัวพิมพ์ PDF ไร้หน้าจอยังเคารพชื่อหน้า จึงตรวจไม่เจอ
 */
.a4.paged{height:297mm;min-height:297mm;overflow:hidden;position:relative}
.a4-pno{position:absolute;right:18mm;bottom:7mm;font-size:11px;color:var(--ink-3)}
.a4 .doc-tbl tr{break-inside:avoid}
/* ช่องลายมือชื่อของผู้รับ (<x-doc-sign>) — ป้ายชิดขวาคอลัมน์เดียว เส้นประยาวเท่ากันทุกบรรทัด */
.sign-row{margin-top:30px;display:flex;justify-content:flex-end;align-items:center;gap:28px}
.sign-grid{display:grid;grid-template-columns:auto 64mm;column-gap:10px;row-gap:12px;align-items:end;line-height:1.4}
.sign-grid .sl{text-align:right;white-space:nowrap}
.sign-grid .sv{border-bottom:1px dotted #1d2b2a;height:1.4em}
.sign-grid .sv-par{display:flex;justify-content:space-between;align-items:flex-end;border-bottom:1px dotted #1d2b2a}
.sign-grid .sv-par i{font-style:normal;line-height:1}
.sign-grid .sv-t{text-align:center;font-size:13px;color:var(--ink-2)}
@media print{
  .a4-stage{display:block;padding:0;background:#fff;min-height:0;zoom:1!important}
  .a4{box-shadow:none;margin:0;break-after:page}
  .a4:last-of-type{break-after:auto}
}

/* หน้าตอบของโรงเรียน (QR · มือถือ) — การ์ดรายคน แทนตารางหลายคอลัมน์ */
.sr-list{display:grid;gap:10px}
.sr-person{display:grid;gap:8px;margin:0;padding:12px 14px;border:1px solid var(--line);border-radius:var(--r3)}
.sr-who{display:grid;gap:2px;padding:0}

/* เมนู "สถานศึกษาเครือข่าย" — แถบหัวกลาง: ปีการศึกษาตัวเดียว + แท็บ (แผนหัวข้อ 17) */
.hub-bar{display:grid;gap:10px;margin-bottom:14px}
.hub-year{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.hub-year label{font-weight:600}
.hub-year .inp{width:auto;min-width:200px}

/* รายชื่อในตารางหนังสือขอความอนุเคราะห์ — รายคนพร้อมผลตอบ */
.req-who{list-style:none;margin:6px 0 0;padding:0;display:grid;gap:4px}
.doc-tbl td.rsn{width:28%}
/* แบบตอบกลับ: ชื่อ/สาขา/หัวตารางบรรทัดเดียว ให้ช่องเหตุผลรับที่เหลือ (เพิ่มคอลัมน์สาขา 6 ต.ค. 69) */
.reply-tbl th,.reply-tbl td:not(.rsn){white-space:nowrap}
.reply-tbl td.rsn{width:auto;min-width:96px}
.a4-slips .doc-head{margin-bottom:12px}
.a4-slips .slip{margin:0 0 8px}
.a4-conth{position:absolute;left:18mm;right:18mm;top:8mm;font-size:11px;color:var(--ink-3);border-bottom:1px solid var(--line);padding-bottom:3px}

/* ผลตรวจ M1 (5 ต.ค. 69) — ตารางเลื่อนแนวนอนได้: ขอบด้านที่ยังมีคอลัมน์ซ่อนอยู่จางลง (app.js initScrollCue) */
.tbl-wrap.sx-r,.xgrid-wrap.sx-r{
  -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 40px),rgba(0,0,0,.2));
  mask-image:linear-gradient(to right,#000 calc(100% - 40px),rgba(0,0,0,.2))}
.tbl-wrap.sx-l,.xgrid-wrap.sx-l{
  -webkit-mask-image:linear-gradient(to right,rgba(0,0,0,.2),#000 40px);
  mask-image:linear-gradient(to right,rgba(0,0,0,.2),#000 40px)}
.tbl-wrap.sx-l.sx-r,.xgrid-wrap.sx-l.sx-r{
  -webkit-mask-image:linear-gradient(to right,rgba(0,0,0,.2),#000 40px,#000 calc(100% - 40px),rgba(0,0,0,.2));
  mask-image:linear-gradient(to right,rgba(0,0,0,.2),#000 40px,#000 calc(100% - 40px),rgba(0,0,0,.2))}
.sx-hint{margin:10px 0 6px;font-size:var(--fs-1);color:var(--ink-3);text-align:right}
/* เหตุผลใต้ปุ่มในคอลัมน์จัดการ — ตัดบรรทัดได้ ไม่ดันคอลัมน์ให้กว้างเกินจอ (ผลตรวจ D1) */
.act-note{display:inline-block;max-width:200px;white-space:normal;line-height:1.45}

/* ผลตรวจ M2 (5 ต.ค. 69) — หน้าเลือกโรงเรียน: จอโทรศัพท์เป็นการ์ดรายแห่ง · ปุ่มบันทึกเต็มกว้างติดล่างจอ */
.pick-name label{cursor:pointer}
@media (max-width:700px){
  .pick-cards .tbl-wrap{max-height:none;overflow:visible;border:0;background:none}
  .pick-cards .tbl,.pick-cards .tbl tbody{display:block}
  .pick-cards .tbl thead{display:none}
  .pick-cards .tbl tr{display:grid;grid-template-columns:auto 1fr;gap:3px 12px;margin:0 0 10px;padding:12px 14px;
    border:1px solid var(--line);border-radius:var(--r3);background:var(--surface)}
  .pick-cards .tbl tr:has(input:checked){border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-soft)}
  .pick-cards .tbl td{display:block;padding:0;border:0;white-space:normal}
  .pick-cards .tbl td:first-child{grid-column:1;grid-row:1 / span 6;align-self:start;padding-top:2px}
  .pick-cards .tbl td:not(:first-child){grid-column:2}
  .pick-cards .tbl td.pick-name{order:-1}
  .pick-cards .tbl td:first-child input{width:22px;height:22px}
  .pick-cards .tbl td.pick-name{font-weight:600;font-size:var(--fs-3)}
  .pick-cards .tbl td.pick-name label{display:block;padding:2px 0}
  .pick-cards .tbl td[data-l]{font-size:var(--fs-2);color:var(--ink-2)}
  .pick-cards .tbl td[data-l]::before{content:attr(data-l) " · ";color:var(--ink-3)}
  .pick-cards .pick-save{position:sticky;bottom:12px;width:100%;margin-top:6px;min-height:46px}
}

/* ผลตรวจ M4 (5 ต.ค. 69) — จอโทรศัพท์การ์ดตัวเลขเรียงคอลัมน์เดียวยาว 4 ใบ ⇒ 2 คอลัมน์ ย่อไอคอน/ระยะ */
@media (max-width:560px){
  .stats{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
  .stat{padding:12px}
  .stat .ic{width:34px;height:34px;margin-bottom:8px}
  .stat .n{font-size:var(--fs-5)}
  .stat .l{font-size:var(--fs-1)}
}

/* ผลตรวจ M5 · D2 (5 ต.ค. 69) — ตารางกรอกคะแนน: ช่องว่างดูเหมือนข้อความ ไม่รู้ว่ากรอกได้ ⇒ มีกรอบจาง ๆ ทุกขนาดจอ ·
   จอโทรศัพท์คอลัมน์ชื่อแคบลง (ตรึงซ้ายกินจอน้อยลง) ข้อความรองตัดบรรทัดไม่ล้น */
.xg-in:not(:disabled):not(.txt){box-shadow:inset 0 0 0 1px var(--line);border-radius:4px}
.xg-in:not(:disabled):not(.txt):focus{box-shadow:inset 0 0 0 2px var(--accent)}
@media (max-width:700px){
  .xgrid .xg-lead{min-width:0;width:118px;max-width:118px}
  .xgrid .xg-lead b{font-size:var(--fs-2)}
  .xgrid .xg-lead small{display:block;white-space:normal;overflow-wrap:break-word;line-height:1.3}
  .xg-in{min-width:46px}
}

/* ผลตรวจ M7 (5 ต.ค. 69) — "เปิดลิงก์งาน" เป็นป้ายเล็ก แตะยากบนมือถือ ⇒ เป้ากดอย่างน้อย 40px บนจอสัมผัส */
a.work-link{display:inline-flex;align-items:center;gap:5px}
@media (max-width:700px), (pointer:coarse){
  a.work-link{min-height:40px;padding:6px 14px;font-size:var(--fs-2)}
}

/* ── ผลตรวจระลอก 3 (5 ต.ค. 69) ── */
/* D10 — tims-ui .tip-warn svg ระบายไอคอนทุกตัวในแถบเป็นสีเตือน รวมไอคอนในปุ่ม (ลูกศรน้ำตาลบนปุ่มน้ำเงิน) */
.tip-warn .btn svg{color:currentColor}
/* M13 — ป้าย–ค่าในการ์ดข้อมูลการฝึก: จอแคบเรียงป้ายบนค่า ไม่บีบป้ายเป็น 4 บรรทัด */
@media (max-width:560px){
  .kv-row{flex-direction:column;gap:2px}
  .kv-row>b,.kv-row>span:last-child{text-align:left}
}
/* D15 · ระลอก 3 แบบ 5A (เจ้าของงานเลือก 5 ต.ค. 69) — ปุ่มลบ/ถอน/ลงทะเบียนในแถวตารางเป็นแบบเส้นขอบ
   สียังบอกความหมายเดิม แต่ไม่กวาดตาไปจากรายชื่อ · พื้นทึบเฉพาะตอนชี้เมาส์ และในกล่องยืนยัน */
.tbl td .btn-bad:not(:disabled){background:var(--surface);color:var(--bad-ink);border-color:color-mix(in srgb,var(--bad) 45%,transparent);box-shadow:none}
.tbl td .btn-bad:not(:disabled):hover{background:var(--bad);border-color:var(--bad);color:var(--on-accent)}
.tbl td .btn-ok:not(:disabled){background:var(--surface);color:var(--ok-ink);border-color:color-mix(in srgb,var(--ok) 45%,transparent);box-shadow:none}
.tbl td .btn-ok:not(:disabled):hover{background:var(--ok-ink);border-color:var(--ok-ink);color:var(--on-accent)}
/* ── เมนูรูปโปรไฟล์ (ระลอก 3 แบบ 2A) — การแจ้งเตือน · ข้อมูลส่วนตัว · คู่มือ · มีอะไรใหม่ · ออกจากระบบ ── */
.usr-menu{position:relative}
.usr-menu>summary{list-style:none;cursor:pointer;border-radius:var(--r2);padding:3px 4px}
.usr-menu>summary::-webkit-details-marker{display:none}
.usr-menu>summary:focus-visible{outline:2px solid var(--accent-btn);outline-offset:2px}
.usr-menu[open]>summary,.usr-menu>summary:hover{background:var(--accent-soft)}
.usr-pop{position:absolute;right:0;top:calc(100% + 8px);width:max-content;min-width:min(240px,88vw);max-width:88vw;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r2);box-shadow:var(--sh-3);padding:6px;z-index:60;display:flex;flex-direction:column;gap:2px}
.usr-pop form{margin:0}
.usr-pop .nav-i{width:100%}
/* ── แถบบริบทบนโทรศัพท์ (ระลอก 3 แบบ 8A · ผลตรวจ M10) — app.js เติม .can-fold · ไม่มี JS = แถบเต็ม ── */
.ctx-fold-btn{display:none}
@media (max-width:700px){
  .ctxbar.can-fold .ctx-fold-btn{display:flex;align-items:center;gap:8px;width:100%;min-height:44px;padding:8px 16px;border:0;background:transparent;
    font:inherit;font-size:var(--fs-2);font-weight:600;color:var(--ink);text-align:left;cursor:pointer}
  .ctx-fold-btn span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .ctx-fold-btn svg{color:var(--ink-3);flex-shrink:0}
  .ctxbar.can-fold.open .ctx-fold-btn svg:last-child{transform:rotate(180deg)}
  .ctxbar.can-fold:not(.open) .ctxbar-in{display:none}
}
/* 1B — ปุ่มเด่นสีส้ม (.btn-terra) เป็นปุ่มหลักอีกแบบ ⇒ ใช้น้ำเงินเดียวกับปุ่มหลัก (ส้มอิฐเคยอ่านเป็นปุ่มลบ) */
.btn-terra{background:var(--accent-btn);box-shadow:0 4px 12px color-mix(in srgb,var(--accent-btn) 28%,transparent)}
.btn-terra:not(:disabled):hover{background:var(--accent-btn-h)}
/* ไอคอนบนสี่เหลี่ยมสีบทบาทของพื้นที่ทำงาน — รายการที่เลือกอยู่เดิมได้สีบทบาททับ เลยกลืนกับพื้น */
.ws-i.on svg{color:var(--on-accent)}

/* ช่วงเวลาของขั้น (เคาะ 6 ต.ค. 69) — ป้ายนับถอยหลัง/ล่าช้าข้างสถานะ · เตือนอย่างเดียว */
.step-win{flex:none;font-weight:500}

/* หน้า "มีอะไรใหม่" ยุบได้ (เคาะ 6 ต.ค. 69) — หัวรุ่นเป็นปุ่มกดเปิด/ปิด */
.chg-rel>summary{cursor:pointer;list-style:none}
.chg-rel>summary::-webkit-details-marker{display:none}
.chg-rel>summary .chg-chev{margin-left:auto;display:inline-flex;transition:transform .2s}
.chg-rel[open]>summary .chg-chev{transform:rotate(180deg)}
.chg-rel+.chg-rel{margin-top:14px}
.chg-older{margin-top:14px}
.fold-card>summary{cursor:pointer;list-style:none}
.fold-card>summary::-webkit-details-marker{display:none}
.fold-card>summary .fold-chev{margin-left:auto;display:inline-flex;transition:transform .2s}
.fold-card[open]>summary .fold-chev{transform:rotate(180deg)}
/* หัวการ์ดแบบพับ: เส้นใต้ของ .sec-h ค้างเป็นขีดสั้นใต้ชื่อ ⇒ ถอดเฉพาะในหัวที่พับได้ */
.fold-card>summary .sec-h,.chg-older>summary .sec-h{border-bottom:0;padding-bottom:0}
.chg-older>summary{cursor:pointer;list-style:none}
.chg-older>summary::-webkit-details-marker{display:none}
.chg-older>summary .chg-chev{margin-left:auto;display:inline-flex;transition:transform .2s}
.chg-older[open]>summary .chg-chev{transform:rotate(180deg)}
.chg-older .chg-rel{box-shadow:none}

/* ═══ ติดตั้งเป็นแอป (PWA ระดับ ก · 7 ต.ค. 69) — หน้า "ติดตั้งแอป" + แผ่นชวนบนหน้าหลัก ═══
   🔴 แผ่นชวนไม่บังการใช้งาน: ปิดได้ด้วยพื้นหลัง/ปุ่มไว้ทีหลัง · iPad ลอยมุมขวาบนใกล้ปุ่มแชร์ (ปุ่มแชร์ iPad อยู่บน ไม่ใช่ล่าง) */
.inst-status .inst-icon{flex:0 0 auto}
.inst-steps{list-style:none;margin:12px 0 0;padding:0;display:grid;gap:10px}
.inst-steps li{display:grid;grid-template-columns:32px 1fr;gap:12px;align-items:center;color:var(--ink-2);line-height:1.55}
.inst-steps .n{width:32px;height:32px;border-radius:50%;background:var(--pine-soft);color:var(--pine);font-weight:700;display:grid;place-items:center}
.inst-steps .n.last{background:var(--terra);color:var(--on-accent)}
.inst-key{display:inline-flex;align-items:center;gap:4px;background:var(--surface-2);color:var(--pine-deep);border-radius:6px;padding:0 7px;font-weight:600;white-space:nowrap}
.inst-key .i{width:15px;height:15px}
.inst-icon{border-radius:14px;flex:none}
.inst-notes{margin:8px 0 0;padding-left:20px;display:grid;gap:8px;color:var(--ink-2);line-height:1.6}
.inst-wrap{position:fixed;inset:0;z-index:900}
.inst-dim{position:absolute;inset:0;background:rgba(20,26,46,.45)}
.inst-sheet{position:absolute;left:0;right:0;bottom:0;max-height:88vh;overflow:auto;background:var(--surface);
  border-radius:22px 22px 0 0;padding:12px 20px calc(20px + env(safe-area-inset-bottom,0px));box-shadow:var(--sh-3)}
.inst-wrap[data-v="ipad"] .inst-sheet{left:auto;right:16px;bottom:auto;top:calc(12px + env(safe-area-inset-top,0px));width:min(380px,calc(100vw - 32px));border-radius:18px}
.inst-wrap[data-v="ipad"] .inst-grab{display:none}
.inst-grab{width:42px;height:5px;border-radius:3px;background:var(--line);margin:0 auto 14px}
.inst-head{display:flex;gap:12px;align-items:center}
.inst-head b{display:block;font-size:var(--fs-5);color:var(--pine-deep)}
.inst-head span{font-size:var(--fs-2);color:var(--ink-3)}
.inst-why{list-style:none;margin:14px 0 0;padding:0;display:grid;gap:8px;color:var(--ink-2)}
.inst-why li{display:flex;gap:10px;align-items:center}
.inst-why .i{color:var(--pine)}
.inst-ttl{font-size:var(--fs-5);font-weight:700;color:var(--pine-deep)}
.inst-sub{font-size:var(--fs-2);color:var(--ink-3);margin-top:2px;line-height:1.5}
.inst-btns{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:18px}
.inst-btns .btn{justify-content:center;min-height:44px}
@media (min-width:701px){.inst-wrap:not([data-v="ipad"]) .inst-sheet{left:50%;right:auto;width:440px;transform:translateX(-50%)}}

/* สถานะว่างในการ์ด = แถบบรรทัดเดียว (13 ต.ค. 69) — เดิม .empty สูง ~300px ดันงานหลักลงไปข้างล่าง
   การ์ดยังอยู่ (เห็นหัวการ์ด + เลข 0) แค่เนื้อหาย่อลง · ปุ่มไปทำต่อชิดขวา · จอแคบปุ่มตกบรรทัดเอง */
.empty.empty-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap;text-align:left;
  padding:10px 14px;border:1px dashed var(--line);border-radius:var(--r2);background:var(--surface-2)}
.empty.empty-row > svg{font-size:var(--fs-5);margin:0;flex:0 0 auto}
.empty-row .empty-txt{flex:1 1 220px;min-width:0;color:var(--ink-2)}
.empty-row .empty-txt b{color:var(--ink);margin-right:6px}
.empty-row .empty-sub,.empty-row .empty-why{font-size:var(--fs-2);color:var(--ink-3);margin:0}
.empty-row .empty-sub + .empty-why::before{content:" · "}
.empty.empty-row .empty-go{margin-top:0;flex:0 0 auto}

/* ผังรายวิชา — ขั้นที่ไม่ใช่ขั้นปัจจุบันยุบเหลือแถวหัวขั้น (13 ต.ค. 69) · กดแล้วกางรายละเอียดเดิมครบ */
.step-x > summary{cursor:pointer;list-style:none}
.step-x > summary::-webkit-details-marker{display:none}
.step-x-more{margin-left:auto;font-size:var(--fs-1);color:var(--ink-3);font-weight:600;white-space:nowrap}
.step-x-more::after{content:" ▾"}
.step-x[open] .step-x-more::after{content:" ▴"}
.step-x[open] > summary{margin-bottom:8px}
.step-x > summary:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:var(--r1)}

/* ═══ กรอกคะแนน v1.8.0 (ประชุมฝ่ายฝึก 14 ต.ค. 69 · ผังหน้าจอ 3–5) ═══
   โหมดผลรวม: ช่องเดียวต่อคน ตัวใหญ่ · ปุ่ม "เต็ม" ต่อแถว · คอลัมน์ผู้กรอก
   จอ < 700px โหมดผลรวมเป็นการ์ดรายคน (ตารางเลื่อนข้างบนโทรศัพท์กรอกยาก) · โหมดรายข้อยังเป็นตารางเลื่อน */
.xg-in.xg-big{min-width:110px;font-weight:700;font-size:var(--fs-3)}
.xgrid td.xg-act{padding:0 6px;text-align:center}
.xgrid td.xg-who{padding:0 12px;color:var(--ink-3);font-size:var(--fs-1);white-space:normal;min-width:120px}
.score-total-in{width:160px;flex:0 0 160px}
.save-sticky{position:sticky;bottom:0;z-index:5;background:var(--surface);padding:10px 0 calc(10px + env(safe-area-inset-bottom, 0px));border-top:1px solid var(--line-2)}
@media (max-width:700px){
  .xgrid-wrap.xg-total-wrap{max-height:none;overflow:visible;border:0;box-shadow:none;background:transparent}
  .xgrid.xg-total,.xgrid.xg-total tbody{display:block;width:100%;min-width:0}
  .xgrid.xg-total thead{display:none}
  .xgrid.xg-total tbody tr{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px 10px;align-items:end;
    padding:12px;margin-bottom:10px;border:1px solid var(--line);border-radius:var(--r2);background:var(--surface)}
  .xgrid.xg-total tbody tr.ok{box-shadow:inset 4px 0 0 var(--ok)}
  .xgrid.xg-total tbody tr.warn{box-shadow:inset 4px 0 0 var(--warn)}
  .xgrid.xg-total tbody tr.bad{box-shadow:inset 4px 0 0 var(--bad)}
  .xgrid.xg-total td{display:block;border:0;padding:0;white-space:normal;background:transparent}
  .xgrid.xg-total tbody tr:hover td{background:transparent}
  .xgrid.xg-total td.xg-lead{position:static;grid-column:1/-1;width:auto;max-width:none;box-shadow:none;padding:0}
  .xgrid.xg-total td[data-label]:not(.xg-lead)::before{content:attr(data-label);display:block;font-size:var(--fs-1);color:var(--ink-3);margin-bottom:4px}
  .xgrid.xg-total td:nth-child(2),.xgrid.xg-total td.xg-who{grid-column:1/-1}
  .xgrid.xg-total .xg-in{height:44px;border:1px solid var(--line);border-radius:var(--r1, 6px)}
  .xgrid.xg-total .xg-in.txt{min-width:0}
  .xgrid.xg-total td.xg-act .btn{min-height:44px}
  .xgrid.xg-total td.xg-who{padding:0;min-width:0}
}

/* หน้ารายงานรวม (21 ต.ค. 69) — ตัวเลือกคอลัมน์แบ่งหมวด ไหลตามจอ */
.rb-cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px}
.rb-cat{border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin:0}
.rb-cat legend{font-weight:700;padding:0 6px}
.rb-col{display:flex;gap:8px;align-items:flex-start;padding:4px 0;font-size:var(--fs-2)}
.rb-col input{margin-top:3px}

/* ═══ ช่องกรอกกว้างตามข้อมูล (เจ้าของงานสั่ง 25 ต.ค. 69 · ทั้งระบบ) ═══════════════════
   เดิมทุกช่องกว้างเต็มคอลัมน์ (.inp{width:100%}) ⇒ คำนำหน้า วันที่ ตัวเลือกสั้น ๆ กินทั้งแถว
   และวางซ้อนกันทีละแถว หน้ายาวเกินจำเป็น
   · "ช่องสั้น" = .field ที่มีดรอปดาวน์ที่มองเห็น · วันที่/เวลา · ตัวเลข · เบอร์โทร · หรือติด .f-sm/.f-md เอง
     ⇒ กว้างตามข้อมูล และ **ช่องสั้นที่อยู่ติดกันเรียงต่อกันในแถวเดียวเอง** (inline-flex)
     ช่องยาว (ข้อความ · textarea) ยังเป็นบล็อกเต็มแถว จึงขึ้นบรรทัดใหม่ให้เอง
   · .frow = แถวที่จัดเอง ช่องสั้นกว้างตามข้อมูล ช่องข้อความยืดเต็มที่เหลือ
   · .inline2/.inline3 (กริดเดิม) คงเต็มช่องกริดเหมือนเดิม
   🔴 เงื่อนไข "ช่องสั้น" อยู่ใน :where() ⇒ ความเจาะจงเท่ากฎพื้นฐาน (.field / .field > .inp) แล้วชนะเพราะมาทีหลัง
      กฎเฉพาะของหน้า/ส่วนอื่น (เจาะจงกว่า) ยังชนะเสมอ
   🔴 ดรอปดาวน์ที่ซ่อน (.d-none) ไม่นับ — ช่องอำเภอมีดรอปดาวน์สำรองซ่อนอยู่ เคยถูกนับเป็นช่องสั้นแล้วช่องพิมพ์หดเหลือนิดเดียว
   🔴 ข้อความใต้ช่อง (hint · error) ห้ามดันความกว้าง ⇒ width:0 + min-width:100% (ตัดบรรทัดตามช่องแทน) */
.field:where(:has(> select.inp:not(.d-none):not([multiple])),
  :has(> input.inp:is([type=date],[type=time],[type=datetime-local],[type=month],[type=number],[type=tel]):not(.d-none)),
  .f-sm, .f-md){
  display:inline-flex;vertical-align:top;width:fit-content;max-width:100%;margin-right:14px}
.field:where(:has(> select.inp:not(.d-none):not([multiple])),
  :has(> input.inp:is([type=date],[type=time],[type=datetime-local],[type=month],[type=number],[type=tel]):not(.d-none)),
  .f-sm, .f-md) > .inp{width:auto;max-width:100%}
.field:where(:has(> select.inp:not(.d-none):not([multiple]))) > select.inp{min-width:11rem}
.field:where(:has(> input.inp:is([type=date],[type=datetime-local],[type=month]))) > .inp{min-width:11.5rem}
.field:where(:has(> input.inp:is([type=time],[type=number]))) > .inp{width:9rem}
.field:where(:has(> input.inp[type=tel])) > .inp{width:12rem}
.field:where(.f-sm) > .inp{width:10rem}
.field:where(.f-md) > .inp{width:16rem}
.field:where(:has(> select.inp:not(.d-none):not([multiple])),
  :has(> input.inp:is([type=date],[type=time],[type=datetime-local],[type=month],[type=number],[type=tel]):not(.d-none)),
  .f-sm, .f-md) > :not(label):not(.inp),
.field:where(:has(> select.inp:not(.d-none):not([multiple]))) > input.inp{width:0;min-width:100%}

/* แถวที่จัดเอง (.frow) — ช่องสั้นกว้างตามข้อมูล · ช่องข้อความยืดเต็มที่เหลือ · ตัดบรรทัดเองเมื่อที่ไม่พอ */
.frow{display:flex;flex-wrap:wrap;gap:0 14px;align-items:flex-start}
.frow > .field{flex:1 1 13rem;min-width:0;margin-right:0}
.frow > .field:where(:has(> select.inp:not(.d-none):not([multiple])),
  :has(> input.inp:is([type=date],[type=time],[type=datetime-local],[type=month],[type=number],[type=tel]):not(.d-none)),
  .f-sm, .f-md){flex:0 1 auto}

/* กริดเดิม 2–3 คอลัมน์: ช่องเต็มช่องกริดเหมือนเดิม (กริดคุมความกว้างอยู่แล้ว) */
.inline2 > .field, .inline3 > .field{width:auto;margin-right:0}
.inline2 > .field > .inp, .inline3 > .field > .inp{width:100%;min-width:0}

@media(max-width:700px){
  .field:where(.f-md){width:100%}
  .field:where(.f-md) > .inp{width:100%}
}

/* ═══ ลิ้นชักเมนูบนจอแคบ: ปุ่มปิด + ฉากมืด (เจ้าของงานทัก 26 ต.ค. 69 · เดิมเปิดแล้วไม่มีทางปิด) ═══ */
.rail-close, .rail-scrim{display:none}
@media(max-width:700px){
  .rail-close{display:inline-flex;align-items:center;justify-content:center;margin-left:auto;flex-shrink:0;
    width:40px;height:40px;border:1px solid var(--line);border-radius:var(--r2);background:var(--surface);color:var(--ink-2);cursor:pointer}
  .rail-close svg{width:18px;height:18px}
  .rail-scrim{display:block;position:fixed;inset:0;z-index:1150;background:rgba(31,36,51,.45);
    opacity:0;pointer-events:none;transition:opacity .2s}
  .shell.nav-open .rail-scrim{opacity:1;pointer-events:auto}
}

/* ปุ่มเปิดประกาศของหน่วยงาน (หน้ากรอก CEFR/ดิจิทัล · 26 ต.ค. 69) — ข้อความกับปุ่มแถวเดียว ตัดบรรทัดเองบนจอแคบ */
.src-link{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px}
.src-link .soc-ic{width:16px;height:16px}

/* ═══ หน้าเข้าสู่ระบบแบบกระชับ (เจ้าของงาน 26 ต.ค. 69 · "มันยาวมาก" · แบบ ก) ═══ */
[data-first-hint].first-line{margin-top:14px}
.first-line > span small{font-size:var(--fs-1);margin-top:2px}
.login-fold{text-align:left}
/* summary เป็นบรรทัดข้อความธรรมดา (ไม่ใช่ flex) — flex แยก <b> ออกเป็นคอลัมน์บนจอแคบ */
.login-fold > summary{cursor:pointer;font-size:var(--fs-2);color:var(--ink-2);list-style:none;display:block;text-align:left}
.login-fold > summary::-webkit-details-marker{display:none}
.login-fold > summary::before{content:'';display:inline-block;vertical-align:middle;margin:-2px 9px 0 2px;width:7px;height:7px;
  border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(-45deg);transition:transform .15s}
.login-fold[open] > summary::before{transform:rotate(45deg)}
.login-fold > p{font-size:var(--fs-2);color:var(--ink-3);line-height:1.6}
.login-help{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:10px 14px;
  margin-top:16px;padding-top:14px;border-top:1px dashed var(--line)}
.login-help > .login-fold{flex:1 1 200px;min-width:0}
.login-help .soc-ic{width:16px;height:16px}

/* ── สถิติการใช้งาน (v1.13.0) ── */
/* ── ชุดสีกราฟกลาง (v1.14.0 ห่อ M4 · เจ้าของงานสั่ง 30 ต.ค. 69 "สีมันใกล้เคียงกันเกินไป") — ที่เดียวของทั้งระบบ ──
   app.js palette() อ่านผ่าน getComputedStyle แล้ววาดทั้งกราฟบนจอและภาพที่กด "บันทึกภาพ" (ห้ามฝังสีของแท่ง/เส้นไว้ที่อื่น)
   กราฟสถานะ (เรียงจากแย่ไปดี): แดง = ไม่ดี · เหลืองทอง = กลาง · เขียว = ดี · เทา = ยังไม่มีผล (เช่น ยังไม่ส่ง)
   กราฟที่ไม่ใช่สถานะ (เช่น แยกตามบทบาท): --chart-1…5 แยกสีชัด สลับความสว่าง — ห้ามสองสีเฉดเดียวกันอยู่ติดกัน
   ตัวอักษรบนแท่ง: พื้นอ่อน (เหลืองทอง เทาอ่อน ส้ม) ใช้ --chart-ink · พื้นเข้ม ใช้ขาว (app.js textOn คิดจากความสว่างจริงของสี) */
:root{--chart-bad:#D64545;--chart-mid:#F2C14E;--chart-good:#3B9B5C;--chart-none:#B4B2A9;
  --chart-1:#2F6FB5;--chart-2:#E8833A;--chart-3:#1D9E75;--chart-4:#7F77DD;--chart-5:#888780;--chart-ink:#412402;}
.stat-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:12px;}
.stat-card{display:flex;flex-direction:column;gap:2px;padding:14px 16px;border:1px solid var(--line);border-radius:var(--r-md,12px);background:var(--card,#fff);color:var(--ink);text-decoration:none;}
/* UX1 · U1: การ์ดที่กดได้ต้องดูออกตั้งแต่ยังไม่ชี้เมาส์ — ขอบสีเน้น + บรรทัดล่าง "ดู… →" บอกปลายทางตรง ๆ (ใช้สีลิงก์ของระบบ) · การ์ดที่ไม่ใช่ <a> ไม่มีทั้งสองอย่าง */
a.stat-card{border-color:color-mix(in srgb,var(--accent) 38%,var(--line));}
a.stat-card:hover{border-color:var(--accent);box-shadow:var(--sh-2);}
.stat-go{margin-top:auto;padding-top:8px;font-style:normal;font-size:var(--fs-1);font-weight:600;color:var(--accent-dark);}
a.stat-card:hover .stat-go{text-decoration:underline;}
.stat-card b{font-size:28px;line-height:1.1;font-weight:700;}
.stat-card span{font-weight:600;}
.stat-card small{color:var(--ink-3);}
.stat-bad b{color:var(--bad);}
.stat-canvas{display:block;width:100%;height:auto;}
.stat-canvas:not([data-drawn]){display:none;}
/* UX1 · S1: หัวการ์ดกราฟ = ชื่อ (ซ้าย) · รูปแบบ + บันทึกภาพ (ขวา) · ปุ่มไม่ลอยทับตาราง (อยู่บนหัว ไม่ใช่ใต้กราฟ) */
.stat-head{display:flex;justify-content:space-between;align-items:flex-start;gap:10px 20px;flex-wrap:wrap;padding-bottom:12px;margin-bottom:14px;border-bottom:1.5px solid var(--line-2);}
.stat-head-tx{flex:1 1 280px;min-width:0;}
.stat-head-tx .sec-h{padding-bottom:0;margin-bottom:4px;border-bottom:0;}
.stat-tools{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
.stat-type{display:flex;align-items:center;gap:8px;font-size:var(--fs-2);font-weight:600;color:var(--ink-2);}
.stat-type .inp{width:auto;min-width:0;}
.stat-plot{margin-bottom:8px;}
/* UX1 · S5: ตารางตัวเลขใต้กราฟพับไว้ (เฉพาะเมื่อ JS วาดกราฟได้ — ไม่มี JS = ตารางเปิดอยู่เสมอ) */
.stat-folded{display:none;}
.stat-fold-row{margin-bottom:8px;}

/* UX1 · U10: การ์ดสองใบเคียงกันที่ตารางข้างในแคบ (ผลตรวจครูพี่เลี้ยง) ⇒ เรียงเป็นคอลัมน์เดียวตั้งแต่จอ ≤ 1100px แทน 900px ของ .g-2 ปกติ */
@media(max-width:1100px){.g-2.g-2-1100{grid-template-columns:1fr;}}

/* UX1 · U9: คอลัมน์ท้ายของตาราง (ปุ่มของแถว) ติดขอบขวาตอนเลื่อนแนวนอน — แบบเดียวกับ .xgrid .xg-lead ที่ติดขอบซ้าย (ปุ่มต้องเห็นโดยไม่ต้องเลื่อนตาราง)
   🔴 หัวตารางปักบน (sticky top) อยู่แล้ว ⇒ ช่องหัวของคอลัมน์ท้ายปักทั้งบนและขวา และอยู่เหนือช่องในแถว */
.tbl-stick th:last-child,.tbl-stick td:last-child{position:sticky;right:0;background:var(--surface);box-shadow:-8px 0 8px -8px rgba(31,36,51,.22);}
.tbl-stick th:last-child{z-index:3;background:var(--surface-2);}
.tbl-stick td:last-child{z-index:1;}
.tbl-stick tbody tr:hover td:last-child{background:var(--surface-2);}

/* ── ทะเบียนครูพี่เลี้ยง (v1.13.0 ระลอก C) ── */
.mq-list{margin:4px 0 0;padding-left:18px;font-size:var(--fs-2);color:var(--ink-2)}
.mq-warn{margin-top:4px;font-size:var(--fs-2);color:var(--warn-ink,var(--ink-3))}
.mq-hint{margin-top:4px;font-size:var(--fs-2);color:var(--ink-2)}   /* UX1 · U6: วิธีแก้ใต้ป้าย "ขาด" */
.mq-kw{display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:8px 14px}
.mq-act{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px 12px;padding:10px 0;border-top:1px solid var(--line)}
.mq-act:first-child{border-top:0}
.mq-act form{display:inline}
.mt-off{opacity:.62}   /* v1.14.0 ห่อ L1: แถวครูที่ "ไม่ได้ทำหน้าที่ปีนี้" จางลง (ยังอ่านออกและกดได้) */

/* ── ครูพี่เลี้ยงในฟอร์มรับสมัคร + ลิงก์ติดตาม (v1.13.0 ระลอก C ห่อ C2) ── */
.ml-group{display:flex;flex-direction:column;gap:12px;min-width:0}
.ml-top{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px 12px}
.ml-prev,.ml-item{display:flex;flex-direction:column;gap:12px;min-width:0;padding:12px 14px;border:1px solid var(--line);border-radius:var(--r2);background:var(--surface)}
.ml-head{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:8px 16px}
.ml-who{min-width:0;flex:1 1 16rem}
.ml-act{display:flex;flex-wrap:wrap;gap:8px}
.ml-list{display:flex;flex-direction:column;gap:12px;min-width:0}
.ml-q{flex:1 1 auto;min-width:0}
.ml-fields[hidden]{display:none}
.ml-rev td{vertical-align:top}
.ml-chg{display:block;color:var(--ink-3);font-size:var(--fs-2)}
.ml-acts{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.ml-acts form{display:inline}
.ml-warnline{color:var(--warn-ink);font-weight:600}
/* นำเข้าครูพี่เลี้ยงจาก Excel (v1.14.0 ห่อ M2) — กล่องในขั้นครูพี่เลี้ยงของฟอร์มรับสมัคร */
.ml-import{display:flex;flex-direction:column;gap:8px;min-width:0;padding:12px 14px;border:1px dashed var(--line);border-radius:var(--r2);background:var(--surface-2)}
.ml-import-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;min-width:0}
.ml-import-row .inp{flex:1 1 12rem;min-width:0;width:auto}
.ml-import-pick{flex:1 1 18rem;flex-wrap:wrap;align-items:center;gap:8px 12px;min-width:0}
.ml-import-notes{display:flex;flex-direction:column;gap:8px;min-width:0}
.ml-import-notes:empty{display:none}
/* ย่อ/ขยายช่องกรอกครูพี่เลี้ยงรายคน + ฉบับร่างของฟอร์มสาธารณะ (v1.14.0 ห่อ M3)
   <details> ทำงานได้แม้ไม่มี JS · บรรทัดย่อ (ชื่อ ตำแหน่ง สาขา) + ป้ายความครบ ตัดคำได้ ไม่ล้นแนวนอนที่จอ 360 px
   🔴 summary ห่อบรรทัดได้ (flex-wrap) — จอแคบ: บรรทัดชื่ออยู่แถวบน ป้ายกับลูกศรอยู่แถวล่าง */
.ml-bar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px 12px;min-width:0}
.ml-bar[hidden]{display:none}
.ml-sumtext{color:var(--ink-2);font-size:var(--fs-2);min-width:0;overflow-wrap:anywhere}
.ml-bar-btns{display:flex;flex-wrap:wrap;gap:8px}
.ml-fold{min-width:0}
.ml-sum{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;min-height:44px;min-width:0;cursor:pointer;list-style:none;border-radius:var(--r1)}
.ml-sum::-webkit-details-marker{display:none}
.ml-sum:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.ml-line{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 10px;flex:1 1 14rem;min-width:0}
.ml-name{min-width:0;overflow-wrap:anywhere}
.ml-meta{color:var(--ink-3);font-size:var(--fs-2);min-width:0;overflow-wrap:anywhere}
.ml-chev{display:inline-flex;margin-left:auto;transition:transform .2s}
.ml-fold[open]>.ml-sum .ml-chev{transform:rotate(180deg)}
.ml-body{display:flex;flex-direction:column;gap:12px;min-width:0;margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}
.draft-note{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;margin:0;color:var(--ink-2);font-size:var(--fs-2)}
.draft-note[hidden]{display:none}
.draft-note>span{flex:1 1 16rem;min-width:0}
/* จับคู่ครูพี่เลี้ยงรายนักศึกษา (v1.13.0 ระลอก C ห่อ C3) — กล่องเลือก/เพิ่มครูในหน้าตอบรับ · หน้ากระดาษ · หน้านักศึกษา */
.mp{display:flex;flex-direction:column;gap:10px;min-width:0}
.mp-box{padding:10px 12px;border-radius:var(--r2);background:var(--surface-2);min-width:0}
.mp-fields{display:flex;flex-direction:column;gap:12px;min-width:0;padding:12px;border:1px dashed var(--line);border-radius:var(--r2)}
.mp-fields[hidden]{display:none}
.mp-own{align-self:flex-start;max-width:100%}
.mp-card{display:flex;flex-direction:column;gap:6px;padding:12px 14px;border:1px solid var(--line);border-radius:var(--r2);background:var(--surface-2)}
.mp-card .kv-row{display:flex;flex-wrap:wrap;justify-content:space-between;gap:4px 14px}
/* ห่อ UX3 (30 ต.ค. 69) — จอโทรศัพท์: หน้าตอบรับที่ 360 px กล่อง "ครูพี่เลี้ยงไม่อยู่ในรายชื่อ" ซ้อน 5 ชั้น
   (main 16 + card-pad 25 + sr-person 15 + mp-box 12 + mp-fields 13 ≈ 81 px ต่อข้าง) ⇒ ช่องกรอกเหลือ ~198 px พิมพ์ชื่อ-วิชาไม่สะดวก
   ⇒ ชั้นใน 3 ชั้นเหลือรวม 9 px ต่อข้าง (ขอบ+ระยะของ sr-person เท่านั้น) · กล่องเทา/กรอบเส้นประของ 2 ชั้นในถอดออก
   คงเส้นคั่นบนเส้นเดียวต่อชั้นให้รู้ว่าเริ่มส่วนใหม่ ⇒ ช่อง .f-md เต็มความกว้างของการ์ด (≥ 260 px ที่ 360 px) · จอกว้างไม่เปลี่ยน */
@media (max-width:560px){
  .sr-person{padding:12px 8px}
  .mp-box{padding:10px 0 0;border-top:1px solid var(--line-2);border-radius:0;background:none}
  .mp-fields{padding:10px 0 0;border:0;border-top:1px dashed var(--line);border-radius:0}
}
/* แบบตอบกลับ A4: ตารางกรอกมือครูพี่เลี้ยง — 15 คอลัมน์แคบ ตัวอักษรเล็ก ช่องกรอกสูงพอเขียนมือ */
.mentor-tbl{table-layout:fixed;font-size:9.5px}
.mentor-tbl th,.mentor-tbl td{padding:3px 2px;line-height:1.25;overflow-wrap:anywhere}
.mentor-tbl th{text-align:center;vertical-align:middle;font-size:9px}
.mentor-tbl tr.mt-row td{height:11mm}
.mentor-tbl th:nth-child(1){width:4%}
.mentor-tbl th:nth-child(2){width:7%}
.mentor-tbl th:nth-child(3){width:5%}
.mentor-tbl th:nth-child(4){width:11%}
.mentor-tbl th:nth-child(5){width:7%}
.mentor-tbl th:nth-child(6){width:8%}
.mentor-tbl th:nth-child(7){width:7%}
.mentor-tbl th:nth-child(8){width:6%}
.mentor-tbl th:nth-child(9){width:6%}
.mentor-tbl th:nth-child(10){width:6%}
.mentor-tbl th:nth-child(11){width:8%}
.mentor-tbl th:nth-child(12){width:5%}
.mentor-tbl th:nth-child(13){width:7%}
.mentor-tbl th:nth-child(14){width:8%}
.mentor-tbl th:nth-child(15){width:5%}


/* ═══════ v1.14.0 ห่อ L4 · ปุ่มระนาบเดียวกัน/ไม่ลอย/ลูกกริดไม่ดันกรอบ + ทำทีละหลายคน ═══════
   ที่มา: เจ้าของงานสั่ง 31 ต.ค. 69 "ปุ่มลอย ปุ่มไม่ได้ระดับ ระนาบ หรือตำแหน่งที่ถูกต้อง เช็คด้วย" · ตัวเฝ้า = tools/check_widths.cjs (ปุ่ม 3 ข้อ + ลูกกริดล้นกรอบ) */

/* ลูกของกริดสองคอลัมน์ต้องหดได้ — เดิมไม่ตั้ง min-width:0 ⇒ ลูกที่มีเนื้อหากว้าง (ดรอปดาวน์ตัวเลือกยาว · ตาราง · การ์ดซ้อน) ดันคอลัมน์กว้างเกินกรอบการ์ด
   เชลล์แอปซ่อน overflow แนวนอน หน้าจึงไม่ฟ้องว่าล้น แต่การ์ดถูกตัดขอบขวาบนมือถือ (เจอจริง: หน้ารายคนครู · ฟอร์มจับคู่นักศึกษา ~900px) — .grid2/.g-83/.g-3 มีกฎนี้แล้ว (tims-ui.css) */
.g-2 > *, .set-2col-cls > *, .calwrap > *, .review-grid > *{min-width:0}
@media (max-width:1100px){.g-2.g-2-1100{grid-template-columns:minmax(0,1fr)}.review-grid{grid-template-columns:minmax(0,1fr)}}
@media (max-width:900px){.set-2col-cls{grid-template-columns:minmax(0,1fr)}.calwrap{grid-template-columns:minmax(0,1fr)}}

/* ช่องติ๊ก/ตัวเลือกในปุ่ม (label.btn) ไม่มีขอบนอกของตัวเอง — ขอบนอกปริยายของเบราว์เซอร์ (3px) ทำให้ปุ่มสูงกว่าปุ่มข้างกัน ~3px ("เลือกทั้งหมด" ↔ "เผยแพร่ประกาศ" ไม่ระนาบ) */
.btn > input[type=checkbox], .btn > input[type=radio]{margin:0;flex:none}

/* ปุ่มข้อความยาวตัดบรรทัดในกรอบแทนการล้นพ้นการ์ด (.btn ปกติ nowrap) — ใส่ .btn-wrap เฉพาะปุ่มที่ข้อความยาวกว่าจอมือถือ (หน้ารายงาน: รายงานคุณสมบัติ · …)
   🔴 ไม่ตั้งเป็นกฎกลางของ .btn: ปุ่มที่อยู่ในฟอร์ม/เซลล์แคบจะหดแล้วตกบรรทัด สูงไม่เท่าปุ่มข้างกัน (เจอจริงตอนลองกฎกลาง · ด่านปุ่มระนาบเดียวกัน) */
.btn.btn-wrap{max-width:100%;white-space:normal;text-align:left}

/* แถวรายการ (.lg · งานรอตรวจ ฯลฯ): ที่ไม่พอวางแถวเดียวให้ปุ่ม/เนื้อหาตกบรรทัดเอง — เดิม .body ที่ flex:1 มี min-width ตามข้อความ ดันปุ่มชุด "ผ่าน/ตีกลับ" ล้นการ์ดที่จอ 1024–1280
   (ตั้ง min-width:0 อย่างเดียวบีบเนื้อหาเหลือกว้าง 0 ตัวอักษรหักทีละตัว — เจอจริงตอนลอง) · เนื้อหากว้างขั้นต่ำ 200px แล้วตกบรรทัด */
.lg{flex-wrap:wrap}
.lg .body{flex:1 1 200px;min-width:0}

/* ทำทีละหลายคน (ห่อ L4): ช่องติ๊กหน้าชื่อในการ์ดรายคน (หน้าตอบรับของโรงเรียน · การ์ดคำแจ้งของฝ่ายฝึก) — ติ๊กได้ทั้งแถบ ไม่ต้องเล็งช่องเล็ก */
.sr-pick{display:inline-flex;align-items:center;gap:10px;cursor:pointer;min-height:32px}
.sr-pick input{width:20px;height:20px;margin:0;accent-color:var(--accent);flex:none}
