:where(:root) { --brand: #1591DC; --brand-dark: #2C5EAD; --btn: #2C5EAD; --btn-dark: #1F4585; --accent: #4BB8FA; }
:root { --sk: #1591DC; --sk-dk: #2C5EAD; --sk-deep: #1F4585; --sk-lt: #4BB8FA; --sk-wash: #e3f0fa; --sk-school: #8cc0eb; --gold: #FFA95A; --gold-dk: #b87708; --green: #16a34a; --green-dk: #12803b; --warm-red: #FF5A5A; --warm-orange: #FF8B5A; --warm-amber: #FFA95A; --warm-yellow: #FFD45A; --sk-ink: #1A3C73; --red-ink: #A63A3A; --green-ink: #0F6B33; --gold-ink: #8A5A05; --ink-on-gold: #3a2900; --r-xs: 6px; --r-sm: 10px; --r-md: 12px; --r-lg: 14px; --r-xl: 20px; --r-pill: 50px; --r-jumbo: 32px; --r-full: 9999px; --sp-xxs: 4px; --sp-xs: 8px; --sp-sm: 12px; --sp-md: 16px; --sp-lg: 20px; --sp-xl: 24px; --sp-xxl: 32px; --sp-section: 40px; --font-display: 'Inter', 'Noto Sans Thai', system-ui, -apple-system, 'Segoe UI', sans-serif; --font-body: 'Inter', 'Noto Sans Thai', system-ui, -apple-system, 'Segoe UI', sans-serif; --brand-light: #4BB8FA; --bg-base: #eef6fc; --bg-soft: #e3f0fa; --bg-card: #ffffff; --bg-input: #ffffff; --border-color: #cfe4f3; --text-main: #1f3a52; --text-muted: #4e6a86; --shadow: 0 1px 2px rgba(3,105,161,.05), 0 6px 20px rgba(3,105,161,.06); --shadow-lg: 0 10px 40px rgba(3,105,161,.16); --red: var(--warm-red); --orange: var(--warm-orange); --amber: var(--warm-amber); --yellow: var(--warm-yellow); --na-royal: var(--sk); --na-royal-dk: var(--sk-dk); --na-royal-lt: var(--sk-lt); --na-navy: #0c4a6e; --na-navy-2: #0e5a86; --na-navy-3: #146a99; --na-sky: var(--sk); --na-sky-dk: var(--sk-dk); --na-gold: var(--gold); --na-gold-dk: var(--gold-dk); --na-green: var(--green); --na-green-dk: var(--green-dk); --dc-blurple: var(--sk); --dc-blurple-dk: var(--sk-dk); --dc-blurple-lt: var(--sk-lt); --dc-green: var(--green); --dc-green-dk: var(--green-dk); --dc-magenta: var(--gold); --dc-violet: #146a99; --dc-link: var(--sk); --dc-canvas: #0c4a6e; --dc-indigo: #0e5a86; --dc-indigo-2: #146a99; --dc-onyx: #1c2430; --dc-black: #082f45; }
html.dark { --brand-light: #7dd3fc; --bg-base: #0b1526; --bg-soft: #12233c; --bg-card: #132540; --bg-input: #0f1e35; --border-color: #24405f; --text-main: #eaf4fc; --text-muted: #9dbbd4; --sk-ink: #4BB8FA; --red-ink: #FF8888; --green-ink: #5fd48b; --gold-ink: #f0c454; --shadow: 0 2px 10px rgba(0,0,0,.32); --shadow-lg: 0 10px 44px rgba(2,10,22,.55); }
html, body { background-color: var(--bg-base); }
body { font-feature-settings: "cv05", "ss01"; -webkit-font-smoothing: antialiased; letter-spacing: -0.006em; font-family: var(--font-body) !important; color: var(--text-main) !important; }
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { letter-spacing: -0.018em; text-wrap: balance; }
.stat-val, .rd-num, .table, .badge, .score-input, .score-cell, .att-stat b, .fm-stat b, .hm-stats b { font-variant-numeric: tabular-nums; }
body::before { content: ""; position: fixed; inset: 0px; z-index: -1; pointer-events: none; opacity: 0.6; background: radial-gradient(50vw 32vw at 100% 0%, color-mix(in srgb, var(--sk-lt) 26%, transparent), transparent 66%),
    radial-gradient(44vw 30vw at 0% 0%,   color-mix(in srgb, var(--sk-school) 22%, transparent), transparent 66%); }
html.dark body::before { opacity: 1; background: radial-gradient(52vw 36vw at 100% 0%, color-mix(in srgb, var(--sk) 12%, transparent), transparent 66%),
    radial-gradient(48vw 34vw at 0% 0%,   color-mix(in srgb, var(--sk-deep) 16%, transparent), transparent 66%); }
.app-shell { background: transparent !important; }
.page-main { background: transparent; }
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6, .dc-display, .stat-card .stat-val, .cw-stat .num, .brand-name { font-family: var(--font-display); font-weight: 700; letter-spacing: -0.014em; line-height: 1.18; }
h1, .h1 { font-size: clamp(1.8rem, 3.6vw, 2.6rem); }
h2, .h2 { font-size: clamp(1.5rem, 2.8vw, 2rem); }
h3, .h3 { font-size: clamp(1.25rem, 2.2vw, 1.6rem); }
.fw-bold, .fw-semibold, b, strong { letter-spacing: -0.008em; }
.small, small { letter-spacing: 0px; }
a { color: var(--sk-dk); }
a:hover { color: var(--sk-deep); }
html.dark a:not(.btn) { color: var(--sk-lt); }
html.dark a:not(.btn):hover { color: rgb(186, 230, 253); }
.dc-eyebrow { font-family: var(--font-display); font-weight: 700; font-size: 0.74rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--sk-dk); }
.btn { border-radius: var(--r-sm); font-family: var(--font-body); font-weight: 600; letter-spacing: -0.003em; border-width: 1px; padding: 0.55rem 1.2rem; transition: background 0.18s, color 0.18s, transform 0.15s, box-shadow 0.2s; }
.btn-sm { border-radius: var(--r-xs); font-size: 0.82rem; padding: 0.3rem 0.8rem; }
.btn-lg { border-radius: var(--r-md); font-size: 1.05rem; padding: 0.8rem 1.9rem; }
.btn:active { transform: translateY(1px); }
.btn-brand, .btn-primary { background: var(--sk-dk); border-color: var(--sk-dk); color: rgb(255, 255, 255); }
.btn-brand:hover, .btn-primary:hover, .btn-brand:focus, .btn-primary:focus { background: var(--sk-deep); border-color: var(--sk-deep); color: rgb(255, 255, 255); }
.btn-outline-brand { background: transparent; border-color: var(--sk); color: var(--sk-dk); }
.btn-outline-brand:hover { background: var(--sk-dk); border-color: var(--sk-dk); color: rgb(255, 255, 255); }
html.dark .btn-outline-brand { color: var(--sk-lt); border-color: var(--sk-lt); }
.btn-success { background: var(--green-dk); border-color: var(--green-dk); color: rgb(255, 255, 255); font-weight: 700; }
.btn-success:hover, .btn-success:focus { background: var(--green-dk); border-color: var(--green-dk); color: rgb(255, 255, 255); }
.btn-outline-success { color: var(--green-dk); border-color: var(--green); }
.btn-outline-success:hover { background: var(--green); color: rgb(255, 255, 255); }
html.dark .btn-outline-success { color: rgb(74, 222, 128); }
.btn-warning { background: var(--gold); border-color: var(--gold); color: rgb(63, 45, 0); font-weight: 700; }
.btn-warning:hover { background: var(--orange); border-color: var(--orange); color: rgb(63, 45, 0); }
.btn-outline-warning { color: var(--gold-dk); border-color: var(--gold); }
.btn-outline-warning:hover { background: var(--gold); border-color: var(--gold); color: rgb(63, 45, 0); }
html.dark .btn-outline-warning { color: var(--gold); }
.btn-info, .btn-outline-info:hover { background: var(--sk); border-color: var(--sk); color: rgb(255, 255, 255); }
.btn-outline-info { color: var(--sk-dk); border-color: var(--sk); }
html.dark .btn-outline-info { color: var(--sk-lt); border-color: var(--sk-lt); }
.btn-danger { background: var(--red); border-color: var(--red); color: rgb(255, 255, 255); }
.btn-danger:hover { background: rgb(201, 51, 56); border-color: rgb(201, 51, 56); }
.btn-outline-danger { color: var(--red); border-color: var(--red); }
.btn-outline-danger:hover { background: var(--red); border-color: var(--red); color: rgb(255, 255, 255); }
.btn-outline-primary { color: var(--sk-dk); border-color: var(--sk); }
.btn-outline-primary:hover { background: var(--sk-dk); border-color: var(--sk-dk); color: rgb(255, 255, 255); }
/* ปุ่มรอง = พื้นขาว ขอบน้ำเงิน หมึกน้ำเงิน (ชุดเดียวกับ .btn-outline-brand)
   ของเดิมพื้นเป็นฟ้าอมน้ำเงินจาง (--bg-soft) ซึ่งบนการ์ดขาวอ่านเป็นแผ่นขุ่น ๆ
   ไม่ใช่ปุ่ม · ปุ่มที่อยู่บนฮีโร่สีน้ำเงินเป็นข้อยกเว้น (กฎถัดไป) */
.btn-light, .btn-secondary, .btn-outline-secondary { background: var(--bg-card); border: 2px solid var(--sk); border-radius: var(--r-sm); color: var(--sk-dk) !important; }
.btn-light:hover, .btn-secondary:hover, .btn-outline-secondary:hover { background: var(--sk-dk); border-color: var(--sk-dk); color: rgb(255, 255, 255) !important; }
html.dark .btn-light, html.dark .btn-secondary, html.dark .btn-outline-secondary { background: transparent; border-color: var(--sk-lt); color: var(--sk-lt) !important; }
html.dark .btn-light:hover, html.dark .btn-secondary:hover, html.dark .btn-outline-secondary:hover { background: var(--sk-dk); border-color: var(--sk-dk); color: rgb(255, 255, 255) !important; }
/* ต้องมี html.dark ด้วย เพราะ `html.dark .btn-light` (0,2,1) ชนะ `.dash-hero .btn-light` (0,2,0) */
.dash-hero .btn-light, .fm-hero .btn-light, html.dark .dash-hero .btn-light, html.dark .fm-hero .btn-light { background: rgb(255, 255, 255); border-color: rgb(255, 255, 255); color: var(--sk-deep) !important; }
.dash-hero .btn-light:hover, .fm-hero .btn-light:hover, html.dark .dash-hero .btn-light:hover, html.dark .fm-hero .btn-light:hover { background: rgb(255, 255, 255); border-color: rgb(255, 255, 255); color: var(--sk-deep) !important; }
.btn-check:checked + .btn { background: var(--sk-dk); border-color: var(--sk-dk); color: rgb(255, 255, 255) !important; }
.sg-del { width: 34px; height: 34px; flex: 0 0 auto; position: relative; display: inline-flex; align-items: center; justify-content: center; overflow: hidden; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; border-radius: var(--r-full); background: var(--bg-soft); color: var(--red-ink); cursor: pointer; vertical-align: middle; text-decoration: none; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 6px; transition: width 0.3s, border-radius 0.3s, background 0.3s, box-shadow 0.25s; }
.sg-del-ic { width: 13px; height: 13px; transition: width 0.3s, height 0.3s, transform 0.3s; }
.sg-del-ic path { fill: var(--red-ink); transition: fill 0.3s; }
.sg-del-tx { position: absolute; top: -14px; left: 0px; right: 0px; color: rgb(255, 255, 255); font-family: var(--font-body); font-weight: 700; font-size: 2px; opacity: 0; text-align: center; white-space: nowrap; transition: font-size 0.3s, opacity 0.3s, transform 0.3s; }
.sg-del:hover, .sg-del:focus-visible { width: 72px; border-radius: var(--r-pill); background: var(--red); box-shadow: color-mix(in srgb, var(--red) 42%, transparent) 0px 6px 16px; }
.sg-del:hover .sg-del-ic, .sg-del:focus-visible .sg-del-ic { width: 22px; height: 22px; transform: translateY(62%); }
.sg-del:hover .sg-del-ic path, .sg-del:focus-visible .sg-del-ic path { fill: rgb(255, 255, 255); }
.sg-del:hover .sg-del-tx, .sg-del:focus-visible .sg-del-tx { font-size: 0.78rem; opacity: 1; transform: translateY(16px); }
.sg-del:active { transform: scale(0.94); }
html.dark .sg-del { background: rgb(27, 53, 82); }
html.dark a.sg-del, html.dark a.sg-del:hover { color: rgb(255, 255, 255); }
@media (prefers-reduced-motion: reduce) {
  .sg-del, .sg-del * { transition: none; }
}
@media print {
  .sg-del { display: none !important; }
}
.sg-back { display: inline-flex; align-items: center; justify-content: flex-start; width: 42px; height: 42px; flex: 0 0 auto; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; border-radius: var(--r-full); background: var(--sk-dk); color: rgb(255, 255, 255); cursor: pointer; position: relative; overflow: hidden; text-decoration: none; vertical-align: middle; box-shadow: 0 2px 10px color-mix(in srgb, var(--sk-deep) 30%, transparent); transition: width 0.3s, border-radius 0.3s, background 0.2s, box-shadow 0.2s; }
.sg-back:hover, .sg-back:focus-visible { width: var(--sg-back-w, 132px); border-radius: var(--r-pill); background: var(--sk-deep); color: rgb(255, 255, 255); box-shadow: 0 6px 18px color-mix(in srgb, var(--sk-deep) 42%, transparent); }
.sg-back:active { transform: translate(1px, 1px); }
.sg-back-sign { width: 100%; display: flex; align-items: center; justify-content: center; transition: width 0.3s, padding 0.3s; }
.sg-back-sign svg { width: 17px; height: 17px; transform: scaleX(-1); display: block; }
.sg-back-sign svg path { fill: rgb(255, 255, 255); }
/* ตัวหนังสือชิดลูกศรมากขึ้น (เจ้าของสั่ง 20 ส.ค. 2569) — ช่องไอคอนตอนกาง
   แคบลง 32→24px และเยื้องซ้าย 14→12px · main.js คำนวณความกว้างรวมจาก
   เลขคู่นี้ด้วย (คอมเมนต์ "46px = ช่องไอคอน") ถ้าแก้ที่นี่ต้องแก้ที่นั่นด้วย */
.sg-back:hover .sg-back-sign, .sg-back:focus-visible .sg-back-sign { width: 24px; padding-left: 12px; justify-content: flex-start; }
.sg-back-text { position: absolute; right: 0px; width: 0px; opacity: 0; color: rgb(255, 255, 255); font-family: var(--font-body); font-size: 0.95rem; font-weight: 600; white-space: nowrap; overflow: hidden; transition: width 0.3s, opacity 0.3s, padding 0.3s; }
.sg-back:hover .sg-back-text, .sg-back:focus-visible .sg-back-text { opacity: 1; width: calc(var(--sg-back-tw, 68%) + 10px); padding-right: 12px; }
html.dark a.sg-back, html.dark a.sg-back:hover { color: rgb(255, 255, 255); }
@media (prefers-reduced-motion: reduce) {
  .sg-back, .sg-back * { transition: none; }
}
@media print {
  .sg-back { display: none !important; }
}
.att-tab.active, .att-tab.active:hover { background: var(--sk-dk) !important; border-color: var(--sk-dk) !important; color: rgb(255, 255, 255) !important; }
.list-group-item.active { background: var(--sk-dk) !important; border-color: var(--sk-dk) !important; color: rgb(255, 255, 255) !important; }
.list-group-item.active .text-muted, .list-group-item.active small { color: rgba(255, 255, 255, 0.92) !important; }
.att-c-total .n { color: rgb(67, 56, 202); }
.att-c-present .n { color: rgb(8, 127, 91); }
.att-c-late .n { color: rgb(154, 103, 0); }
.att-c-absent .n { color: rgb(190, 18, 60); }
.att-c-leave .n { color: var(--sk-ink); }
html.dark .att-c-total .n { color: rgb(165, 180, 252); }
html.dark .att-c-present .n { color: rgb(94, 234, 212); }
html.dark .att-c-late .n { color: var(--gold-ink); }
html.dark .att-c-absent .n { color: rgb(253, 164, 175); }
html.dark .att-c-leave .n { color: var(--sk-ink); }
.dash-hero .badge, .dash-hero .badge.bg-light { background: rgba(0, 0, 0, 0.26) !important; color: rgb(255, 255, 255) !important; }
.text-secondary { color: var(--text-muted) !important; }
.btn.btn-53 { --face: var(--bg-soft); --ink: var(--text-main); --face-dk: color-mix(in srgb, var(--face) 72%, #000); position: relative; display: inline-block; align-self: center; overflow: hidden; isolation: isolate; color: rgb(255, 255, 255); font-weight: 700; line-height: 1.5; white-space: nowrap; transition: box-shadow 0.25s, transform 0.25s; border-width: medium !important; border-style: none !important; border-color: currentcolor !important; border-image: initial !important; border-radius: var(--r-pill) !important; padding: 0.55rem 1.4rem !important; background: var(--face-dk) !important; box-shadow: 0 3px 10px color-mix(in srgb, var(--face-dk) 34%, transparent),
              0 1px 2px rgba(0,0,0,.10) !important; }
.btn-53:hover { transform: translateY(-2px); box-shadow: 0 10px 24px color-mix(in srgb, var(--face-dk) 44%, transparent),
              0 2px 6px rgba(0,0,0,.14) !important; }
.btn-53:active { transform: translateY(0px); }
.btn-53.disabled, .btn-53:disabled { filter: grayscale(0.5) opacity(0.65); pointer-events: none; }
.btn-53 > .original { position: absolute; inset: 0px; z-index: 1; display: flex; align-items: center; justify-content: center; gap: 0.35rem; background: var(--face); color: var(--ink); border-radius: inherit; transition: transform 0.24s cubic-bezier(0.87, 0, 0.13, 1); }
.btn-53 > .original > .bi { margin: 0px !important; }
.btn-53:hover > .original { transform: translateY(100%); }
.btn-53 > .letters { display: inline-flex; align-items: center; gap: 0.35rem; height: 1.5em; line-height: 1; }
/* .letters เป็นตัวกำหนดความกว้างปุ่ม (.original เป็น absolute) จึงต้องเว้นระยะแบบ
   เดียวกับ .original เป๊ะ ๆ — .original ใช้ `gap:.35rem` ระหว่างไอคอนกับข้อความ
   ที่นี่จึงตั้ง gap เท่ากันแล้วหักออกเฉพาะ "ตัวอักษรติดตัวอักษร" ด้วย margin ลบ
   ผลคือ ไอคอน→ตัวอักษร และ ตัวอักษร→badge ได้ .35rem เหมือนกัน แต่ตัวอักษรยังชิดกัน
   ถ้าตั้ง gap:0 แล้วใส่ margin-right ให้ .bi อย่างเดียวแบบเดิม ปุ่มที่มี badge หรือ
   ไอคอนสองตัวจะแคบกว่า .original แล้วข้อความโดน overflow:hidden เฉือนหาย */
.btn-53 > .letters > .g + .g { margin-left: -0.35rem; }
.btn-53 > .letters > .g { opacity: 0; transform: translateY(-14px); transition: transform 0.2s cubic-bezier(0.87, 0, 0.13, 1), opacity 0.2s; white-space: pre; }
.btn-53 > .letters > .g:nth-child(2n) { transform: translateY(14px); }
.btn-53:hover > .letters > .g { opacity: 1; transform: translateY(0px); transition-delay: calc(var(--i, 0) * 45ms); }
@media (prefers-reduced-motion: reduce) {
  .btn-53, .btn-53 > .original, .btn-53 > .letters > .g { transition: none !important; }
  .btn-53:hover { transform: none; }
}
.btn-53.btn-sm { padding: 0.3rem 1rem !important; }
.btn-53.btn-lg { padding: 0.8rem 2rem !important; }
.btn-53.btn-brand, .btn-53.btn-primary, .btn-53.btn-info { --face: var(--sk-dk); --ink: #fff; }
.btn-53.btn-success { --face: var(--green-dk); --ink: #fff; }
.btn-53.btn-danger { --face: var(--red); --ink: #fff; --face-dk: #8C2F2F; }
.btn-53.btn-warning { --face: var(--gold); --ink: var(--ink-on-gold); --face-dk: #7a5605; }
.btn-53.btn-dark { --face: var(--sk-deep); --ink: #fff; }
.btn-53.btn-light, .btn-53.btn-secondary { --face: var(--bg-card); --ink: var(--sk-ink); --face-dk: var(--sk-dk); }
.btn-53.btn-outline-brand, .btn-53.btn-outline-primary, .btn-53.btn-outline-info { --face: var(--bg-card); --ink: var(--sk-ink); --face-dk: var(--sk-dk); }
.btn-53.btn-outline-success { --face: var(--bg-card); --ink: var(--green-ink); --face-dk: var(--green-dk); }
.btn-53.btn-outline-danger { --face: var(--bg-card); --ink: var(--red-ink); --face-dk: #8C2F2F; }
.btn-53.btn-outline-warning { --face: var(--bg-card); --ink: var(--gold-ink); --face-dk: #7a5605; }
.btn-53.btn-outline-secondary, .btn-53.btn-outline-dark { --face: var(--bg-card); --ink: var(--text-main); --face-dk: var(--sk-deep); }
/* วงขอบของปุ่มพื้นขาว — .btn-light/.btn-secondary ใช้พื้นขาวแล้ว จึงต้องมีขอบด้วย
   ไม่งั้นบนการ์ดขาวจะเหลือแค่ตัวหนังสือลอย ๆ ไม่รู้ว่าขอบปุ่มอยู่ตรงไหน */
.btn-53[class*="btn-outline-"] > .original, .btn-53.btn-light > .original, .btn-53.btn-secondary > .original { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--face-dk) 45%, transparent); }
.btn-53.btn-link { --face: transparent; --ink: var(--sk-ink); background: transparent !important; box-shadow: none !important; }
.btn-53.btn-link:hover { transform: none; box-shadow: none !important; }
.btn-53.btn-link > .original { transform: none !important; }
.btn-check:checked + .btn-53 { --face: var(--sk-dk) !important; --ink: #fff !important; }
/* โหมดมืดไม่ต้องมีกฎแยก — --face/--ink อ่านจาก token ที่พลิกเองแล้ว (เหมือน btn-outline-brand)
   ที่ต้องมีคือปุ่มบนฮีโร่สีน้ำเงิน ซึ่งยังต้องเป็นแผ่นขาวทึบทั้งสองโหมด */
.dash-hero .btn-53.btn-light, .fm-hero .btn-53.btn-light, html.dark .dash-hero .btn-53.btn-light, html.dark .fm-hero .btn-53.btn-light { --face: #fff; --ink: var(--sk-deep); --face-dk: var(--sk-deep); }
.dash-hero .btn-53.btn-light > .original, .fm-hero .btn-53.btn-light > .original { box-shadow: none; }
html.dark a.btn-53 > .original { color: var(--ink); }
.btn.btn-53 > .letters, html.dark .btn.btn-53 > .letters { color: rgb(255, 255, 255); }
.btn.btn-53 > .letters > .bi { color: rgb(255, 255, 255); }
.card { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--r-lg); box-shadow: var(--shadow); }
html.dark .card { border-color: rgba(255, 255, 255, 0.07); }
.card-header { background: transparent; border-bottom: 1px solid var(--border-color); font-family: var(--font-display); font-weight: 700; letter-spacing: -0.01em; color: var(--text-main); border-radius: var(--r-lg) var(--r-lg) 0 0 !important; }
html.dark .card-header { color: rgb(255, 255, 255); border-bottom-color: rgba(255, 255, 255, 0.08); }
.card-footer { background: transparent; border-top: 1px solid var(--border-color); border-radius: 0 0 var(--r-lg) var(--r-lg); }
html.dark .card-footer { border-top-color: rgba(255, 255, 255, 0.08); }
.card-feature { border-radius: var(--r-xl); }
.modal-content { border-radius: var(--r-xl); border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; overflow: hidden; background: var(--bg-card); box-shadow: var(--shadow-lg); }
.modal-header { background: transparent; border-bottom: 1px solid var(--border-color); }
.modal-title { font-family: var(--font-display); font-weight: 700; color: var(--text-main); }
html.dark .modal-title { color: rgb(255, 255, 255); }
.modal-footer { border-top: 1px solid var(--border-color); }
.dropdown-menu { border-radius: var(--r-md); border: 1px solid var(--border-color); box-shadow: var(--shadow-lg); padding: 6px; background: var(--bg-card); }
.dropdown-item { border-radius: var(--r-xs); font-weight: 600; color: var(--text-main) !important; }
.dropdown-item:hover, .dropdown-item:focus { background: var(--sk-dk) !important; color: rgb(255, 255, 255) !important; }
.dropdown-header { font-family: var(--font-display); font-weight: 700; color: var(--text-muted); }
.alert { border-radius: var(--r-md); border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; font-weight: 600; padding: var(--sp-md) var(--sp-lg); }
.alert-success { background: color-mix(in srgb, var(--green) 13%, var(--bg-card)); color: rgb(15, 107, 51); }
html.dark .alert-success { background: color-mix(in srgb, var(--green) 16%, #12233c); color: rgb(110, 231, 160); }
.alert-danger { background: color-mix(in srgb, var(--red) 11%, var(--bg-card)); color: rgb(163, 39, 43); }
html.dark .alert-danger { background: color-mix(in srgb, var(--red) 18%, #12233c); color: rgb(255, 154, 160); }
.alert-warning { background: color-mix(in srgb, var(--gold) 15%, var(--bg-card)); color: rgb(138, 90, 0); }
html.dark .alert-warning { background: color-mix(in srgb, var(--gold) 18%, #12233c); color: rgb(255, 214, 113); }
.alert-info, .alert-primary, .alert-secondary { background: color-mix(in srgb, var(--sk) 12%, var(--bg-card)); color: var(--sk-deep); }
html.dark .alert-info, html.dark .alert-primary, html.dark .alert-secondary { background: color-mix(in srgb, var(--sk) 18%, #12233c); color: rgb(186, 230, 253); }
.list-group-item { background: transparent; border-color: var(--border-color); color: var(--text-main); }
.list-group-item:first-child { border-top-left-radius: var(--r-lg); border-top-right-radius: var(--r-lg); }
.list-group-item:last-child { border-bottom-left-radius: var(--r-lg); border-bottom-right-radius: var(--r-lg); }
.nav-tabs { border-bottom: 1px solid var(--border-color); gap: 4px; }
.nav-tabs .nav-link { border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; border-radius: var(--r-sm) var(--r-sm) 0 0; font-weight: 600; color: var(--text-muted); padding: 0.6rem 1.1rem; }
.nav-tabs .nav-link:hover { background: var(--bg-soft); color: var(--text-main); }
.nav-tabs .nav-link.active { background: transparent; color: var(--sk-dk); box-shadow: inset 0 -3px 0 var(--sk); }
html.dark .nav-tabs .nav-link.active { color: var(--sk-lt); box-shadow: inset 0 -3px 0 var(--sk-lt); }
.badge { border-radius: var(--r-xs); font-weight: 600; letter-spacing: 0.01em; padding: 0.38em 0.7em; }
.badge.bg-primary { background: var(--sk-dk) !important; }
.badge.bg-secondary { background: var(--bg-soft) !important; color: var(--text-main) !important; }
html.dark .badge.bg-secondary { background: rgb(27, 53, 82) !important; color: rgb(255, 255, 255) !important; }
.badge.bg-success { background: var(--green) !important; }
.badge.bg-warning { background: var(--gold) !important; color: rgb(63, 45, 0) !important; }
.badge.bg-danger { background: var(--red-ink) !important; }
.badge.bg-info { background: var(--sk) !important; }
.badge.bg-light { background: var(--bg-soft) !important; color: var(--text-main) !important; }
.grade-badge { border-radius: var(--r-pill); }
.progress { border-radius: var(--r-pill); background: var(--bg-soft); height: 10px; }
html.dark .progress { background: rgba(255, 255, 255, 0.08); }
.progress-bar { background: linear-gradient(90deg, var(--sk-dk), var(--sk-lt)); border-radius: var(--r-pill); }
.form-control, .form-select { background: var(--bg-input); border: 1px solid var(--border-color); border-radius: var(--r-sm); color: var(--text-main); font-weight: 500; padding: 0.5rem 0.85rem; }
.form-control-sm, .form-select-sm { border-radius: var(--r-xs); padding: 0.28rem 0.6rem; font-size: 0.85rem; }
.form-control-lg, .form-select-lg { border-radius: var(--r-md); padding: 0.8rem 1.1rem; }
.form-control:focus, .form-select:focus { background: var(--bg-input); color: var(--text-main); border-color: var(--sk); box-shadow: 0 0 0 3px color-mix(in srgb, var(--sk) 18%, transparent); }
html.dark .form-control, html.dark .form-select { border-color: rgba(255, 255, 255, 0.12); }
.form-label { font-weight: 600; color: var(--text-main); }
.input-group-text { background: var(--bg-soft); border: 1px solid var(--border-color); border-radius: var(--r-sm); color: var(--text-muted); font-weight: 600; }
html.dark .input-group-text { border-color: rgba(255, 255, 255, 0.12); color: rgb(255, 255, 255); }
.form-check-input:checked { background-color: var(--sk-dk); border-color: var(--sk-dk); }
.form-switch .form-check-input:checked { background-color: var(--green); border-color: var(--green); }
.app-topbar { background: var(--bg-card); border-bottom: 1px solid var(--border-color); box-shadow: rgba(21, 145, 220, 0.06) 0px 2px 10px; height: 62px; padding: 0px 18px; }
.app-topbar .brand-name { font-family: var(--font-display); font-weight: 800; letter-spacing: -0.016em; color: var(--brand-dark) !important; }
html.dark .app-topbar .brand-name { color: var(--sk-lt) !important; }
.app-topbar .brand img { border-radius: var(--r-xs); }
.sidebar-toggle { color: var(--sk-dk); background: var(--bg-soft); border-radius: var(--r-sm); }
.sidebar-toggle:hover { background: var(--sk-wash); color: var(--sk-deep); }
.topbar-icon-btn { background: var(--bg-soft); border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; color: var(--sk-dk); border-radius: var(--r-full); }
.topbar-icon-btn:hover { background: var(--sk-dk); color: rgb(255, 255, 255); }
html.dark a.topbar-icon-btn { color: var(--sk-lt); }
html.dark a.topbar-icon-btn:hover { color: rgb(255, 255, 255); }
.profile-chip { background: var(--bg-soft); border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; color: var(--text-main); border-radius: var(--r-pill); font-weight: 600; padding: 4px 14px 4px 4px; }
.profile-chip:hover { background: var(--sk-wash); color: var(--sk-deep); }
html.dark .profile-chip { color: rgb(255, 255, 255); }
html.dark .profile-chip:hover { color: rgb(255, 255, 255); }
.notif-badge { border: 2px solid var(--bg-card); background: var(--warm-red) !important; }
.app-sidebar { background: var(--bg-card); border-right: 1px solid var(--border-color); width: 212px; padding: 14px 12px; gap: 4px; }
.sidebar-link { gap: 12px; padding: 10px 14px; color: var(--text-muted); border-radius: var(--r-md); font-weight: 600; flex-direction: row !important; justify-content: flex-start !important; }
.sidebar-link i { font-size: 1.15rem !important; color: var(--text-muted); }
.sidebar-link .sidebar-label { font-size: 0.9rem !important; line-height: 1.2; }
.sidebar-link:hover { background: var(--bg-soft); color: var(--sk-deep); }
.sidebar-link:hover i { color: var(--sk-dk); }
.sidebar-link.active { background: linear-gradient(135deg, var(--sk-dk) 0%, var(--sk-deep) 100%); color: rgb(255, 255, 255); box-shadow: 0 6px 16px color-mix(in srgb, var(--sk) 40%, transparent); }
.sidebar-link.active i { color: rgb(255, 255, 255); }
html.dark a.sidebar-link.active, html.dark a.sidebar-link.active i { color: rgb(255, 255, 255); }
.page-inner { padding: var(--sp-xxl) var(--sp-xxl) var(--sp-section); }
@media (max-width: 820px) {
  .page-inner { padding: var(--sp-lg) var(--sp-md) var(--sp-xxl); }
  .app-sidebar { background: var(--bg-card); }
}
.page-footer { border-top: 1px solid var(--border-color); }
.table-dark-custom { border-collapse: separate; border-spacing: 0px; }
.table-dark-custom thead th { background: color-mix(in srgb, var(--sk) 7%, var(--bg-card)); font-family: var(--font-display); font-weight: 700; font-size: 0.72rem; letter-spacing: 0.05em; text-transform: uppercase; padding: 0.8rem 1rem; white-space: nowrap; color: var(--sk-deep) !important; border-bottom: 2px solid color-mix(in srgb, var(--sk) 32%, transparent) !important; }
html.dark .table-dark-custom thead th { background: color-mix(in srgb, var(--sk) 10%, var(--bg-card)); color: var(--sk-lt) !important; border-bottom-color: color-mix(in srgb, var(--sk) 34%, transparent) !important; }
.card > .table-responsive > .table-dark-custom thead tr:first-child th:first-child { border-top-left-radius: 13px; }
.card > .table-responsive > .table-dark-custom thead tr:first-child th:last-child { border-top-right-radius: 13px; }
.table-dark-custom:not(.gradebook-table) tbody td { padding: 0.8rem 1rem; border-bottom: 1px solid color-mix(in srgb, var(--border-color) 72%, transparent); color: var(--text-main); }
.table-dark-custom:not(.gradebook-table) tbody tr:last-child td { border-bottom-width: medium; border-bottom-style: none; border-bottom-color: currentcolor; }
.table-dark-custom:not(.gradebook-table) tbody td:first-child { font-weight: 600; color: var(--text-main); }
html.dark .table-dark-custom:not(.gradebook-table) tbody td:first-child { color: rgb(255, 255, 255); }
.table-dark-custom:not(.gradebook-table) tbody tr { transition: background 0.12s; }
.table-dark-custom:not(.gradebook-table) tbody tr:hover td { background: color-mix(in srgb, var(--sk) 5%, var(--bg-card)); }
.table-dark-custom:not(.gradebook-table) tbody tr:hover td:first-child { box-shadow: inset 3px 0 0 var(--sk); }
.gradebook-wrapper { border-radius: var(--r-lg); border: 1px solid var(--border-color); background: var(--bg-card); box-shadow: var(--shadow); }
.gradebook-table thead th { font-family: var(--font-display); font-weight: 700; font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; background: color-mix(in srgb, var(--sk) 9%, var(--bg-card)) !important; color: var(--sk-deep) !important; border-bottom: 2px solid color-mix(in srgb, var(--sk) 32%, transparent) !important; }
.gradebook-table thead th:first-child { background: color-mix(in srgb, var(--sk) 15%, var(--bg-card)) !important; }
html.dark .gradebook-table thead th { color: var(--sk-lt) !important; }
.gradebook-table .col-sticky { box-shadow: 1px 0 0 var(--border-color); font-weight: 600; }
.gradebook-table tbody td { border-bottom: 1px solid color-mix(in srgb, var(--border-color) 65%, transparent); }
.gradebook-table tbody tr:hover td { background: color-mix(in srgb, var(--sk) 5%, var(--bg-card)); }
.score-input { border-radius: 8px; border: 1px solid var(--border-color); background: var(--bg-input); color: var(--text-main); font-weight: 600; transition: border-color 0.15s, box-shadow 0.15s, background 0.15s; }
.score-input:hover { border-color: var(--sk-lt); }
.score-input:focus { border-color: var(--sk); box-shadow: 0 0 0 3px color-mix(in srgb, var(--sk) 18%, transparent); }
.score-input.saved { border-color: var(--green); background: color-mix(in srgb, var(--green) 7%, var(--bg-input)); }
.score-input.saving { border-color: var(--gold); }
.score-input.error { border-color: var(--red); }
/* แสงตกแต่งมุมขวาบนต้องไม่เกิน .10 — จุดที่มันสว่างที่สุดตรงกับตำแหน่งกล่องเลือก
   ปีการศึกษาพอดี ที่ .22 พื้นถูกดันจาก #2C5EAD เป็น #5A81BF ทำให้ตัวขาวในกล่องนั้น
   เหลือ 3.06-3.94 (บั๊ก #4) · เอฟเฟกต์แสงยังเห็นอยู่ที่ .10 แต่ตัวหนังสือได้ 4.67  */
.dash-hero { background: radial-gradient(80% 160% at 96% 0%, rgba(255,255,255,.10) 0%, transparent 52%),
    /* ไล่จากเข้มไปอ่อนภายในเฉดของสีหลักที่แอดมินตั้ง — ทุกสต็อปผ่าน AA กับ
       --brand-on แล้วตั้งแต่ฝั่ง PHP จึงไม่มีทางเกิด "ตัวขาวบนพื้นสว่าง" อีก
       (ของเดิมตรึงเป็น --sk-deep → #045d90 → --sk-dk ซึ่งไม่ฟังค่าที่แอดมินตั้ง) */
    /* โทน backup: ไล่จากน้ำเงินไปฟ้าสว่าง ไม่ผ่านช่วง "ลึก" ที่ทำให้แถบดูทึบ
       ของเดิมเริ่มที่ --brand-deep (คอนทราสต์ 7.0) ซึ่งเข้มกว่าเว็บเก่าหนึ่งสเต็ป
       เริ่มที่ --brand-dark (5.5) แทน ปลายยังเป็น --brand-lt ซึ่ง palette.php
       ดันให้สว่างที่สุดเท่าที่หมึก --brand-on ยังทับได้ (4.8) อยู่แล้ว
       จะให้สว่างเท่า #4BB8FA ของเว็บเก่าไม่ได้ — ตัวขาวบนนั้นได้ 2.20 */
    linear-gradient(115deg, var(--brand-dark) 0%, var(--brand-lt) 100%); border-radius: var(--r-xl); padding: var(--sp-xxl) var(--sp-section); box-shadow: rgba(2, 132, 199, 0.26) 0px 10px 34px; color: var(--brand-on); }
.dash-hero::after { right: -60px; top: -80px; width: 280px; height: 280px; background: rgba(255, 255, 255, 0.1); border-radius: var(--r-full); pointer-events: none; }
.dash-hero > * { position: relative; z-index: 1; }
.term-picker { position: relative; display: inline-flex; align-items: center; gap: 0.7rem; padding: 0.45rem 2.4rem 0.45rem 0.7rem; border-radius: var(--r-pill); background: var(--bg-card); border: 1px solid var(--border-color); box-shadow: var(--shadow); transition: border-color 0.18s, box-shadow 0.18s, transform 0.18s; min-width: 232px; }
.term-picker:hover { border-color: var(--sk); box-shadow: 0 6px 20px color-mix(in srgb, var(--sk) 22%, transparent); }
.term-picker:has(select:focus-visible) { outline: 2px solid var(--sk); outline-offset: 2px; }
.term-picker > select { position: absolute; inset: 0px; width: 100%; height: 100%; opacity: 0; cursor: pointer; border: 0px; appearance: none; }
.term-picker > select option { color: var(--text-main); background: var(--bg-card); }
.tp-ic { flex: 0 0 auto; width: 2.1rem; height: 2.1rem; border-radius: var(--r-full); display: grid; place-items: center; font-size: 0.95rem; background: color-mix(in srgb, var(--sk) 14%, transparent); color: var(--sk-ink); }
.tp-main { display: flex; flex-direction: column; line-height: 1.2; min-width: 0px; }
/* 0.62rem = 9.9px ซึ่งต่ำกว่าเกณฑ์อ่านออกของข้อความไทย — สระบนล่างกับวรรณยุกต์
   ซ้อนกันจนกลายเป็นรอยเปื้อนบนจอความละเอียดปกติ · 0.7rem = 11.2px ยังคงเล็ก
   พอที่จะเป็นป้ายกำกับ แต่อ่านออกจริง */
.tp-label { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); }
.tp-val { font-family: var(--font-display); font-weight: 700; font-size: 0.92rem; color: var(--text-main); white-space: nowrap; }
.tp-sep { opacity: 0.85; margin: 0px 0.1rem; }
.tp-now { margin-left: 0.4rem; padding: 0.08rem 0.45rem; border-radius: var(--r-pill); font-family: var(--font-body); font-size: 0.68rem; font-weight: 700; vertical-align: middle; background: rgb(17, 121, 58); color: rgb(255, 255, 255); }
.tp-chev { position: absolute; right: 0.85rem; top: 50%; transform: translateY(-50%); font-size: 0.8rem; color: var(--text-muted); transition: transform 0.18s; }
.term-picker:hover .tp-chev { transform: translateY(-50%) translateY(1px); color: var(--sk-ink); }
.term-picker.on-hero { background: rgba(0, 0, 0, 0.28); border-color: rgba(255, 255, 255, 0.3); box-shadow: none; backdrop-filter: blur(6px); }
.term-picker.on-hero:hover { background: rgba(0, 0, 0, 0.38); border-color: rgba(255, 255, 255, 0.55); box-shadow: none; }
.term-picker.on-hero .tp-ic { background: rgba(255, 255, 255, 0.2); color: rgb(255, 255, 255); }
.term-picker.on-hero .tp-label { color: rgba(255, 255, 255, 0.92); }
.term-picker.on-hero .tp-val { color: rgb(255, 255, 255); }
.term-picker.on-hero .tp-chev { color: rgba(255, 255, 255, 0.8); }
.term-picker.on-hero:hover .tp-chev { color: rgb(255, 255, 255); }
.term-picker.on-hero .tp-now { background: rgba(255, 255, 255, 0.9); color: var(--sk-deep); }
.term-picker.on-hero > select option { color: var(--text-main); background: var(--bg-card); }
@media (max-width: 575px) {
  .term-picker { min-width: 0px; width: 100%; }
  .tp-val { font-size: 0.86rem; white-space: normal; }
}
@media print {
  .term-picker { display: none !important; }
}
.hero-avatar { width: 64px; height: 64px; min-width: 64px; border-radius: var(--r-full); overflow: hidden; display: flex; align-items: center; justify-content: center; background: rgba(255, 255, 255, 0.2); box-shadow: rgba(255, 255, 255, 0.35) 0px 0px 0px 2px inset; text-decoration: none; transition: box-shadow 0.18s, transform 0.18s; }
.hero-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero-avatar:hover { transform: scale(1.04); box-shadow: rgba(255, 255, 255, 0.85) 0px 0px 0px 2px inset; }
html.dark a.hero-avatar { color: rgb(255, 255, 255); }
@media (max-width: 575px) {
  .hero-avatar { width: 52px; height: 52px; min-width: 52px; }
}
.dash-hero h3, .dash-hero h4 { font-family: var(--font-display); font-weight: 700; font-size: clamp(1.5rem, 3vw, 2.2rem); letter-spacing: -0.02em; }
.dash-hero .sub { opacity: 0.92; font-size: 1rem; font-weight: 500; }
.dash-hero .badge, .dash-hero .badge.bg-light { border-radius: var(--r-pill); background: rgba(255, 255, 255, 0.18) !important; color: rgb(255, 255, 255) !important; }
.stat-card { border-radius: var(--r-lg); border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; box-shadow: none; }
.stat-card:hover { transform: translateY(-3px); box-shadow: rgba(2, 132, 199, 0.22) 0px 10px 30px; }
.stat-card .stat-val { font-family: var(--font-display); font-size: 2.2rem; font-weight: 700; letter-spacing: -0.02em; }
.stat-card .stat-label { font-weight: 600; font-size: 0.84rem; }
.stat-card .stat-icon { font-size: 3rem; opacity: 0.22; right: 18px; }
.stat-1, .card.stat-1 { background: linear-gradient(135deg, var(--sk-dk) 0%, var(--sk-deep) 100%); color: rgb(255, 255, 255); }
.stat-2, .card.stat-2 { background: linear-gradient(135deg, var(--sk-deep) 0%, #086699 100%); color: rgb(255, 255, 255); }
.stat-3, .card.stat-3 { background: linear-gradient(135deg, var(--sk-lt) 0%, #38bdf8 100%); color: rgb(2, 64, 95); }
.stat-4, .card.stat-4 { background: linear-gradient(135deg, var(--gold) 0%, #d99a10 100%); color: var(--ink-on-gold); }
.stat-1 .stat-label, .stat-2 .stat-label, .stat-1 .stat-val, .stat-2 .stat-val { color: rgb(255, 255, 255); }
.stat-3 .stat-label, .stat-3 .stat-val { color: rgb(2, 64, 95); }
.stat-4 .stat-label, .stat-4 .stat-val { color: var(--ink-on-gold); }
.stat-3 .stat-icon, .stat-4 .stat-icon { opacity: 0.34; }
html.dark .card.stat-1 { background: linear-gradient(135deg, var(--sk-dk) 0%, var(--sk-deep) 100%); color: rgb(255, 255, 255); }
html.dark .card.stat-2 { background: linear-gradient(135deg, rgb(8, 80, 124) 0%, rgb(7, 63, 97) 100%); color: rgb(255, 255, 255); }
html.dark .card.stat-3 { background: linear-gradient(135deg, rgb(30, 111, 160) 0%, rgb(23, 88, 127) 100%); color: rgb(234, 244, 252); }
html.dark .card.stat-3 .stat-label, html.dark .card.stat-3 .stat-val { color: rgb(234, 244, 252); }
html.dark .card.stat-4 { background: linear-gradient(135deg, rgb(143, 100, 9) 0%, rgb(109, 75, 5) 100%); color: rgb(255, 248, 230); }
html.dark .card.stat-4 .stat-label, html.dark .card.stat-4 .stat-val { color: rgb(255, 248, 230); }
.cw-stat { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--r-lg); box-shadow: none; }
.cw-stat::after { background: color-mix(in srgb, var(--sk) 10%, transparent); opacity: 1; }
.cw-stat .num { font-family: var(--font-display); font-weight: 700; }
a.cw-stat-link:hover { border-color: var(--sk); box-shadow: rgba(2, 132, 199, 0.14) 0px 6px 24px; }
a.cw-stat-link.cw-stat-on { border-color: var(--sk); box-shadow: inset 0 0 0 2px var(--sk); }
.course-tile { border-radius: var(--r-xl); border-color: var(--border-color); background: var(--bg-card); box-shadow: var(--shadow); }
.course-tile .tile-top { height: 8px; background: linear-gradient(90deg, var(--sk-dk), var(--sk-lt)); }
.proj-box { border-radius: var(--r-xl); border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; color: rgb(255, 255, 255); padding: var(--sp-xl); box-shadow: rgba(2, 132, 199, 0.14) 0px 6px 22px; }
.proj-box:hover { box-shadow: rgba(2, 132, 199, 0.26) 0px 10px 34px; }
.proj-box .pb-title { font-family: var(--font-display); font-weight: 700; font-size: 1.15rem; letter-spacing: -0.014em; }
.proj-box .pb-code, .proj-box .pb-chip, .proj-box .pb-grade { background: rgba(255, 255, 255, 0.22); color: rgb(255, 255, 255) !important; }
.proj-box .pb-grade { border-radius: var(--r-sm); }
.proj-box .pb-chip { border-radius: var(--r-pill); }
.proj-box .pb-bar { background: rgba(255, 255, 255, 0.3); border-radius: var(--r-pill); }
.proj-box .pb-bar > span { background: rgb(255, 255, 255); }
.proj-box .text-muted, html.dark .proj-box .text-muted { color: rgba(255, 255, 255, 0.8) !important; }
html.dark .proj-box { color: rgb(255, 255, 255); }
.pb-c1 { --pb: linear-gradient(135deg,#0472ae,#0369a1); --pb-accent: #fff; }
.pb-c2 { --pb: linear-gradient(135deg,#0369a1,#086699); --pb-accent: #fff; }
.pb-c3 { --pb: linear-gradient(135deg,#0c4a6e,#155e85); --pb-accent: #fff; }
.pb-c4 { --pb: linear-gradient(135deg,#045d90,#0472ae); --pb-accent: #fff; }
.pb-c5 { --pb: linear-gradient(135deg,#0e7490,#0d5f75); --pb-accent: #fff; }
.pb-c6 { --pb: linear-gradient(135deg,#1e5f8a,#164a6d); --pb-accent: #fff; }
.proj-box { background: var(--pb); }
.cw-task { border-radius: var(--r-lg); }
.cw-task:hover { border-color: var(--sk); }
.cw-subj { background: color-mix(in srgb, var(--sk) 12%, transparent); color: var(--sk-deep); border-radius: var(--r-xs); }
html.dark .cw-subj { color: rgb(186, 230, 253); background: rgba(14, 165, 233, 0.22); }
.cw-prog > i { background: linear-gradient(90deg, var(--sk-dk), var(--sk-lt)); }
.cw-badge { border-radius: var(--r-pill); }
.cw-b-wait { background: color-mix(in srgb, var(--sk) 12%, transparent); color: var(--sk-deep); }
.cw-b-warn { background: color-mix(in srgb, var(--gold) 18%, transparent); color: rgb(138, 90, 0); }
.cw-b-group { background: color-mix(in srgb, var(--sk-lt) 26%, transparent); color: rgb(7, 89, 133); }
html.dark .cw-b-wait { background: rgba(14, 165, 233, 0.24); color: rgb(186, 230, 253); }
html.dark .cw-b-warn { background: rgba(242, 176, 30, 0.22); color: rgb(255, 214, 113); }
html.dark .cw-b-group { background: rgba(125, 211, 252, 0.22); color: rgb(201, 236, 255); }
.cw-feedback { border-radius: var(--r-sm); }
.cw-feedback.wait { background: color-mix(in srgb, var(--sk) 10%, transparent); border-color: color-mix(in srgb, var(--sk) 24%, transparent); color: var(--sk-deep); }
html.dark .cw-feedback.wait { background: rgba(14, 165, 233, 0.16); border-color: rgba(14, 165, 233, 0.34); color: rgb(186, 230, 253); }
/* `.sg-pop` ถูกถอดออกจากรายการนี้ — ป็อปอัปมีมุมโค้งของตัวเอง (28px) ที่ style.css
   กฎนี้อยู่หลังในลำดับไฟล์ จึงเคยทับค่านั้นให้เหลือ 16px เงียบ ๆ */
.group-card, .code-card, .pin-card, .att-card, .att-stat, .ecal-wrap, .cw-card, .overdue-card, .submission-preview { border-radius: var(--r-lg); }
.att-ticket { border-radius: var(--r-xl); box-shadow: var(--shadow-lg); }
.ecal-card { border-radius: var(--r-xl); border-color: var(--border-color); box-shadow: var(--shadow); }
.ecal-filter { border-radius: var(--r-pill); font-weight: 600; }
.ecal-filter:hover { border-color: var(--sk); }
.ecal-filter.is-on { background: var(--sk-wash); border-color: var(--sk); color: var(--sk-deep); }
html.dark .ecal-filter.is-on { background: rgba(14, 165, 233, 0.18); color: var(--sk-lt); }
.ecal-dow { background: color-mix(in srgb, var(--sk) 7%, var(--bg-card)); color: var(--sk-deep); font-family: var(--font-display); text-transform: uppercase; }
html.dark .ecal-dow { color: var(--sk-lt); background: color-mix(in srgb, var(--sk) 10%, var(--bg-card)); }
.ecal-cell:hover { background: color-mix(in srgb, var(--sk) 5%, transparent); }
.ecal-today { background: color-mix(in srgb, var(--sk) 7%, transparent); }
.ecal-cell.sel { box-shadow: inset 0 0 0 2px var(--sk); border-radius: 10px; }
.ecal-daynum { font-weight: 700; }
.ecal-daynum.is-today { background: linear-gradient(135deg, var(--sk-dk), var(--sk-deep)); color: rgb(255, 255, 255); box-shadow: 0 3px 10px color-mix(in srgb, var(--sk) 45%, transparent); }
.ecal-hasdot { background: var(--sk); }
.ecal-dot-class { background: var(--sk); }
.ecal-dot-ses { background: var(--green); }
.ecal-dot-asg { background: var(--gold); }
.ecal-pill { border-radius: 6px; padding: 2px 6px; font-size: 0.68rem; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; }
.ecal-pill.ecal-class { background: var(--sk-wash); color: var(--sk-deep); }
.ecal-pill.ecal-ses { background: color-mix(in srgb, var(--green) 12%, transparent); color: rgb(15, 107, 51); }
.ecal-pill.ecal-asg { background: color-mix(in srgb, var(--gold) 18%, transparent); color: rgb(138, 90, 0); }
html.dark .ecal-pill.ecal-class { background: rgba(14, 165, 233, 0.24); color: rgb(186, 230, 253); }
html.dark .ecal-pill.ecal-ses { background: rgba(22, 163, 74, 0.22); color: rgb(143, 240, 182); }
html.dark .ecal-pill.ecal-asg { background: rgba(242, 176, 30, 0.22); color: rgb(255, 214, 113); }
.card.ecal-agenda, .ecal-agenda { background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main); border-radius: var(--r-xl); box-shadow: var(--shadow); }
.ecal-agenda-label { color: var(--sk-dk); }
html.dark .ecal-agenda-label { color: var(--sk-lt); }
.ecal-empty { color: var(--text-muted); }
.ecal-ag-item { background: var(--bg-soft); color: var(--text-main); border-radius: 12px; box-shadow: none; }
.ecal-ag-item:hover { background: var(--sk-wash); color: var(--text-main); }
html.dark .ecal-ag-item { background: rgba(255, 255, 255, 0.05); color: var(--text-main); }
html.dark .ecal-ag-item:hover { background: rgba(14, 165, 233, 0.16); color: rgb(255, 255, 255); }
.ecal-ag-item.ecal-class .ecal-ag-bar { background: var(--sk); }
.ecal-ag-item.ecal-ses .ecal-ag-bar { background: var(--green); }
.ecal-ag-item.ecal-asg .ecal-ag-bar { background: var(--gold); }
.ecal-ag-tag { background: var(--sk-wash); color: var(--sk-deep); }
html.dark .ecal-ag-tag { background: rgba(14, 165, 233, 0.2); color: rgb(186, 230, 253); }
.ecal-ag-tag2 { background: var(--bg-card); color: var(--text-muted); border: 1px solid var(--border-color); }
html.dark .ecal-ag-tag2 { background: rgba(255, 255, 255, 0.06); border-color: transparent; }
.ecal-ag-time { color: var(--text-muted); }
.ecal-ag-go { color: var(--sk-dk); }
html.dark .ecal-ag-go { color: var(--sk-lt); }
.att-stat .ico { border-radius: var(--r-md); }
.att-stat .ico.c1 { background: linear-gradient(135deg, #0284c7, #0369a1); }
.att-stat .ico.c2 { background: linear-gradient(135deg, #0891b2, #0e7490); }
.att-stat .ico.c3 { background: linear-gradient(135deg, #b87708, #9c6406); }
.att-stat .ico.c4 { background: linear-gradient(135deg, #16a34a, #12803b); }
.att-prog { border-radius: var(--r-pill); }
.sg-toast { border-radius: var(--r-md); }
.sg-toast.t-warning { --tclr: var(--gold); }
.sg-toast.t-help { --tclr: var(--sk-dk); }
.ann-item.ann-pinned { border-left-color: var(--gold); background: color-mix(in srgb, var(--gold) 8%, transparent); }
.lb-rank-1 { background: linear-gradient(135deg, rgb(242, 176, 30) 0%, rgb(217, 147, 13) 100%); color: rgb(255, 255, 255); }
.lb-rank-2 { background: linear-gradient(135deg, rgb(147, 168, 200) 0%, rgb(110, 134, 173) 100%); color: rgb(255, 255, 255); }
.lb-rank-3 { background: linear-gradient(135deg, rgb(201, 141, 84) 0%, rgb(169, 111, 60) 100%); color: rgb(255, 255, 255); }
.lb-rank-1 .text-muted, .lb-rank-2 .text-muted, .lb-rank-3 .text-muted { color: rgba(255, 255, 255, 0.8) !important; }
.avatar-circle, .avatar-initials { border-radius: var(--r-full); }
.school-logo-nav { border-radius: var(--r-xs); }
.dc-canvas { background: linear-gradient(rgb(224, 242, 254) 0%, rgb(240, 249, 255) 45%, rgb(255, 255, 255) 100%); color: var(--text-main); min-height: 100vh; position: relative; overflow-x: hidden; font-family: var(--font-body); }
.dc-canvas::before { content: ""; position: fixed; inset: 0px; z-index: 0; pointer-events: none; background: radial-gradient(42vw 30vw at 92% 0%, rgba(125, 211, 252, 0.45), transparent 66%), radial-gradient(46vw 32vw at 4% 6%, rgba(140, 192, 235, 0.4), transparent 66%); }
.dc-canvas > * { position: relative; z-index: 1; }
.dc-container { max-width: 1200px; margin: 0px auto; padding: 0 var(--sp-xl); }
.dc-hero-card { position: relative; overflow: hidden; background: linear-gradient(115deg, var(--sk-deep) 0%, var(--sk-dk) 52%, var(--sk) 100%); border-radius: var(--r-xl); padding: clamp(36px, 6vw, 64px) clamp(24px, 5vw, 56px); color: rgb(255, 255, 255); text-align: center; box-shadow: rgba(2, 132, 199, 0.28) 0px 12px 40px; }
.dc-hero-card::before { content: ""; position: absolute; inset: 0px; pointer-events: none; background: radial-gradient(36% 58% at 92% 0%, rgba(255, 255, 255, 0.22), transparent 62%), radial-gradient(40% 60% at 4% 100%, rgba(125, 211, 252, 0.3), transparent 64%), radial-gradient(24% 38% at 60% 118%, rgba(242, 176, 30, 0.18), transparent 64%); }
.dc-hero-card > * { position: relative; z-index: 1; }
.dc-hero-card h1, .dc-hero h1, .dc-band h2, .dc-panel h2, .dc-panel-gradient h2, .dash-hero h3, .dash-hero h4 { overflow-wrap: anywhere; word-break: break-word; }
.dc-hero-card h1 { font-family: var(--font-display); font-weight: 700; font-size: clamp(1.9rem, 4.6vw, 3.4rem); line-height: 1.08; letter-spacing: -0.025em; color: rgb(255, 255, 255); margin: var(--sp-md) 0; }
.dc-hero-card .lead { font-size: clamp(1rem, 1.4vw, 1.15rem); font-weight: 500; color: rgba(255, 255, 255, 0.88); max-width: 620px; margin: 0 auto var(--sp-xl); }
.dc-hero-logo { height: 84px; width: auto; object-fit: contain; filter: drop-shadow(rgba(3, 105, 161, 0.35) 0px 8px 24px); }
.dc-swoop { border-radius: var(--r-lg) var(--r-lg) 60px 60px; overflow: hidden; }
.dc-hero { padding: clamp(44px,8vw,96px) 0 var(--sp-section); text-align: center; }
.dc-hero h1 { font-family: var(--font-display); font-weight: 700; font-size: clamp(2.1rem, 6vw, 4rem); line-height: 1.06; letter-spacing: -0.028em; color: var(--sk-deep); margin: var(--sp-md) 0; }
.dc-hero p.lead { font-size: clamp(1rem, 1.5vw, 1.2rem); font-weight: 500; color: var(--text-muted); max-width: 640px; margin: 0 auto var(--sp-xl); }
.dc-cta-row { display: flex; flex-wrap: wrap; gap: var(--sp-md); justify-content: center; }
.dc-btn { display: inline-flex; align-items: center; gap: 10px; font-family: var(--font-body); font-weight: 600; font-size: 1rem; padding: 14px 28px; border-radius: var(--r-sm); border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; text-decoration: none; min-height: 44px; cursor: pointer; transition: transform 0.15s, box-shadow 0.2s, background 0.2s; }
.dc-btn:hover { transform: translateY(-2px); }
.dc-btn-green { background: var(--sk-dk); color: rgb(255, 255, 255); font-weight: 700; box-shadow: rgba(2, 132, 199, 0.34) 0px 8px 26px; }
.dc-btn-green:hover { background: var(--sk-deep); color: rgb(255, 255, 255); }
.dc-btn-primary { background: var(--sk); color: rgb(255, 255, 255); box-shadow: rgba(14, 165, 233, 0.34) 0px 8px 26px; }
.dc-btn-primary:hover { background: var(--sk-dk); color: rgb(255, 255, 255); }
.dc-btn-white { background: rgb(255, 255, 255); color: var(--sk-deep); border-radius: var(--r-sm); box-shadow: rgba(3, 105, 161, 0.14) 0px 4px 18px; }
.dc-btn-white:hover { background: rgb(240, 249, 255); color: var(--sk-deep); }
.dc-btn-ghost { background: rgba(255, 255, 255, 0.16); color: rgb(255, 255, 255); border-radius: var(--r-sm); border: 1px solid rgba(255, 255, 255, 0.3); }
.dc-btn-ghost:hover { background: rgba(255, 255, 255, 0.26); color: rgb(255, 255, 255); }
.dc-panel { background: var(--bg-card); border-radius: var(--r-xl); padding: var(--sp-section); color: var(--text-main); border: 1px solid var(--border-color); box-shadow: var(--shadow); }
.dc-panel h2, .dc-panel h3, .dc-panel h5 { color: var(--text-main); }
.dc-panel-dark { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.12); color: rgb(255, 255, 255); box-shadow: none; }
.dc-panel-dark h2, .dc-panel-dark h3, .dc-panel-dark h5 { color: rgb(255, 255, 255) !important; }
.dc-panel-dark .text-muted { color: rgba(255, 255, 255, 0.62) !important; }
.dc-panel-gradient { background: linear-gradient(135deg, var(--sk-deep) 0%, var(--sk-dk) 55%, var(--sk) 100%); border-radius: var(--r-xl); padding: var(--sp-section); color: rgb(255, 255, 255); }
.dc-band { background: linear-gradient(115deg, var(--sk-deep), var(--sk-dk) 60%, var(--sk)); border-radius: var(--r-xl); padding: var(--sp-section); color: rgb(255, 255, 255); text-align: center; }
.dc-band h2 { font-family: var(--font-display); font-weight: 700; font-size: clamp(1.5rem, 3.2vw, 2.4rem); letter-spacing: -0.02em; margin-bottom: var(--sp-md); }
.dc-band-black { background: linear-gradient(160deg, rgb(12, 74, 110), rgb(7, 89, 133)); border-radius: var(--r-xl); padding: var(--sp-section); color: rgb(255, 255, 255); }
.dc-marquee { background: linear-gradient(90deg, var(--sk-deep), var(--sk-dk)); padding: var(--sp-md) 0; overflow: hidden; white-space: nowrap; mask-image: linear-gradient(90deg, transparent, rgb(0, 0, 0) 8%, rgb(0, 0, 0) 92%, transparent); }
.dc-marquee-track { display: inline-flex; gap: var(--sp-section); animation: 26s linear 0s infinite normal none running dcScroll; }
.dc-marquee span { font-family: var(--font-display); font-weight: 700; color: rgba(255, 255, 255, 0.92); font-size: clamp(1.1rem, 2.2vw, 1.8rem); letter-spacing: 0.02em; text-transform: uppercase; }
@keyframes dcScroll { 
  0% { transform: translateX(0px); }
  100% { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .dc-marquee-track { animation: auto ease 0s 1 normal none running none; }
}
.dc-wordmark { font-family: var(--font-display); font-weight: 800; text-transform: uppercase; font-size: clamp(3rem, 13vw, 11rem); line-height: 0.85; letter-spacing: -0.05em; color: color-mix(in srgb, var(--sk) 14%, transparent); text-align: center; user-select: none; padding: var(--sp-section) 0 0; overflow: hidden; white-space: nowrap; }
.dc-tile { background: var(--bg-card); border-radius: var(--r-lg); padding: var(--sp-xl); color: var(--text-main); text-decoration: none; display: block; transition: transform 0.18s, box-shadow 0.18s; border: 1px solid var(--border-color); }
.dc-tile:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); color: var(--text-main); border-color: var(--sk-lt); }
.dc-tile h5 { font-family: var(--font-display); font-weight: 700; color: var(--text-main); }
.dc-badge { display: inline-block; background: var(--sk-wash); color: var(--sk-deep); font-size: 0.78rem; font-weight: 700; border-radius: var(--r-xs); padding: var(--sp-xxs) var(--sp-sm); }
.dc-rank-row { background: rgba(255, 255, 255, 0.07); border-radius: var(--r-md); padding: var(--sp-sm) var(--sp-md); color: rgb(255, 255, 255); display: flex; align-items: center; gap: var(--sp-md); min-height: 44px; border: 1px solid rgba(255, 255, 255, 0.1); }
.dc-rank-row + .dc-rank-row { margin-top: var(--sp-xs); }
.dc-rank-num { font-family: var(--font-display); font-weight: 800; font-size: 1.15rem; color: var(--gold); min-width: 30px; }
.swal2-popup { border-radius: var(--r-xl) !important; background: var(--bg-card) !important; color: var(--text-main) !important; font-family: var(--font-body) !important; }
.swal2-title { font-family: var(--font-display) !important; font-weight: 700 !important; color: var(--text-main) !important; }
.swal2-styled.swal2-confirm { background: var(--sk-dk) !important; border-radius: var(--r-sm) !important; font-weight: 600 !important; }
.swal2-styled.swal2-cancel { background: var(--bg-soft) !important; color: var(--text-main) !important; border-radius: var(--r-sm) !important; font-weight: 600 !important; }
/* ── ป็อปอัป `.sg-pop` ย้ายไปคุมที่ style.css ทั้งก้อนแล้ว (20 ส.ค. 2569) ─────
   ของเดิมทับไว้ตรงนี้หกกฎ (มุมโค้ง · สีไอคอนต่อโทน · แถบนับถอยหลัง · ปุ่มหลัก)
   ซึ่งชนะเพราะ theme.css โหลดทีหลัง · ดีไซน์ใหม่เดินด้วย `--pt` ที่ตั้งจาก
   `data-tone` บนตัวการ์ด กฎเก่าจึงกลายเป็นตัวทับที่ทำให้โทนสีไม่ครบใบ
   **เหตุผลเดิมยังใช้อยู่ ย้ายไปเขียนไว้ที่ style.css แล้ว:**
   ไอคอนต้องใช้สีระดับ ink ไม่ใช่ระดับ fill (ไม่มีตัวตรวจ contrast ไหนจับ
   เพราะไอคอนไม่มี text node) และปุ่มหลักห้ามผูกกับ `--brand` ดิบ ๆ         */
.t-info { --nt: var(--sk-dk); }
/* --nt ถูกใช้เป็น "หมึก" ของป้ายประเภทการแจ้งเตือน (ตัวเล็ก 0.68rem) จึงต้อง
   ใช้ระดับ ink ไม่ใช่ระดับ fill — --green (#16a34a) ได้ 3.3 บนขาว และ
   --gold-dk (#b87708) ได้ 3.70 ทั้งคู่ต่ำกว่าเกณฑ์ตัวเล็ก                    */
.t-success { --nt: var(--green-ink); }
.t-warning { --nt: var(--gold-ink); }
.t-danger { --nt: var(--red-ink); }
html.dark .t-info { --nt: var(--sk-lt); }
html.dark .t-warning { --nt: var(--gold); }
.notif-btn { width: 40px; height: 40px; flex: 0 0 auto; position: relative; display: flex; align-items: center; justify-content: center; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; border-radius: var(--r-full); background: var(--bg-soft); cursor: pointer; transition: background 0.25s, transform 0.12s; }
.notif-btn:hover { background: var(--sk-dk); }
.notif-btn:active { transform: scale(0.88); }
.notif-btn .bell { width: 18px; height: 18px; display: block; transform-origin: center top; }
.notif-btn .bell path { fill: var(--sk-dk); transition: fill 0.2s; }
.notif-btn:hover .bell path { fill: rgb(255, 255, 255); }
html.dark .notif-btn .bell path { fill: var(--sk-lt); }
html.dark .notif-btn:hover .bell path { fill: rgb(255, 255, 255); }
.notif-btn.has-unread .bell path { fill: var(--gold-ink); }
html.dark .notif-btn.has-unread .bell path { fill: var(--gold); }
/* **ต้องมีทั้งคู่และต้องอยู่ท้ายสุด** — `html.dark .notif-btn.has-unread .bell path`
   คือ (0,4,1) ซึ่งชนะ `.notif-btn.has-unread:hover .bell path` (0,4,0)
   ผลคือในโหมดมืด+มีแจ้งเตือนค้าง พอเอาเมาส์ไปชี้ พื้นปุ่มเปลี่ยนเป็นน้ำเงินเข้ม
   แต่กระดิ่งยังเป็นสีทองเดิม ตัดกันน้อยมากจนดูเหมือนกระดิ่ง "จางหายไป"
   (เจ้าของเจอเอง) · เขียนคู่โหมดมืดให้เป็น (0,5,1) จึงชนะได้จริง          */
.notif-btn.has-unread:hover .bell path,
.notif-btn.has-unread:focus-visible .bell path { fill: rgb(255, 255, 255); }
html.dark .notif-btn.has-unread:hover .bell path,
html.dark .notif-btn.has-unread:focus-visible .bell path { fill: rgb(255, 255, 255); }
/* กันกรณีไม่มีแจ้งเตือนค้างด้วย — กฎ html.dark ปกติก็ (0,3,1) ชนะ hover (0,3,0) เหมือนกัน */
html.dark .notif-btn:hover .bell path,
html.dark .notif-btn:focus-visible .bell path { fill: rgb(255, 255, 255); }
.notif-btn:hover .bell, .notif-btn.ring .bell { animation: 0.9s ease 0s 1 normal both running bellRing; }
@keyframes bellRing { 
  0%, 100% { transform: rotateZ(0deg); }
  15% { transform: rotateZ(10deg); }
  30% { transform: rotateZ(-10deg); }
  45% { transform: rotateZ(5deg); }
  60% { transform: rotateZ(-5deg); }
  75% { transform: rotateZ(2deg); }
}
@media (prefers-reduced-motion: reduce) {
  .notif-btn .bell { animation: auto ease 0s 1 normal none running none !important; }
}
.notif-btn .notif-badge { position: absolute; top: -2px; right: -2px; min-width: 19px; height: 19px; padding: 0px 5px; display: flex; align-items: center; justify-content: center; border-radius: var(--r-full); color: rgb(255, 255, 255); font-family: var(--font-body); font-size: 0.68rem; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; box-shadow: color-mix(in srgb, var(--red) 50%, transparent) 0px 2px 6px; background: var(--red) !important; border: 2px solid var(--bg-card) !important; }
.notif-menu { min-width: 350px; max-width: 390px; padding: 0px; overflow: hidden; border-radius: var(--r-lg); border: 1px solid var(--border-color); background: var(--bg-card); box-shadow: var(--shadow-lg); }
.notif-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 0.7rem 0.9rem; border-bottom: 1px solid var(--border-color); background: var(--bg-soft); }
.notif-head-t { font-family: var(--font-display); font-weight: 700; font-size: 0.95rem; color: var(--text-main); }
.notif-head-t i { color: var(--sk-dk); margin-right: 0.4rem; }
.notif-head-a { border: 0px; background: none; padding: 0px; cursor: pointer; font-size: 0.78rem; font-weight: 600; color: var(--sk-dk); }
.notif-head-a:hover { text-decoration: underline; }
.notif-scroll { max-height: 60vh; overflow-y: auto; overscroll-behavior: contain; }
.notif-item { display: flex; gap: 0.7rem; align-items: flex-start; padding: 0.7rem 0.9rem; text-decoration: none; border-bottom: 1px solid var(--border-color); transition: background 0.15s; }
.notif-item:last-child { border-bottom-width: medium; border-bottom-style: none; border-bottom-color: currentcolor; }
.notif-item:hover { background: var(--bg-soft); }
.notif-item.unread { background: color-mix(in srgb, var(--nt, var(--sk)) 8%, transparent); }
.notif-ic { flex: 0 0 auto; width: 2.1rem; height: 2.1rem; border-radius: var(--r-full); display: grid; place-items: center; font-size: 0.95rem; background: color-mix(in srgb, var(--nt, var(--sk)) 14%, var(--bg-card)); color: var(--nt, var(--sk)); }
.notif-txt { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0px; flex: 1 1 auto; }
.notif-title { font-weight: 600; font-size: 0.84rem; color: var(--text-main); line-height: 1.35; overflow-wrap: anywhere; }
.notif-item.unread .notif-title::after { content: ""; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--nt, var(--sk)); margin-left: 6px; vertical-align: middle; }
.notif-body { font-size: 0.76rem; color: var(--text-muted); line-height: 1.35; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.notif-time { font-size: 0.7rem; color: var(--text-muted); opacity: 0.85; }
.notif-empty { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; padding: 2.2rem 1rem; color: var(--text-muted); font-size: 0.85rem; }
.notif-empty i { font-size: 1.8rem; opacity: 0.5; }
.notif-foot { display: block; text-align: center; padding: 0.65rem; font-size: 0.8rem; font-weight: 600; color: var(--sk-dk); text-decoration: none; border-top: 1px solid var(--border-color); background: var(--bg-soft); }
.notif-foot:hover { background: var(--sk-wash); color: var(--sk-deep); }
html.dark .notif-foot:hover { background: rgba(125, 211, 252, 0.12); }
@media (max-width: 575px) {
  .notif-menu { min-width: min(92vw, 350px); max-width: 92vw; }
}
.nf-filters { display: flex; flex-direction: column; gap: 0.5rem; }
.nf-filter-row { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.nf-filter-lb { font-size: 0.78rem; font-weight: 700; color: var(--text-muted); min-width: 3.6rem; }
.nf-chip { display: inline-flex; align-items: center; gap: 0.32rem; padding: 0.3rem 0.72rem; border-radius: var(--r-pill); font-size: 0.78rem; font-weight: 600; text-decoration: none; color: var(--text-main); background: var(--bg-card); border: 1px solid var(--border-color); transition: background 0.15s, border-color 0.15s, color 0.15s; }
.nf-chip i { color: var(--nt, var(--sk-dk)); font-size: 0.82rem; }
.nf-chip b { font-weight: 700; opacity: 0.85; }
.nf-chip:hover { border-color: var(--nt, var(--sk)); color: var(--nt, var(--sk-dk)); }
.nf-chip.on { background: var(--nt, var(--sk-dk)); border-color: var(--nt, var(--sk-dk)); color: rgb(255, 255, 255); }
.nf-chip.on i, .nf-chip.on b { color: rgb(255, 255, 255); opacity: 0.85; }
.nf-list { display: flex; flex-direction: column; gap: 0.55rem; }
.nf-item { display: flex; align-items: flex-start; gap: 0.8rem; padding: 0.85rem 1rem; background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--r-lg); box-shadow: var(--shadow); transition: transform 0.15s, box-shadow 0.15s, opacity 0.18s; }
.nf-item:hover { transform: translateY(-1px); box-shadow: var(--shadow-lg); }
.nf-item.unread { border-left: 4px solid var(--nt, var(--sk)); }
.nf-item.going { opacity: 0; transform: translateX(28px); }
.nf-ic { flex: 0 0 auto; width: 2.5rem; height: 2.5rem; border-radius: var(--r-full); display: grid; place-items: center; font-size: 1.05rem; background: color-mix(in srgb, var(--nt, var(--sk)) 14%, var(--bg-card)); color: var(--nt, var(--sk)); }
.nf-main { flex: 1 1 auto; min-width: 0px; text-decoration: none; color: inherit; display: block; }
.nf-nolink { cursor: default; }
.nf-top { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.15rem; }
.nf-type { font-size: 0.68rem; font-weight: 700; letter-spacing: 0.02em; text-transform: uppercase; padding: 0.1rem 0.45rem; border-radius: var(--r-xs); background: color-mix(in srgb, var(--nt, var(--sk)) 14%, transparent); color: var(--nt, var(--sk-dk)); }
.nf-time { font-size: 0.72rem; color: var(--text-muted); }
.nf-title { font-weight: 600; font-size: 0.9rem; color: var(--text-main); line-height: 1.4; overflow-wrap: anywhere; }
.nf-item.unread .nf-title { font-weight: 700; }
.nf-body { font-size: 0.8rem; color: var(--text-muted); line-height: 1.45; overflow-wrap: anywhere; }
.nf-actions { flex: 0 0 auto; display: flex; gap: 0.25rem; }
.nf-btn { width: 1.9rem; height: 1.9rem; border-radius: var(--r-sm); border: 1px solid var(--border-color); background: var(--bg-card); color: var(--text-muted); font-size: 0.82rem; display: grid; place-items: center; cursor: pointer; transition: 0.15s; }
.nf-btn:hover { background: var(--sk-dk); border-color: var(--sk-dk); color: rgb(255, 255, 255); }
.nf-btn.danger:hover { background: var(--red); border-color: var(--red); }
@media (max-width: 575px) {
  .nf-item { padding: 0.7rem 0.8rem; gap: 0.6rem; }
  .nf-ic { width: 2.1rem; height: 2.1rem; font-size: 0.9rem; }
  .nf-filter-lb { min-width: 100%; }
}
.sg-toast { --tclr: var(--nt, var(--sk-dk)); border-radius: var(--r-md); padding-bottom: 1rem; }
.sg-toast.t-help { --tclr: var(--sk-dk); }
.sg-toast-kind { font-size: 0.64rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--tclr); opacity: 0.85; margin-bottom: 0.05rem; }
.sg-toast-title { color: var(--text-main); }
.sg-toast-bar { position: absolute; left: 0.9rem; right: 0.9rem; bottom: 0.5rem; height: 3px; border-radius: var(--r-pill); background: color-mix(in srgb, var(--tclr) 18%, transparent); overflow: hidden; }
.sg-toast-bar > i { display: block; height: 100%; width: 100%; background: var(--tclr); transform-origin: left center; animation-name: sgToastBar; animation-timing-function: linear; animation-fill-mode: forwards; }
@keyframes sgToastBar { 
  0% { transform: scaleX(1); }
  100% { transform: scaleX(0); }
}
.sg-toast-more { justify-content: center; font-size: 0.8rem; font-weight: 600; color: var(--sk-dk); text-decoration: none; padding: 0.6rem 1rem; }
.sg-toast-more::before { display: none; }
.bi { -webkit-font-smoothing: antialiased; text-rendering: optimizelegibility; }
.btn .bi { font-size: 0.92em; }
h1 > .bi:first-child, h2 > .bi:first-child, h3 > .bi:first-child, h4 > .bi:first-child, h5 > .bi:first-child, h6 > .bi:first-child, .modal-title > .bi:first-child { color: var(--sk-dk); }
html.dark h1 > .bi:first-child, html.dark h2 > .bi:first-child, html.dark h3 > .bi:first-child, html.dark h4 > .bi:first-child, html.dark h5 > .bi:first-child, html.dark h6 > .bi:first-child, html.dark .modal-title > .bi:first-child { color: var(--sk-lt); }
.dash-hero h1 > .bi, .dash-hero h2 > .bi, .dash-hero h3 > .bi, .dash-hero h4 > .bi, .dash-hero h5 > .bi, .dash-hero h6 > .bi, .dc-hero-card h1 > .bi, .dc-hero-card h2 > .bi, .dc-panel-gradient h1 > .bi, .dc-panel-gradient h2 > .bi, .dc-panel-gradient h3 > .bi, .dc-band h1 > .bi, .dc-band h2 > .bi, .dc-band h3 > .bi, .proj-box .pb-title > .bi, .stat-card .bi, .card-header .bi { color: inherit; }
.card-header > .bi:first-child { color: var(--sk-deep); background: var(--sk-wash); border-radius: 8px; padding: 6px; font-size: 0.95em; margin-right: 10px; vertical-align: -0.1em; }
html.dark .card-header > .bi:first-child { color: var(--sk-lt); background: rgba(14, 165, 233, 0.16); }
.table-dark-custom thead th .bi { color: var(--sk-dk); font-size: 0.9em; }
html.dark .table-dark-custom thead th .bi { color: var(--sk-lt); }
.sidebar-link i { width: 1.35em; text-align: center; transition: transform 0.15s; }
.sidebar-link:hover i { transform: scale(1.08); }
.sidebar-link.active:hover i { transform: none; }
.bi.fs-1.text-muted, .text-muted > .bi.fs-1 { color: color-mix(in srgb, var(--text-muted) 85%, transparent) !important; }
.text-brand, a.text-brand { color: var(--sk-ink) !important; }
.text-primary, .link-primary, .text-info { color: var(--sk-ink) !important; }
.text-success { color: var(--green-ink) !important; }
.text-danger { color: var(--red-ink) !important; }
.text-warning { color: var(--gold-ink) !important; }
.btn-link { color: var(--sk-ink); }
code { color: rgb(181, 25, 122); }
html.dark code { color: rgb(247, 143, 187); }
.avatar-initial { background: var(--brand-dark) !important; }
html.dark .avatar-initial { background: var(--sk-deep) !important; }
.badge.bg-primary { background: var(--sk-dk) !important; color: rgb(255, 255, 255) !important; }
.badge.bg-info { background: var(--sk-dk) !important; color: rgb(255, 255, 255) !important; }
.badge.bg-success { background: rgb(17, 121, 58) !important; color: rgb(255, 255, 255) !important; }
.badge.bg-danger { background: var(--red-ink) !important; color: rgb(255, 255, 255) !important; }
.badge.bg-warning, .badge.bg-warning.text-dark, html.dark .badge.bg-warning, html.dark .badge.bg-warning.text-dark { background: var(--gold) !important; color: var(--ink-on-gold) !important; }
.badge.bg-brand { background: var(--sk-dk) !important; color: rgb(255, 255, 255) !important; }
html.dark .badge.bg-danger-subtle { background: rgb(74, 20, 24) !important; }
html.dark .badge.bg-success-subtle { background: rgb(13, 51, 32) !important; }
html.dark .badge.bg-warning-subtle { background: rgb(61, 44, 5) !important; }
html.dark .badge.bg-info-subtle, html.dark .badge.bg-primary-subtle { background: rgb(13, 44, 69) !important; }
.badge.bg-danger-subtle.text-danger { color: #A63A3A !important; }
html.dark .badge.bg-danger-subtle.text-danger { color: var(--red-ink) !important; }
.att-risk.has { background: color-mix(in srgb, var(--red) 16%, transparent); color: #A63A3A; }
.att-risk.none { background: color-mix(in srgb, rgb(17, 121, 58) 14%, transparent); color: rgb(17, 121, 58); }
html.dark .att-risk.has { background: rgba(255, 138, 143, 0.16); color: var(--red-ink); }
html.dark .att-risk.none { background: rgba(95, 212, 139, 0.16); color: var(--green-ink); }
.att-card .ac-code { color: var(--sk-ink); }
.btn-outline-warning { color: var(--gold-ink); }
.btn-outline-success { color: var(--green-ink); }
.btn-outline-danger { color: var(--red); }
html.dark a.btn-brand, html.dark a.btn-primary, html.dark a.btn-success, html.dark a.btn-danger, html.dark a.btn-info, html.dark a.btn-dark { color: rgb(255, 255, 255); }
html.dark a.btn-warning { color: var(--ink-on-gold); }
html.dark a.btn-light { color: var(--sk-lt); }
html.dark .dash-hero a.btn-light, html.dark .fm-hero a.btn-light { color: var(--sk-deep); }
html.dark a.btn-outline-brand { color: var(--sk-ink); }
html.dark a.btn-outline-success { color: var(--green-ink); }
html.dark a.btn-outline-danger { color: var(--red); }
html.dark a.btn-outline-warning { color: var(--gold-ink); }
html.dark a.btn-outline-secondary, html.dark a.btn-outline-dark { color: var(--text-main); }
html.dark a.nf-chip.on, html.dark a.notif-foot:hover { color: rgb(255, 255, 255); }
html.dark a.btn-outline-brand:hover, html.dark a.btn-outline-success:hover, html.dark a.btn-outline-danger:hover, html.dark a.btn-outline-warning:hover, html.dark a.btn-outline-secondary:hover, html.dark a.btn-outline-dark:hover { color: rgb(255, 255, 255); }
html.dark .table { --bs-table-bg: transparent; --bs-table-color: var(--text-main); }
.folder-upload { background: linear-gradient(135deg, var(--sk-dk), var(--sk-deep)); }
.folder-upload .fu-text { background: rgba(0, 0, 0, 0.16); }
.folder-upload:hover .fu-text { background: rgba(0, 0, 0, 0.26); }
@media print {
  body::before, .dc-canvas::before { display: none !important; }
  .app-topbar, .app-sidebar { display: none !important; }
  .card { box-shadow: none; border-radius: var(--r-lg); }
}
.ecal-class { --ec: var(--sk); --ec-ink: var(--sk-ink); --ec-wash: color-mix(in srgb, var(--sk) 12%, #fff); }
.ecal-ses { --ec: var(--green); --ec-ink: var(--green-ink); --ec-wash: color-mix(in srgb, var(--green) 12%, #fff); }
.ecal-asg { --ec: var(--gold); --ec-ink: var(--gold-ink); --ec-wash: color-mix(in srgb, var(--gold) 16%, #fff); }
.ecal-exam { --ec: var(--red); --ec-ink: var(--red-ink); --ec-wash: color-mix(in srgb, var(--red) 14%, #fff); }
.ecal-event { --ec: #8b5cf6; --ec-ink: #6d28d9; --ec-wash: color-mix(in srgb, #8b5cf6 12%, #fff); }
html.dark .ecal-class, html.dark .ecal-ses, html.dark .ecal-asg, html.dark .ecal-exam, html.dark .ecal-event { --ec-wash: color-mix(in srgb, var(--ec) 22%, transparent); }
html.dark .ecal-class { --ec-ink: #bae6fd; }
html.dark .ecal-ses { --ec-ink: #8ff0b6; }
html.dark .ecal-asg { --ec-ink: #ffd671; }
html.dark .ecal-exam { --ec-ink: #ffb4b6; }
html.dark .ecal-event { --ec-ink: #d6c2ff; }
.ecal-dot-class { background: var(--sk); }
.ecal-dot-ses { background: var(--green); }
.ecal-dot-asg { background: var(--gold); }
.ecal-dot-exam { background: rgb(229, 72, 77); }
.ecal-dot-event { background: rgb(139, 92, 246); }
.ecal-filterbar { border-radius: var(--r-lg); }
.ecal-filterbar-label { font-weight: 700; font-size: 0.8rem; color: var(--text-muted); }
.ecal-filter.is-on.ecal-f-class { background: color-mix(in srgb, var(--sk) 12%, #fff); border-color: var(--sk); color: var(--sk-ink); }
.ecal-filter.is-on.ecal-f-ses { background: color-mix(in srgb, var(--green) 12%, #fff); border-color: var(--green); color: var(--green-ink); }
.ecal-filter.is-on.ecal-f-asg { background: color-mix(in srgb, var(--gold) 16%, #fff); border-color: var(--gold); color: var(--gold-ink); }
.ecal-filter.is-on.ecal-f-exam { background: color-mix(in srgb, var(--red) 14%, rgb(255, 255, 255)); border-color: var(--red); color: var(--red-ink); }
.ecal-filter.is-on.ecal-f-event { background: color-mix(in srgb, rgb(139, 92, 246) 12%, rgb(255, 255, 255)); border-color: rgb(139, 92, 246); color: rgb(109, 40, 217); }
html.dark .ecal-filter.is-on.ecal-f-class { background: rgba(14, 165, 233, 0.2); color: rgb(186, 230, 253); }
html.dark .ecal-filter.is-on.ecal-f-ses { background: rgba(22, 163, 74, 0.2); color: rgb(143, 240, 182); }
html.dark .ecal-filter.is-on.ecal-f-asg { background: rgba(242, 176, 30, 0.2); color: rgb(255, 214, 113); }
html.dark .ecal-filter.is-on.ecal-f-exam { background: rgba(229, 72, 77, 0.2); color: rgb(255, 180, 182); }
html.dark .ecal-filter.is-on.ecal-f-event { background: rgba(139, 92, 246, 0.22); color: rgb(214, 194, 255); }
.ecal-pill.ecal-exam { background: var(--ec-wash); color: var(--ec-ink); }
.ecal-pill.ecal-event { background: var(--ec-wash); color: var(--ec-ink); }
.ecal-ag-item.ecal-exam .ecal-ag-bar { background: rgb(229, 72, 77); }
.ecal-ag-item.ecal-event .ecal-ag-bar { background: rgb(139, 92, 246); }
.ecal-viewsw { display: inline-flex; gap: 2px; padding: 3px; background: var(--bg-soft); border: 1px solid var(--border-color); border-radius: var(--r-pill); }
.ecal-viewsw a { padding: 5px 16px; border-radius: var(--r-pill); font-size: 0.84rem; font-weight: 700; color: var(--text-muted); text-decoration: none; line-height: 1.4; }
.ecal-viewsw a:hover { color: var(--sk-ink); background: color-mix(in srgb, var(--sk) 10%, transparent); }
.ecal-viewsw a.on { background: linear-gradient(135deg, var(--sk-dk), var(--sk-deep)); color: rgb(255, 255, 255); }
html.dark .ecal-viewsw { background: rgba(255, 255, 255, 0.06); }
html.dark .ecal-viewsw a { color: var(--text-muted); }
html.dark .ecal-viewsw a.on { color: rgb(255, 255, 255); }
.ecal-weekcard { overflow-x: auto; }
.ecal-weekgrid { display: grid; grid-template-columns: repeat(7, minmax(0px, 1fr)); }
.ecal-wcol { border-right: 1px solid var(--border-color); min-height: 340px; display: flex; flex-direction: column; }
.ecal-wcol:last-child { border-right: 0px; }
.ecal-wcol.is-today { background: color-mix(in srgb, var(--sk) 6%, transparent); }
.ecal-whead { display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 10px 4px; border-bottom: 1px solid var(--border-color); text-decoration: none; color: var(--text-main); }
.ecal-whead:hover { background: color-mix(in srgb, var(--sk) 8%, transparent); color: var(--text-main); }
.ecal-wdow { font-size: 0.7rem; font-weight: 800; letter-spacing: 0.04em; color: var(--text-muted); }
.ecal-wnum { font-family: var(--font-display); font-weight: 800; font-size: 1.25rem; line-height: 1.1; width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; }
.ecal-wnum.is-today { background: linear-gradient(135deg, var(--sk-dk), var(--sk-deep)); color: rgb(255, 255, 255); box-shadow: 0 3px 10px color-mix(in srgb, var(--sk) 45%, transparent); }
.ecal-wmon { font-size: 0.66rem; color: var(--text-muted); font-weight: 700; }
.ecal-wevs { display: flex; flex-direction: column; gap: 6px; padding: 8px 6px; }
.ecal-wev { display: block; text-decoration: none; padding: 6px 8px; border-radius: var(--r-xs); border-left: 3px solid var(--ec); background: var(--ec-wash); color: var(--ec-ink); }
.ecal-wev:hover { color: var(--ec-ink); filter: brightness(0.97); transform: translateX(1px); }
.ecal-wev-time { display: block; font-size: 0.66rem; font-weight: 800; opacity: 0.9; }
.ecal-wev-title { display: block; font-size: 0.74rem; font-weight: 600; color: var(--text-main); line-height: 1.35; }
html.dark .ecal-wev-title { color: var(--text-main); }
.ecal-wempty { text-align: center; color: var(--text-muted); font-size: 0.8rem; padding: 6px 0px; }
.ecal-day-row { display: flex; align-items: stretch; gap: 10px; padding: 12px; border-radius: var(--r-md); background: var(--bg-soft); margin-bottom: 8px; border-left: 4px solid var(--ec); }
html.dark .ecal-day-row { background: rgba(255, 255, 255, 0.05); }
.ecal-day-row.is-hl { box-shadow: 0 0 0 2px var(--ec); }
.ecal-day-time { flex: 0 0 74px; font-family: var(--font-display); font-weight: 800; font-size: 0.84rem; color: var(--ec-ink); padding-top: 2px; }
.ecal-day-bar { width: 3px; border-radius: 3px; background: var(--ec); flex-shrink: 0; opacity: 0.5; }
.ecal-day-title { display: block; font-weight: 700; font-size: 0.96rem; color: var(--text-main); text-decoration: none; }
.ecal-day-title:hover { color: var(--ec-ink); text-decoration: underline; }
.ecal-day-desc { font-size: 0.82rem; color: var(--text-muted); margin-top: 4px; }
.ecal-count-badge { min-width: 28px; height: 28px; padding: 0px 9px; border-radius: var(--r-pill); background: linear-gradient(135deg, var(--sk-dk), var(--sk-deep)); color: rgb(255, 255, 255); font-family: var(--font-display); font-weight: 800; font-size: 0.82rem; display: inline-flex; align-items: center; justify-content: center; }
.ecal-upcoming { display: flex; flex-direction: column; gap: 8px; padding: 10px; }
.ecal-up-item { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: var(--r-md); text-decoration: none; background: var(--ec-wash); border-left: 4px solid var(--ec); }
.ecal-up-item:hover { filter: brightness(0.97); transform: translateX(2px); }
.ecal-up-ico { flex: 0 0 34px; width: 34px; height: 34px; border-radius: var(--r-sm); background: var(--ec); color: rgb(255, 255, 255); display: inline-flex; align-items: center; justify-content: center; font-size: 1rem; }
.ecal-up-title { font-weight: 700; font-size: 0.86rem; color: var(--text-main); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ecal-up-meta { font-size: 0.72rem; color: var(--text-muted); }
.ecal-up-code { display: inline-block; padding: 0px 6px; margin-right: 4px; border-radius: var(--r-xs); background: var(--ec); color: rgb(255, 255, 255); font-weight: 800; font-size: 0.66rem; }
.ecal-up-chip { display: inline-block; padding: 2px 9px; border-radius: var(--r-pill); font-size: 0.68rem; font-weight: 800; white-space: nowrap; background: var(--bg-card); color: var(--text-muted); border: 1px solid var(--border-color); }
.ecal-up-chip.is-ok { background: rgb(17, 121, 58); color: rgb(255, 255, 255); border-color: transparent; }
.ecal-up-chip.is-now { background: var(--red-ink); color: rgb(255, 255, 255); border-color: transparent; }
.ecal-up-chip.is-soon { background: var(--gold); color: var(--ink-on-gold); border-color: transparent; }
html.dark .ecal-up-chip { background: rgba(255, 255, 255, 0.1); border-color: transparent; }
html.dark .ecal-day-row { border-left: 4px solid var(--ec); }
html.dark .ecal-up-item { border-left: 4px solid var(--ec); }
html.dark .ecal-wev { border-left: 3px solid var(--ec); }
.ecal-typepick { display: flex; gap: 6px; flex-wrap: wrap; }
@media (max-width: 575px) {
  .ecal-viewsw a { padding: 5px 11px; font-size: 0.78rem; }
  .ecal-day-time { flex-basis: 58px; font-size: 0.76rem; }
  .ecal-weekgrid { grid-template-columns: repeat(7, minmax(118px, 1fr)); min-width: 100%; }
  .ecal-wcol { min-height: 240px; }
  .ecal-wdow { font-size: 0.62rem; }
}
:root { --rd-line-soft: #eaf3fa; --rd-wash-red: #FFEBEB; --rd-wash-gold: #fef8ea; --rd-wash-green: #e4f7ea; --rd-h-bar: 0px; --rd-h-subnav: 0px; --rd-gut-x: 26px; --rd-gut-y: 22px; }
html.dark { --rd-line-soft: #1b3150; --rd-wash-red: #3D1E1E; --rd-wash-gold: #3d2f10; --rd-wash-green: #123122; }
.rd-num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
.rd-today { margin-top: 20px; padding-top: 17px; border-top: 1px solid rgba(255, 255, 255, 0.22); }
.rd-today-hd { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.11em; text-transform: uppercase; opacity: 0.88; margin-bottom: 10px; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.rd-today-note { text-transform: none; letter-spacing: 0px; font-weight: 600; font-size: 0.74rem; background: rgba(0, 0, 0, 0.24); border: 1px solid rgba(255, 255, 255, 0.18); padding: 2px 9px; border-radius: var(--r-pill); }
.rd-today-rail { display: flex; gap: 9px; overflow-x: auto; padding-bottom: 4px; scrollbar-width: thin; }
.rd-per { flex: 0 0 auto; min-width: 132px; border-radius: var(--r-md); padding: 9px 12px; background: rgba(0, 0, 0, 0.22); border: 1px solid rgba(255, 255, 255, 0.18); color: rgb(255, 255, 255); text-decoration: none; transition: background 0.16s; }
.rd-per:hover { background: rgba(0, 0, 0, 0.34); color: rgb(255, 255, 255); }
.rd-per.is-now { background: rgb(255, 255, 255); color: var(--sk-deep); border-color: rgb(255, 255, 255); box-shadow: rgba(0, 0, 0, 0.16) 0px 4px 14px; }
.rd-per.is-now:hover { background: rgb(244, 251, 255); color: var(--sk-deep); }
.rd-per.is-free { opacity: 0.5; }
.rd-per .p-t { font-size: 0.66rem; font-weight: 700; letter-spacing: 0.06em; opacity: 0.85; }
.rd-per .p-n { font-family: var(--font-display); font-weight: 700; font-size: 0.94rem; margin: 1px 0px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rd-per .p-r { font-size: 0.72rem; opacity: 0.9; }
.rd-per.is-now .p-t, .rd-per.is-now .p-r { opacity: 1; color: var(--sk-dk); }
.rd-nowdot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--green); margin-right: 5px; box-shadow: rgba(22, 163, 74, 0.25) 0px 0px 0px 3px; }
.rd-stats { display: grid; grid-template-columns: repeat(4, minmax(0px, 1fr)); gap: 14px; margin-bottom: 24px; }
.rd-stat { position: relative; overflow: hidden; border-radius: var(--r-lg); padding: 17px 18px; background: var(--bg-card); border: 1px solid var(--border-color); box-shadow: var(--shadow); transition: transform 0.18s, box-shadow 0.18s, border-color 0.18s; display: block; color: var(--text-main); text-decoration: none; }
a.rd-stat:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); border-color: var(--sk-lt); color: var(--text-main); }
/* แถบสีขอบซ้าย 4px ถูกถอดออก (15 ส.ค. 2569) — สีตระกูลย้ายไปอยู่ที่ไอคอนกับ
   ขีดใต้ตัวเลขในบล็อก FIGURE RAIL ท้ายไฟล์ · เคยถูกปิดด้วย display:none
   ทับไว้เฉย ๆ ซึ่งทำให้ยังเป็นโค้ดตายที่อ่านแล้วเข้าใจผิดว่ายังใช้อยู่        */
.rd-stat-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 9px; }
.rd-stat-ic { width: 34px; height: 34px; border-radius: var(--r-sm); display: grid; place-items: center; background: var(--sk-wash); color: var(--sk-deep); font-size: 1.02rem; }
.rd-stat.s-gold .rd-stat-ic { background: var(--rd-wash-gold); color: var(--gold-ink); }
.rd-stat.s-red .rd-stat-ic { background: var(--rd-wash-red); color: var(--red-ink); }
.rd-stat.s-green .rd-stat-ic { background: var(--rd-wash-green); color: var(--green-ink); }
html.dark .rd-stat-ic { background: rgb(18, 57, 90); color: var(--sk-lt); }
.rd-stat-val { font-family: var(--font-display); font-size: 1.95rem; font-weight: 700; line-height: 1.08; font-variant-numeric: tabular-nums; }
.rd-stat-val small { font-size: 0.9rem; font-weight: 600; color: var(--text-muted); margin-left: 2px; }
/* ขยายจาก .82/.72rem ตามที่เจ้าของขอ — ตัวโตขึ้นอ่านง่ายขึ้นจริง แต่ **ไม่ได้
   ลดเกณฑ์ AA** เกณฑ์จะลดจาก 4.5 เหลือ 3.0 ต่อเมื่อ >= 18.66px ตัวหนา หรือ
   24px ตัวธรรมดา ซึ่ง .88rem (~14px) ยังห่างมาก คอนทราสต์จึงต้องผ่าน 4.5 อยู่ดี */
.rd-stat-label { font-size: 0.88rem; color: var(--text-muted); font-weight: 600; margin-top: 3px; }
.rd-stat-note { font-size: 0.78rem; font-weight: 700; margin-top: 8px; display: flex; align-items: center; gap: 5px; }
.rd-n-gold { color: var(--gold-ink); }
.rd-n-red { color: var(--red-ink); }
.rd-n-green { color: var(--green-ink); }
.rd-n-mute { color: var(--text-muted); font-weight: 600; }
.rd-panel { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--r-lg); box-shadow: var(--shadow); margin-bottom: 22px; overflow: hidden; }
.rd-panel-hd { padding: 14px 18px; border-bottom: 1px solid var(--border-color); display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.rd-panel-hd .t { font-family: var(--font-display); font-weight: 700; font-size: 0.98rem; display: flex; align-items: center; gap: 9px; }
.rd-panel-hd .t i { color: var(--sk-dk); }
html.dark .rd-panel-hd .t i { color: var(--sk-lt); }
.rd-panel-hd .hint { font-size: 0.78rem; color: var(--text-muted); font-weight: 600; }
.rd-cnt { background: var(--sk-wash); color: var(--sk-deep); font-size: 0.74rem; font-weight: 700; padding: 2px 9px; border-radius: var(--r-pill); }
.rd-cnt.urgent { background: var(--rd-wash-red); color: var(--red-ink); }
html.dark .rd-cnt { background: rgb(18, 57, 90); color: var(--sk-lt); }
.rd-row { display: flex; align-items: center; gap: 14px; padding: 13px 18px; border-bottom: 1px solid var(--border-color); position: relative; color: var(--text-main); text-decoration: none; transition: background 0.14s; }
.rd-row:last-of-type { border-bottom: 0px; }
.rd-row:hover { background: var(--sk-wash); color: var(--text-main); }
html.dark .rd-row:hover { background: rgb(22, 48, 78); }
.rd-row::before { content: ""; position: absolute; left: 0px; top: 0px; bottom: 0px; width: 3px; background: transparent; transition: background 0.14s; }
.rd-row:hover::before { background: var(--sk); }
.rd-when { flex: 0 0 auto; width: 76px; text-align: center; border-radius: var(--r-sm); padding: 6px 4px; background: var(--bg-soft); border: 1px solid var(--border-color); }
.rd-when .d { font-family: var(--font-display); font-weight: 700; font-size: 0.94rem; line-height: 1.15; }
.rd-when .m { font-size: 0.68rem; color: var(--text-muted); font-weight: 600; }
.rd-when.late { background: var(--rd-wash-red); border-color: #FFC9C9; }
.rd-when.late .d, .rd-when.late .m { color: var(--red-ink); }
.rd-when.soon { background: var(--rd-wash-gold); border-color: rgb(240, 220, 174); }
.rd-when.soon .d, .rd-when.soon .m { color: var(--gold-ink); }
html.dark .rd-when.late { border-color: rgb(92, 43, 43); }
html.dark .rd-when.soon { border-color: rgb(92, 72, 26); }
.rd-row-b { flex: 1 1 0%; min-width: 0px; }
.rd-row-t { font-weight: 700; font-size: 0.92rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rd-row-s { font-size: 0.79rem; color: var(--text-muted); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rd-row-r { flex: 0 0 auto; display: flex; align-items: center; gap: 9px; }
.rd-panel-ft { display: block; padding: 11px 18px; background: var(--bg-soft); font-size: 0.83rem; font-weight: 700; color: var(--sk-dk); text-decoration: none; }
.rd-panel-ft:hover { background: var(--sk-wash); color: var(--sk-deep); }
html.dark .rd-panel-ft { color: var(--sk-lt); }
html.dark .rd-panel-ft:hover { background: rgb(22, 48, 78); color: var(--sk-lt); }
.rd-chev { color: var(--text-muted); font-size: 0.9rem; }
.rd-tag { font-size: 0.72rem; font-weight: 700; padding: 3px 10px; border-radius: var(--r-pill); white-space: nowrap; }
.rd-t-blue { background: var(--sk-wash); color: var(--sk-deep); }
.rd-t-gold { background: var(--rd-wash-gold); color: var(--gold-ink); }
.rd-t-red { background: var(--rd-wash-red); color: var(--red-ink); }
.rd-t-green { background: var(--rd-wash-green); color: var(--green-ink); }
html.dark .rd-t-blue { background: #12395A; color: var(--sk-lt); }
.rd-chead { background: var(--bg-card); border-bottom: 1px solid var(--border-color); margin: calc(var(--rd-gut-y) * -1) calc(var(--rd-gut-x) * -1) 0; padding: var(--rd-gut-y) var(--rd-gut-x) 0; }
.rd-crumb { font-size: 0.79rem; color: var(--text-muted); margin-bottom: 12px; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.rd-crumb a { color: var(--text-muted); }
.rd-crumb a:hover { color: var(--sk-dk); text-decoration: underline; }
html.dark .rd-crumb a:hover { color: var(--sk-lt); }
.rd-ctop { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
.rd-ceyebrow { display: flex; align-items: center; gap: 9px; margin-bottom: 5px; flex-wrap: wrap; }
.rd-ccode { font-family: var(--font-display); font-weight: 700; font-size: 0.79rem; letter-spacing: 0.06em; color: var(--sk-deep); background: var(--sk-wash); padding: 3px 9px; border-radius: var(--r-xs); }
/* พลิกแต่หมึกไม่ได้ — พื้นยังเป็น --sk-wash (สว่าง) อยู่ ได้ 1.90 */
html.dark .rd-ccode { background: #12395A; color: var(--sk-lt); }
.rd-croom { font-size: 0.79rem; font-weight: 600; color: var(--text-muted); }
.rd-chead h1 { font-family: var(--font-display); font-size: 1.72rem; font-weight: 700; letter-spacing: -0.018em; line-height: 1.2; margin-bottom: 7px; }
.rd-cmeta { font-size: 0.85rem; color: var(--text-muted); display: flex; flex-wrap: wrap; gap: 5px 14px; }
.rd-cmeta b { color: var(--text-main); font-weight: 600; }
.rd-cact { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.rd-subnav { position: sticky; top: var(--rd-h-bar); z-index: 20; background: var(--bg-card); border-bottom: 1px solid var(--border-color); overflow-x: auto; scrollbar-width: none; margin: 0 calc(var(--rd-gut-x) * -1); padding: 0 var(--rd-gut-x); }
.rd-subnav::-webkit-scrollbar { display: none; }
.rd-subnav-in { display: flex; gap: 2px; }
.rd-tab { position: relative; padding: 13px 15px 12px; font-size: 0.88rem; font-weight: 700; color: var(--text-muted); white-space: nowrap; border-bottom: 2px solid transparent; margin-bottom: -1px; display: flex; align-items: center; gap: 7px; text-decoration: none; }
.rd-tab:hover { color: var(--text-main); }
.rd-tab.on { color: var(--sk-dk); border-bottom-color: var(--sk-dk); }
html.dark .rd-tab.on { color: var(--sk-lt); border-bottom-color: var(--sk-lt); }
.rd-pip { font-size: 0.7rem; font-weight: 700; background: var(--bg-soft); color: var(--text-muted); padding: 1px 7px; border-radius: var(--r-pill); min-width: 20px; text-align: center; }
.rd-tab.on .rd-pip { background: var(--sk-wash); color: var(--sk-deep); }
html.dark .rd-tab.on .rd-pip { color: var(--sk-lt); }
.rd-pip.alert { background: var(--rd-wash-red); color: var(--red-ink); }
.rd-cols { display: grid; grid-template-columns: minmax(0px, 1.85fr) minmax(0px, 1fr); gap: 28px; padding-top: 26px; }
.rd-rail { position: sticky; top: calc(var(--rd-h-bar) + var(--rd-h-subnav) + 18px); align-self: start; display: flex; flex-direction: column; gap: 16px; }
.rd-rail .rd-panel { margin-bottom: 0px; }
.rd-task { display: flex; align-items: center; gap: 14px; padding: 14px 18px; border-bottom: 1px solid var(--rd-line-soft); color: var(--text-main); text-decoration: none; }
.rd-task:last-of-type { border-bottom: 0px; }
.rd-task:hover { background: var(--bg-soft); color: var(--text-main); }
.rd-tk-ic { flex: 0 0 auto; width: 34px; height: 34px; border-radius: var(--r-sm); display: grid; place-items: center; font-size: 1rem; }
.rd-i-red { background: var(--rd-wash-red); color: var(--red-ink); }
.rd-i-gold { background: var(--rd-wash-gold); color: var(--gold-ink); }
.rd-i-blue { background: var(--sk-wash); color: var(--sk-deep); }
.rd-i-green { background: var(--rd-wash-green); color: var(--green-ink); }
html.dark .rd-i-blue { color: var(--sk-lt); }
.rd-tk-b { flex: 1 1 0%; min-width: 0px; }
.rd-tk-t { font-weight: 700; font-size: 0.9rem; }
.rd-tk-s { font-size: 0.79rem; color: var(--text-muted); margin-top: 1px; }
.rd-seglist { display: flex; flex-direction: column; gap: 13px; }
.rd-segrow { display: grid; grid-template-columns: 1fr auto; gap: 4px 10px; align-items: baseline; }
.rd-segrow .lb { font-size: 0.84rem; font-weight: 600; }
.rd-segrow .vl { font-size: 0.81rem; font-weight: 700; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.rd-segbar { grid-column: 1 / -1; height: 7px; border-radius: var(--r-pill); background: var(--bg-soft); overflow: hidden; display: flex; }
.rd-segbar i { display: block; height: 100%; }
.rd-f-done { background: var(--sk-dk); }
html.dark .rd-f-done { background: var(--sk); }
.rd-strip { display: flex; flex-wrap: wrap; gap: 4px; }
.rd-cell { width: 26px; height: 26px; border-radius: var(--r-xs); display: grid; place-items: center; font-size: 0.68rem; font-weight: 700; font-variant-numeric: tabular-nums; border: 1px solid transparent; text-decoration: none; }
.rd-cell.p { background: var(--rd-wash-green); color: var(--green-ink); border-color: rgb(195, 234, 208); }
.rd-cell.l { background: var(--rd-wash-gold); color: var(--gold-ink); border-color: rgb(240, 220, 174); }
.rd-cell.a { background: var(--rd-wash-red); color: var(--red-ink); border-color: #FFC9C9; }
.rd-cell.v { background: var(--sk-wash); color: var(--sk-deep); border-color: rgb(191, 228, 247); }
.rd-cell.n { background: var(--bg-soft); color: var(--text-muted); border-color: var(--border-color); }
html.dark .rd-cell.p { border-color: rgb(29, 77, 51); }
html.dark .rd-cell.l { border-color: rgb(92, 72, 26); }
html.dark .rd-cell.a { border-color: rgb(92, 43, 43); }
html.dark .rd-cell.v { background: rgb(18, 57, 90); color: var(--sk-lt); border-color: rgb(29, 79, 116); }
.rd-striplegend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 0.76rem; color: var(--text-muted); margin-top: 12px; }
.rd-striplegend span { display: inline-flex; align-items: center; gap: 5px; }
.rd-dot { width: 9px; height: 9px; border-radius: 3px; display: inline-block; }
.rd-tscroll { overflow-x: auto; }
.rd-stable { width: 100%; min-width: 420px; border-collapse: collapse; font-size: 0.86rem; margin: 0px; }
.rd-stable th { text-align: left; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-muted); padding: 9px 18px; border-bottom: 1px solid var(--border-color); background: var(--bg-soft); }
.rd-stable td { padding: 9px 18px; border-bottom: 1px solid var(--rd-line-soft); }
.rd-stable tbody tr:hover { background: var(--bg-soft); }
.rd-stable .r { text-align: right; font-variant-numeric: tabular-nums; }
.rd-catrow td { background: var(--bg-soft); font-weight: 700; font-size: 0.79rem; color: var(--text-muted); padding: 6px 18px; letter-spacing: 0.03em; }
.rd-sumrow td { font-weight: 700; border-top: 2px solid var(--border-color); border-bottom: 0px; background: transparent; }
.rd-miss { color: var(--text-muted); font-weight: 600; }
.rd-gradebox { text-align: center; padding: 22px 18px 18px; }
.rd-gradebox .g { font-family: var(--font-display); font-size: 3.6rem; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; letter-spacing: -0.03em; }
.rd-gradebox .gsub { font-size: 0.83rem; color: var(--text-muted); margin-top: 5px; font-variant-numeric: tabular-nums; }
.rd-nextup { margin: 0px 18px 18px; padding: 11px 13px; border-radius: var(--r-sm); background: var(--sk-wash); font-size: 0.81rem; line-height: 1.5; }
.rd-nextup b { font-variant-numeric: tabular-nums; }
html.dark .rd-nextup { background: rgb(18, 57, 90); }
.rd-metric { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 10px 18px; border-bottom: 1px solid var(--rd-line-soft); }
.rd-metric:last-child { border-bottom: 0px; }
.rd-metric .k { font-size: 0.83rem; color: var(--text-muted); }
.rd-metric .v { font-family: var(--font-display); font-weight: 700; font-size: 1.02rem; font-variant-numeric: tabular-nums; }
.rd-metric .v.warn { color: var(--red-ink); }
.rd-dist { display: flex; gap: 3px; align-items: flex-end; height: 56px; padding: 0px 18px 4px; }
.rd-dist div { flex: 1 1 0%; border-radius: 3px 3px 0px 0px; background: var(--sk); min-height: 3px; }
.rd-dist div.lo { background: var(--red); }
.rd-dist div.hi { background: var(--green); }
.rd-distx { display: flex; gap: 3px; padding: 5px 18px 14px; font-size: 0.65rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.rd-distx span { flex: 1 1 0%; text-align: center; }
.rd-person { display: flex; align-items: center; gap: 11px; padding: 14px 18px; }
.rd-av { width: 42px; height: 42px; border-radius: 50%; background: var(--sk-dk); color: rgb(255, 255, 255); display: grid; place-items: center; font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; flex: 0 0 auto; }
.rd-tile-pip { position: absolute; top: 10px; right: 10px; min-width: 22px; height: 22px; padding: 0px 7px; border-radius: var(--r-pill); background: var(--bg-soft); color: var(--text-muted); font-family: var(--font-display); font-weight: 800; font-size: 0.72rem; display: inline-flex; align-items: center; justify-content: center; font-variant-numeric: tabular-nums; }
.rd-tile-pip.alert { background: var(--red); color: rgb(255, 255, 255); }
html.dark .rd-tile-pip { background: rgba(255, 255, 255, 0.1); color: var(--text-main); }
html.dark .rd-tile-pip.alert { background: var(--red); color: rgb(255, 255, 255); }
.rd-tiles { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(126px, 1fr)); }
.rd-tile { position: relative; display: flex; flex-direction: column; align-items: center; gap: 9px; padding: 18px 10px; border-radius: var(--r-lg,16px); text-decoration: none; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main); transition: transform 0.15s, box-shadow 0.15s, border-color 0.15s; }
.rd-tile:hover { transform: translateY(-3px); border-color: var(--brand); box-shadow: rgba(0, 0, 0, 0.1) 0px 10px 24px; color: var(--text-main); }
.rd-tile-ic { width: 52px; height: 52px; border-radius: 50%; display: grid; place-items: center; color: rgb(255, 255, 255); font-size: 1.35rem; flex: 0 0 auto; }
.rd-tile-lb { font-weight: 600; font-size: 0.9rem; text-align: center; line-height: 1.35; overflow-wrap: anywhere; }
html.dark .rd-tile { background: rgba(255, 255, 255, 0.03); }
html.dark a.rd-tile, html.dark a.rd-tile:hover { color: var(--text-main); }
@media (max-width: 575px) {
  .rd-tiles { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 10px; }
  .rd-tile { padding: 14px 8px; }
  .rd-tile-ic { width: 44px; height: 44px; font-size: 1.15rem; }
  .rd-tile-lb { font-size: 0.82rem; }
}
.rd-tools { display: flex; flex-wrap: wrap; gap: 7px; }
.rd-tool { display: inline-flex; align-items: center; gap: 7px; font-size: 0.83rem; font-weight: 600; padding: 7px 12px; border: 1px solid var(--border-color); border-radius: var(--r-sm); color: var(--text-main); background: var(--bg-card); text-decoration: none; }
.rd-tool:hover { border-color: var(--sk); color: var(--sk-dk); background: var(--sk-wash); }
html.dark .rd-tool:hover { color: var(--sk-lt); }
.rd-tool i { color: var(--text-muted); font-size: 0.95rem; }
.rd-tool:hover i { color: inherit; }
.rd-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(285px, 1fr)); gap: 16px; }
.rd-course { position: relative; border-radius: var(--r-lg); padding: 18px; color: rgb(255, 255, 255); overflow: hidden; box-shadow: var(--shadow); transition: transform 0.18s, box-shadow 0.18s; display: block; min-height: 186px; text-decoration: none; }
.rd-course:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); color: rgb(255, 255, 255); }
.rd-course:has(.proj-actions):hover { transform: none; }
.rd-course::after { content: ""; position: absolute; right: -46px; bottom: -56px; width: 150px; height: 150px; border-radius: 50%; background: rgba(255, 255, 255, 0.1); pointer-events: none; }
.rd-course > * { position: relative; z-index: 1; }
.rd-c1 { background: linear-gradient(135deg, rgb(4, 114, 174), rgb(3, 105, 161)); }
.rd-c2 { background: linear-gradient(135deg, rgb(3, 105, 161), rgb(7, 89, 133)); }
.rd-c3 { background: linear-gradient(135deg, rgb(12, 74, 110), rgb(21, 94, 133)); }
.rd-c4 { background: linear-gradient(135deg, rgb(4, 93, 144), rgb(4, 114, 174)); }
.rd-c5 { background: linear-gradient(135deg, rgb(125, 211, 252), rgb(56, 189, 248)); --rd-cink: #02405f; }
.rd-c6 { background: linear-gradient(135deg, rgb(242, 176, 30), rgb(217, 147, 13)); --rd-cink: var(--ink-on-gold); }
.rd-c5, .rd-c6 { color: var(--rd-cink); }
.rd-c5:hover, .rd-c6:hover { color: var(--rd-cink); }
.rd-c5 .rd-ccode2, .rd-c6 .rd-ccode2, .rd-c5 .rd-cchip, .rd-c6 .rd-cchip { background: rgba(255, 255, 255, 0.72); border-color: rgba(255, 255, 255, 0.9); color: var(--rd-cink); }
.rd-c5 .rd-cbar, .rd-c6 .rd-cbar { background: rgba(0, 0, 0, 0.16); }
.rd-c5 .rd-cbar span, .rd-c6 .rd-cbar span { background: var(--rd-cink); }
.rd-c5::after, .rd-c6::after { background: rgba(0, 0, 0, 0.07); }
.rd-ctop2 { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; margin-bottom: 9px; }
.rd-ccode2 { font-size: 0.71rem; font-weight: 700; letter-spacing: 0.07em; background: rgba(0, 0, 0, 0.22); border: 1px solid rgba(255, 255, 255, 0.24); padding: 3px 9px; border-radius: var(--r-pill); }
.rd-cgrade { font-family: var(--font-display); font-size: 1.75rem; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.rd-ctitle { font-family: var(--font-display); font-weight: 700; font-size: 1.04rem; line-height: 1.3; margin-bottom: 3px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.rd-csub { font-size: 0.76rem; opacity: 0.88; margin-bottom: 13px; }
.rd-cbarlabel { display: flex; justify-content: space-between; font-size: 0.73rem; font-weight: 700; opacity: 0.95; margin-bottom: 5px; font-variant-numeric: tabular-nums; }
.rd-cbar { height: 6px; border-radius: var(--r-pill); background: rgba(255, 255, 255, 0.26); overflow: hidden; }
.rd-cbar span { display: block; height: 100%; border-radius: var(--r-pill); background: rgb(255, 255, 255); }
.rd-cfoot { display: flex; gap: 7px; flex-wrap: wrap; margin-top: 13px; }
.rd-cchip { font-size: 0.72rem; font-weight: 700; background: rgba(0, 0, 0, 0.22); border: 1px solid rgba(255, 255, 255, 0.24); padding: 3px 10px; border-radius: var(--r-pill); }
.rd-cchip.warn { background: var(--gold); color: var(--ink-on-gold); border-color: var(--gold); }
.rd-cchip.bad { background: rgb(255, 255, 255); color: var(--red-ink); border-color: rgb(255, 255, 255); }
.rd-sechd { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: 0px 0px 14px; }
.rd-sechd h5 { font-family: var(--font-display); font-size: 1.06rem; font-weight: 700; display: flex; align-items: center; gap: 9px; margin: 0px; }
.rd-sechd h5 i { color: var(--sk-dk); }
html.dark .rd-sechd h5 i { color: var(--sk-lt); }
.rd-plink { font-size: 0.81rem; font-weight: 700; color: var(--sk-dk); display: inline-flex; align-items: center; gap: 5px; text-decoration: none; }
html.dark .rd-plink { color: var(--sk-lt); }
.rd-plink:hover { text-decoration: underline; }
.rd-pbody { padding: 16px 18px; }
.rd-pbody.flush { padding: 0px; }
.rd-empty { text-align: center; padding: 34px 18px; color: var(--text-muted); }
.rd-empty i { font-size: 1.9rem; display: block; margin-bottom: 9px; opacity: 0.85; }
.rd-empty p { font-size: 0.87rem; margin-bottom: 0px; }
@media (max-width: 980px) {
  .rd-cols { grid-template-columns: minmax(0px, 1fr); gap: 0px; }
  .rd-rail { position: static; margin-bottom: 22px; }
}
@media (max-width: 900px) {
  .rd-stats { grid-template-columns: repeat(2, minmax(0px, 1fr)); }
}
@media (max-width: 820px) {
  :root { --rd-gut-x: 14px; --rd-gut-y: 16px; }
}
@media (max-width: 575px) {
  :root { --rd-gut-x: 12px; --rd-gut-y: 14px; }
  .rd-stats { grid-template-columns: repeat(2, minmax(0px, 1fr)); gap: 10px; }
  .rd-stat { padding: 13px 14px; }
  .rd-stat-val { font-size: 1.5rem; }
  .rd-chead h1 { font-size: 1.34rem; }
  .rd-when { width: 64px; }
  .rd-stable th, .rd-stable td { padding: 8px 12px; }
  .rd-today-rail { gap: 7px; }
  .rd-per { min-width: 120px; }
}
.rd-gradehero { position: relative; overflow: hidden; border: 0px; color: rgb(255, 255, 255); background: radial-gradient(80% 150% at 92% 0%, rgba(255,255,255,.14) 0%, transparent 55%),
    linear-gradient(150deg, var(--sk-deep) 0%, #045d90 55%, var(--sk-dk) 100%); box-shadow: 0 10px 30px color-mix(in srgb, var(--sk-deep) 32%, transparent); padding: 20px; }
.rd-gradehero::after { content: ""; position: absolute; right: -60px; bottom: -70px; width: 180px; height: 180px; border-radius: 50%; background: rgba(255, 255, 255, 0.07); pointer-events: none; }
.rd-gradehero > * { position: relative; z-index: 1; }
.rd-gh-top { display: flex; align-items: center; gap: 16px; }
.rd-gh-ring { position: relative; width: 112px; height: 112px; flex: 0 0 auto; }
.rd-gh-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.rd-gh-ring circle { fill: none; stroke-width: 9; stroke-linecap: round; }
.rd-gh-ring .trk { stroke: rgba(0, 0, 0, 0.22); }
.rd-gh-ring .val { stroke: rgb(255, 255, 255); transition: stroke-dashoffset 0.6s; }
@media (prefers-reduced-motion: reduce) {
  .rd-gh-ring .val { transition: none; }
}
.rd-gh-num { position: absolute; inset: 0px; display: grid; place-items: center; font-family: var(--font-display); font-weight: 800; font-size: 2.5rem; line-height: 1; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
.rd-gh-num.is-long { font-size: 1.7rem; }
.rd-gh-side { min-width: 0px; }
.rd-gh-eyebrow { font-size: 0.68rem; font-weight: 800; letter-spacing: 0.13em; text-transform: uppercase; opacity: 0.92; }
.rd-gh-pct { font-family: var(--font-display); font-weight: 800; font-size: 1.85rem; line-height: 1.15; margin: 1px 0px; }
.rd-gh-pct small { font-size: 0.95rem; font-weight: 700; opacity: 0.8; margin-left: 1px; }
.rd-gh-sub { font-size: 0.8rem; opacity: 0.92; }
.rd-gh-link { display: inline-flex; align-items: center; gap: 5px; margin-top: 8px; font-size: 0.78rem; font-weight: 700; color: rgb(255, 255, 255); text-decoration: none; background: rgba(0, 0, 0, 0.24); border: 1px solid rgba(255, 255, 255, 0.24); padding: 4px 11px; border-radius: var(--r-pill); }
.rd-gh-link:hover { background: rgba(0, 0, 0, 0.36); color: rgb(255, 255, 255); }
.rd-gh-next { display: flex; gap: 9px; margin-top: 16px; padding: 11px 13px; border-radius: var(--r-sm); background: rgba(0, 0, 0, 0.24); border: 1px solid rgba(255, 255, 255, 0.18); font-size: 0.83rem; line-height: 1.5; }
.rd-gh-next i { flex: 0 0 auto; margin-top: 2px; font-size: 1rem; }
.rd-gh-next-sub { display: block; opacity: 0.8; font-size: 0.76rem; margin-top: 1px; }
.rd-gh-next.is-lock i { color: var(--gold); }
.rd-gh-prog { margin-top: 16px; padding: 11px 13px; border-radius: var(--r-sm); background: rgba(0, 0, 0, 0.24); border: 1px solid rgba(255, 255, 255, 0.18); font-size: 0.8rem; }
.rd-gh-prog .lb { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; opacity: 0.92; }
.rd-gh-prog .lb b { font-family: var(--font-display); font-weight: 800; font-size: 1.02rem; }
.rd-gh-prog .bar { height: 7px; border-radius: var(--r-pill); margin: 7px 0px 0px; background: rgba(0, 0, 0, 0.35); overflow: hidden; }
.rd-gh-prog .bar i { display: block; height: 100%; border-radius: var(--r-pill); background: rgba(255, 255, 255, 0.9); }
.rd-gh-prog .rng { margin-top: 7px; font-size: 0.75rem; opacity: 0.85; }
.rd-gh-risk { display: flex; gap: 9px; margin-top: 10px; padding: 10px 13px; border-radius: var(--r-sm); background: rgba(0, 0, 0, 0.24); border: 1px solid rgba(255, 255, 255, 0.18); font-size: 0.82rem; line-height: 1.5; }
.rd-gh-risk i { flex: 0 0 auto; margin-top: 2px; font-size: 1rem; }
.rd-gh-risk .d { display: block; opacity: 0.82; font-size: 0.75rem; margin-top: 1px; }
.rd-gh-risk.r-safe i, .rd-gh-risk.r-none i { color: var(--green); }
.rd-gh-risk.r-watch i, .rd-gh-risk.r-early i, .rd-gh-risk.r-unknown i { color: var(--gold); }
.rd-gh-risk.r-high i, .rd-gh-risk.r-certain i { color: rgb(255, 154, 162); }
.rd-gh-metrics { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; margin-top: 16px; background: rgba(255, 255, 255, 0.18); border-radius: var(--r-sm); overflow: hidden; }
.rd-gh-metrics > div { background: rgba(0, 0, 0, 0.22); padding: 10px 12px; }
.rd-gh-metrics span { display: block; font-size: 0.72rem; opacity: 0.82; font-weight: 600; }
.rd-gh-metrics b { font-family: var(--font-display); font-weight: 800; font-size: 1.16rem; }
.rd-gh-metrics b small { font-size: 0.78rem; font-weight: 700; opacity: 0.8; }
@media (max-width: 575px) {
  .rd-gh-ring { width: 92px; height: 92px; }
  .rd-gh-num { font-size: 2rem; }
  .rd-gh-pct { font-size: 1.5rem; }
}
/* เฉดเกรด — เจ้าของขอให้ "สดใสขึ้นอีกนิด" (13 ส.ค. 2569)
   ดันความอิ่มสีขึ้นทั้งสองสต็อป โดยเฉพาะ --gt-2 (ปลายเข้มของ gradient) ซึ่งเดิม
   หม่นจนการ์ดดูเป็นสีฝุ่น เช่น gt-f เดิม #A63A3A เป็นแดงอมเทา ไม่ใช่แดงสด
   --gt-wash / --gt-ink เป็นคนละหน้าที่ (พื้นอ่อน + หมึกบนพื้นอ่อน) ไม่ได้แตะ */
.gt-a { --gt: #00A85C; --gt-2: #00854A; --gt-wash: #e2f6ea; --gt-ink: #0b6b3a; }
.gt-b { --gt: #34B33A; --gt-2: #1E8C28; --gt-wash: #e6f6e7; --gt-ink: #256b29; }
.gt-c { --gt: #7FC62F; --gt-2: #5FA318; --gt-wash: #eef7e2; --gt-ink: #4a7222; }
.gt-d { --gt: #CEDB1E; --gt-2: #9FB000; --gt-wash: #f6f8de; --gt-ink: #5f6a00; }
.gt-e { --gt: #FFBA0A; --gt-2: #D99400; --gt-wash: #fdf4dd; --gt-ink: #8a6100; }
.gt-f1 { --gt: #FF9308; --gt-2: #DB7200; --gt-wash: #fdeedd; --gt-ink: #94500a; }
.gt-f2 { --gt: #FF6F24; --gt-2: #DB5200; --gt-wash: #fdeae2; --gt-ink: #a3410c; }
.gt-f { --gt: #FF4A56; --gt-2: #D62839; --gt-wash: #FFEBEB; --gt-ink: #A63A3A; }
.gt-ms { --gt: #B81C25; --gt-2: #941218; --gt-wash: #fbe3e4; --gt-ink: #8c171c; }
.gt-pending { --gt: #8b95a6; --gt-2: #6b7482; --gt-wash: #eef1f5; --gt-ink: #525b68; }
.gt-none { --gt: #b6c2d0; --gt-2: #94a2b3; --gt-wash: #eef1f5; --gt-ink: #525b68; }
html.dark .gt-a { --gt-wash: rgba(15,157,88,.20); --gt-ink: #6fdca0; }
html.dark .gt-b { --gt-wash: rgba(67,160,71,.20); --gt-ink: #7ede82; }
html.dark .gt-c { --gt-wash: rgba(124,179,66,.20); --gt-ink: #b7e07c; }
html.dark .gt-d { --gt-wash: rgba(192,202,51,.20); --gt-ink: #dbe36a; }
html.dark .gt-e { --gt-wash: rgba(242,176,30,.20); --gt-ink: #f0c454; }
html.dark .gt-f1 { --gt-wash: rgba(243,139,28,.20); --gt-ink: #f8b070; }
html.dark .gt-f2 { --gt-wash: rgba(239,108,48,.20); --gt-ink: #f89b74; }
html.dark .gt-f { --gt-wash: rgba(229,72,77,.20); --gt-ink: #ff9a9a; }
html.dark .gt-ms { --gt-wash: rgba(155,28,34,.26); --gt-ink: #ff9a9a; }
html.dark .gt-pending, html.dark .gt-none { --gt-wash: rgba(255,255,255,.10); --gt-ink: #b8c6d6; }
.gt-badge { display: inline-flex; align-items: center; gap: 5px; padding: 2px 11px; border-radius: var(--r-pill); font-family: var(--font-display); font-weight: 800; font-size: 0.86rem; font-variant-numeric: tabular-nums; background: var(--gt-wash); color: var(--gt-ink); }
.gt-badge .lb { font-family: var(--font-body); font-weight: 700; font-size: 0.7rem; opacity: 0.92; }
.gt-solid { background: var(--gt-2); color: rgb(255, 255, 255); }
.rd-gradehero.gt-a, .rd-gradehero.gt-b, .rd-gradehero.gt-c, .rd-gradehero.gt-d, .rd-gradehero.gt-e, .rd-gradehero.gt-f1, .rd-gradehero.gt-f2, .rd-gradehero.gt-f, .rd-gradehero.gt-ms, .rd-gradehero.gt-pending, .rd-gradehero.gt-none { background: radial-gradient(80% 150% at 92% 0%, rgba(255,255,255,.14) 0%, transparent 55%),
    linear-gradient(150deg, var(--gt-2) 0%, var(--gt) 100%); box-shadow: 0 10px 30px color-mix(in srgb, var(--gt-2) 34%, transparent); }
.rd-gh-tone { display: inline-block; margin-top: 6px; font-size: 0.72rem; font-weight: 800; letter-spacing: 0.02em; background: rgba(0, 0, 0, 0.26); border: 1px solid rgba(255, 255, 255, 0.22); padding: 2px 10px; border-radius: var(--r-pill); }
.att-st { --as: #8b95a6; --as-ink: #3c4653; --as-wash: #eef1f5; }
.att-st.st-present { --as: #16a34a; --as-ink: #0b6b3a; --as-wash: #e2f6ea; }
.att-st.st-late { --as: #f5c518; --as-ink: #7a5e00; --as-wash: #fef7d9; }
.att-st.st-leave { --as: #0ea5e9; --as-ink: #0369a1; --as-wash: #e0f2fe; }
.att-st.st-absent { --as: #FF5A5A; --as-ink: #A63A3A; --as-wash: #FFEBEB; }
.att-st.st-skip { --as: #7c4dff; --as-ink: #5327cc; --as-wash: #ece5ff; }
html.dark .att-st.st-present { --as-ink: #6fdca0; --as-wash: rgba(22,163,74,.22); }
html.dark .att-st.st-late { --as-ink: #ffdc5e; --as-wash: rgba(245,197,24,.22); }
html.dark .att-st.st-leave { --as-ink: #7dd3fc; --as-wash: rgba(14,165,233,.22); }
html.dark .att-st.st-absent { --as-ink: #ff9a9a; --as-wash: rgba(229,72,77,.22); }
html.dark .att-st.st-skip { --as-ink: #c4b0ff; --as-wash: rgba(124,77,255,.26); }
.att-st { border: 1px solid var(--border-color); background: var(--bg-card); color: var(--text-muted); font-weight: 700; }
.att-st:hover { border-color: var(--as); color: var(--as-ink); background: var(--as-wash); }
.btn-check:checked + .att-st, .att-st.is-on { background: var(--as-wash) !important; border-color: var(--as) !important; color: var(--as-ink) !important; box-shadow: inset 0 0 0 1px var(--as) !important; }
.btn-check:checked + .att-st .att-dot, .att-st.is-on .att-dot { background: var(--as); }
.att-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--as); opacity: 0.55; margin-right: 5px; vertical-align: middle; }
.btn-check:checked + .att-st .att-dot { opacity: 1; }

/* ── ปุ่มกดสถานะในระบบเรียกชื่อ (Voice Roll-call) ── */
.vc-mark.att-st {
  background: var(--as-wash) !important;
  border-color: var(--as) !important;
  color: var(--as-ink) !important;
  font-weight: 700;
  padding: 8px 18px;
  font-size: 0.96rem;
  border-radius: var(--r-md, 12px);
  box-shadow: 0 2px 6px color-mix(in srgb, var(--as) 18%, transparent);
  transition: transform .12s, box-shadow .12s, background .12s, color .12s;
}
.vc-mark.att-st:hover, .vc-mark.att-st:focus-visible {
  background: var(--as) !important;
  border-color: var(--as) !important;
  color: #ffffff !important;
  transform: translateY(-2px);
  box-shadow: 0 6px 14px color-mix(in srgb, var(--as) 35%, transparent);
}
.vc-mark.att-st:active {
  transform: translateY(0);
}

/* ── ปุ่มตั้งทุกคน (Bulk Set) ── */
.bulk-set.att-st {
  background: var(--as-wash);
  border-color: var(--as);
  color: var(--as-ink);
  font-weight: 700;
  padding: 3px 11px;
  border-radius: var(--r-pill, 999px);
  transition: all .15s ease;
}
.bulk-set.att-st:hover, .bulk-set.att-st:focus-visible {
  background: var(--as) !important;
  border-color: var(--as) !important;
  color: #ffffff !important;
  box-shadow: 0 2px 8px color-mix(in srgb, var(--as) 30%, transparent);
}

/* ── แถบสัดส่วนการเข้าเรียน (.att-bar) ───────────────────────────────────────
   **ของเดิมไม่มีกฎ CSS เลยสักบรรทัด** — มาร์กอัปใน attendance.php ใส่คลาส
   `att-st st-<สถานะ>` ให้ segment เพื่อหยิบตัวแปรสี (--as) มาใช้ แต่คลาสนั้น
   พ่วง "หน้าตาของชิป" มาด้วย (พื้นขาว + ขอบเทา ที่บรรทัด ~1123) segment จึง
   วาดออกมาเป็นกล่องขาวขอบเทาเรียงกัน = แถบเปล่า ๆ ที่เจ้าของเห็นว่าบัค

   ต้องอยู่ **หลัง** บล็อก .att-st ทั้งหมด เพราะ `.att-bar .seg` (0,2,0)
   เสมอกับ `.att-st:hover` (0,2,0) — ชนะได้ด้วยลำดับไฟล์เท่านั้น            */
.att-bar {
  display: flex; height: 12px; border-radius: var(--r-pill, 50px); overflow: hidden;
  background: var(--bg-soft); box-shadow: inset 0 1px 3px rgba(0, 0, 0, .10);
}
.att-bar .seg, .att-bar .seg:hover {
  display: block; height: 100%; min-width: 0; padding: 0;
  border: 0; border-radius: 0; background: var(--as); color: transparent;
  transition: width .3s ease;
}
/* segment ที่เป็น 0% ต้องไม่กินเส้นแบ่ง — ไม่งั้นสถานะที่ไม่มีใครเลยยังเห็นเป็นขีด */
.att-bar .seg[style*="width:0%"], .att-bar .seg[style*="width: 0%"] { display: none; }
html.dark .att-bar { background: rgba(255, 255, 255, .08); box-shadow: inset 0 1px 3px rgba(0, 0, 0, .4); }
.att-chip { display: inline-flex; align-items: center; gap: 5px; padding: 2px 10px; border-radius: var(--r-pill); font-size: 0.74rem; font-weight: 700; background: var(--as-wash); color: var(--as-ink); }
.chk-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.chk-grid-item { display: inline-flex; align-items: center; gap: 8px; margin: 0px; cursor: pointer; padding: 7px 14px 7px 11px; border-radius: var(--r-sm); border: 1px solid var(--border-color); background: var(--bg-card); color: var(--text-muted); font-weight: 700; font-size: 0.88rem; transition: border-color 0.15s, background 0.15s, color 0.15s; }
.chk-grid-item .form-check-input { margin: 0px; flex: 0 0 auto; cursor: pointer; }
.chk-grid-item:hover { border-color: var(--sk); background: var(--sk-wash); }
.chk-grid-item.on, .chk-grid-item:has(.form-check-input:checked) { border-color: var(--sk-dk); background: var(--sk-wash); color: var(--sk-deep); }
html.dark .chk-grid-item.on, html.dark .chk-grid-item:has(.form-check-input:checked) { color: var(--sk-lt); }
.xq-img { max-width: 100%; height: auto; max-height: 220px; border-radius: 10px; border: 1px solid var(--border-color); display: block; }
/* รูปของตัวเลือกในรายการข้อ (หน้าแก้ข้อสอบ · หน้าตรวจข้อสอบ) — เจ้าของแจ้ง 9 ก.ย. 2569
   ว่าเพิ่มรูปให้ตัวเลือกแล้วรายการทางซ้ายไม่โชว์ · เล็กกว่ารูปโจทย์ชัดเจนเพราะมันคือ
   ของประกอบตัวเลือกหนึ่งตัว ไม่ใช่ตัวโจทย์ แต่ต้องใหญ่พอให้ครูดูออกว่าใช่รูปที่ตั้งใจ */
.xq-cimg { display: block; margin-top: 5px; max-width: 240px; max-height: 150px; width: auto; height: auto;
  border-radius: 8px; border: 1px solid var(--border-color); background: #fff; }
.xq-rubric { font-size: 0.87rem; line-height: 1.65; padding: 10px 12px; border-radius: 10px; background: var(--bg-soft, rgba(88,101,242,.06)); border: 1px solid color-mix(in srgb, var(--brand) 30%, transparent); color: var(--text-muted); }
.xg-item { padding: 16px 0px; border-bottom: 1px solid var(--border-color); }
.xg-item:last-of-type { border-bottom: 0px; }
.xg-hd { display: flex; align-items: flex-start; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.xg-no { flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; background: var(--brand); color: rgb(255, 255, 255); font-weight: 700; font-size: 0.85rem; }
.xg-body { flex: 1 1 240px; font-weight: 600; white-space: pre-wrap; }
.xg-ans { white-space: pre-wrap; line-height: 1.8; padding: 14px 16px; border-radius: var(--r-md,14px); background: var(--bg-card); border: 1px solid var(--border-color); margin-bottom: 10px; }
.xg-grade { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.xg-grade input[type="text"] { flex: 1 1 220px; min-width: 0px; }
html.dark .xg-ans { background: rgba(255, 255, 255, 0.04); }
.hm-hero { display: grid; grid-template-columns: minmax(0px, 1.35fr) minmax(0px, 0.85fr); gap: 32px; align-items: center; padding: 44px 42px; margin-bottom: 26px; border-radius: var(--r-xl,40px); color: rgb(255, 255, 255); position: relative; overflow: hidden; background: linear-gradient(135deg, var(--brand-dark, #1e2353) 0%, var(--brand) 62%, var(--sk-dk) 100%); }
.hm-hero::after { content: ""; position: absolute; right: -90px; top: -90px; width: 340px; height: 340px; border-radius: 50%; background: rgba(255, 255, 255, 0.07); pointer-events: none; }
.hm-hero > * { position: relative; z-index: 1; }
.hm-eyebrow { display: inline-flex; align-items: center; gap: 7px; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--gold, #f5c518); margin-bottom: 12px; }
.hm-hero h1 { font-family: var(--font-display); font-weight: 800; letter-spacing: -0.02em; font-size: clamp(1.9rem, 4.2vw, 3rem); line-height: 1.18; margin: 0px 0px 14px; color: rgb(255, 255, 255); }
.hm-hero p { font-size: 1.03rem; line-height: 1.75; color: rgba(255, 255, 255, 0.9); max-width: 52ch; margin: 0px 0px 22px; font-weight: 500; }
.hm-term { margin-top: 18px; font-size: 0.86rem; color: rgba(255, 255, 255, 0.78); display: inline-flex; align-items: center; gap: 7px; }
.hm-cta { display: flex; gap: 12px; flex-wrap: wrap; }
.hm-btn { display: inline-flex; align-items: center; gap: 9px; padding: 13px 26px; border-radius: var(--r-pill,50px); font-weight: 700; font-size: 0.96rem; text-decoration: none; border: 2px solid transparent; transition: transform 0.15s, box-shadow 0.15s; }
.hm-btn:hover { transform: translateY(-2px); box-shadow: rgba(0, 0, 0, 0.22) 0px 10px 24px; }
.hm-btn-main { background: rgb(255, 255, 255); color: var(--brand-dark, #1e2353); }
.hm-btn-ghost { background: rgba(255, 255, 255, 0.12); color: rgb(255, 255, 255); border-color: rgba(255, 255, 255, 0.42); }
.hm-btn-line { background: transparent; color: var(--brand); border-color: var(--brand); }
html.dark a.hm-btn-main { color: var(--brand-dark, #1e2353); }
html.dark a.hm-btn-ghost { color: rgb(255, 255, 255); }
html.dark a.hm-btn-line { color: var(--brand); }
.hm-hero-card { background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: var(--r-lg,16px); padding: 26px 22px; text-align: center; }
.hm-hero-card img { max-width: 72px; height: auto; margin-bottom: 16px; }
.hm-stats { display: grid; gap: 10px; }
.hm-stats > div { display: grid; grid-template-columns: 24px auto minmax(0px, 1fr); align-items: baseline; gap: 10px; background: rgba(0, 0, 0, 0.22); border-radius: var(--r-sm,12px); padding: 11px 14px; text-align: left; }
.hm-stats i { font-size: 1.1rem; color: var(--gold, #f5c518); justify-self: center; }
.hm-stats b { font-family: var(--font-display); font-weight: 800; font-size: 1.32rem; color: rgb(255, 255, 255); }
.hm-stats span { font-size: 0.8rem; color: rgba(255, 255, 255, 0.78); font-weight: 600; }
.hm-roles { display: grid; gap: 16px; margin-bottom: 32px; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.hm-role { display: flex; flex-direction: column; gap: 9px; padding: 26px 24px 22px; border-radius: var(--r-lg,16px); text-decoration: none; background: var(--bg-card); border-right-color: ; border-right-style: ; border-right-width: ; border-bottom-color: ; border-bottom-style: ; border-bottom-width: ; border-left-color: ; border-left-style: ; border-left-width: ; border-image-source: ; border-image-slice: ; border-image-width: ; border-image-outset: ; border-image-repeat: ; border-top: 1px solid var(--hm-c); color: var(--text-main); transition: transform 0.16s, box-shadow 0.16s; }
.hm-role:hover { transform: translateY(-4px); box-shadow: rgba(0, 0, 0, 0.12) 0px 14px 30px; color: var(--text-main); }
.hm-role .ic { width: 50px; height: 50px; border-radius: 14px; display: grid; place-items: center; background: var(--hm-c); color: rgb(255, 255, 255); font-size: 1.35rem; margin-bottom: 4px; }
.hm-role b { font-family: var(--font-display); font-weight: 800; font-size: 1.16rem; }
.hm-role .d { font-size: 0.9rem; line-height: 1.7; color: var(--text-muted); }
.hm-role .go { margin-top: auto; padding-top: 12px; font-weight: 700; font-size: 0.88rem; color: var(--hm-c); display: inline-flex; align-items: center; gap: 6px; }
.r-stu { --hm-c: var(--sk-dk, #0472ae); }
.r-tea { --hm-c: var(--brand); }
.r-par { --hm-c: var(--green-ink, #11793a); }
html.dark a.hm-role, html.dark a.hm-role:hover { color: var(--text-main); }
html.dark a.hm-role .go { color: var(--hm-c); }
html.dark .hm-role { background: rgba(255, 255, 255, 0.03); }
.hm-sec { margin-bottom: 32px; }
.hm-sec-hd { margin-bottom: 18px; }
.hm-sec-hd h2 { font-family: var(--font-display); font-weight: 800; font-size: 1.5rem; letter-spacing: -0.01em; margin: 0px 0px 4px; display: flex; align-items: center; gap: 10px; color: var(--text-main); }
.hm-sec-hd h2 i { color: var(--gold, #f5c518); }
.hm-sec-hd span { font-size: 0.9rem; color: var(--text-muted); }
.hm-board { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(310px, 1fr)); }
.hm-card { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--r-lg,16px); padding: 20px 22px; display: flex; flex-direction: column; }
.hm-card header { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; margin-bottom: 14px; }
.hm-card h3 { font-family: var(--font-display); font-weight: 800; font-size: 1.06rem; margin: 0px 0px 7px; color: var(--text-main); }
.hm-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.hm-tag { display: inline-block; padding: 3px 10px; border-radius: var(--r-pill,50px); background: var(--bg-soft, rgba(88,101,242,.08)); color: var(--brand); font-size: 0.74rem; font-weight: 700; }
.hm-term-sm { font-size: 0.78rem; color: var(--text-muted); white-space: nowrap; font-weight: 600; }
.hm-rank { display: grid; grid-template-columns: 26px minmax(0px, 1fr) auto; align-items: center; gap: 6px 10px; padding: 9px 0px; border-bottom: 1px solid var(--border-color); }
.hm-rank:last-of-type { border-bottom: 0px; }
.hm-rank .n { width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center; background: var(--bg-soft, rgba(0,0,0,.06)); color: var(--text-muted); font-family: var(--font-display); font-weight: 800; font-size: 0.78rem; }
.hm-rank.is-top .n { background: var(--gold, #f5c518); color: rgb(58, 44, 0); }
.hm-rank .nm { font-weight: 600; font-size: 0.92rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0px; }
.hm-rank .sc { font-family: var(--font-display); font-weight: 800; font-size: 0.98rem; white-space: nowrap; color: var(--text-main); }
.hm-rank .sc small { font-weight: 600; font-size: 0.76rem; color: var(--text-muted); margin-left: 2px; }
.hm-rank .bar { grid-column: 1 / -1; height: 5px; border-radius: var(--r-pill,50px); background: var(--bg-soft, rgba(0,0,0,.07)); overflow: hidden; }
.hm-rank .bar i { display: block; height: 100%; border-radius: var(--r-pill,50px); background: linear-gradient(90deg, var(--brand), var(--sk-dk)); }
.hm-rank.is-top .bar i { background: linear-gradient(90deg, var(--gold,#f5c518), #e0a800); }
.hm-empty { text-align: center; color: var(--text-muted); font-size: 0.9rem; padding: 22px 0px; margin: 0px; }
.hm-card footer { margin-top: auto; padding-top: 14px; border-top: 1px solid var(--border-color); font-size: 0.82rem; color: var(--text-muted); display: flex; align-items: center; gap: 7px; }
html.dark .hm-card { background: rgba(255, 255, 255, 0.03); }
html.dark .hm-rank .n, html.dark .hm-rank .bar { background: rgba(255, 255, 255, 0.08); }
html.dark .hm-rank.is-top .n { background: var(--gold,#f5c518); color: rgb(58, 44, 0); }
.hm-end { text-align: center; padding: 46px 30px; border-radius: var(--r-xl,40px); margin-bottom: 26px; background: linear-gradient(135deg, var(--brand-dark, #1e2353), var(--brand)); color: rgb(255, 255, 255); }
.hm-end h2 { font-family: var(--font-display); font-weight: 800; font-size: 1.75rem; letter-spacing: -0.02em; margin: 0px 0px 10px; color: rgb(255, 255, 255); }
.hm-end p { color: rgba(255, 255, 255, 0.86); max-width: 54ch; margin: 0px auto 24px; font-weight: 500; }
.hm-end .hm-cta { justify-content: center; }
.hm-end .hm-btn-line { color: rgb(255, 255, 255); border-color: rgba(255, 255, 255, 0.55); }
html.dark .hm-end a.hm-btn-line { color: rgb(255, 255, 255); }

/* ══════════════════════════════════════════════════════════════════════════
   หน้าแรก — ออกแบบใหม่ (hx-*) · 13 ส.ค. 2569
   ──────────────────────────────────────────────────────────────────────────
   เจ้าของบอกว่าของเดิม "ไม่สวย" · ปัญหาไม่ได้อยู่ที่รายละเอียด แต่อยู่ที่
   **แผ่นน้ำเงินทึบเต็มความกว้าง** ที่กินพื้นที่ครึ่งจอแรก มันบังคับให้ทุก
   อย่างข้างในเป็นสีขาวหมด จึงไม่เหลือช่องให้เล่นลำดับความสำคัญเลย และแผ่นสี
   ทึบใบใหญ่คือภาษางานออกแบบยุคก่อน · ชุด hx-* จึงเปลี่ยนเป็นฮีโร่พื้นสว่าง
   ที่ให้ "ตัวหนังสือ" เป็นพระเอก แล้วใช้สีแบรนด์เน้นเฉพาะจุดที่ต้องการ

   คลาส hm-* ของเดิมยังอยู่ในไฟล์ ไม่ได้ลบ — หน้าอื่นอาจอ้างถึง และการลบ
   CSS ที่ไม่รู้ว่าใครใช้อยู่คือวิธีทำให้หน้าอื่นพังเงียบ ๆ                 */

.hx-hero {
  position: relative; display: grid; gap: 44px; align-items: center;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr);
  padding: 52px 0 60px; margin-bottom: 8px;
}
/* แสงพื้นหลังแบบ mesh — ให้ความลึกโดยไม่ต้องมีกล่องสีทึบ */
.hx-hero::before {
  content: ""; position: absolute; inset: -40px -50vw -20px; z-index: -1; pointer-events: none;
  background:
    radial-gradient(38vw 30vw at 22% 18%, color-mix(in srgb, var(--brand) 16%, transparent), transparent 68%),
    radial-gradient(32vw 26vw at 82% 8%,  color-mix(in srgb, var(--accent, var(--gold)) 15%, transparent), transparent 68%);
}
/* ── "สีเน้น" ที่แอดมินตั้งได้ (--accent) ────────────────────────────────────
   **ใช้ได้เฉพาะจุดที่เป็นการตกแต่งล้วน** — --gold อีก 30+ จุดในไฟล์นี้เป็นสี
   ที่ "สื่อความหมาย" (เตือน · รอตรวจ · เลยกำหนด · คาบชน) ถ้าเอา --accent ไป
   ทับด้วย โรงเรียนที่ตั้งสีเน้นเป็นเขียวจะได้ป้าย "เลยกำหนด" สีเขียว ซึ่งคือ
   การโกหกด้วยสี · กฎเดียวกับที่ statFamilies() เขียนไว้ว่าแดงต้องแดงเสมอ

   จุดที่ต่อท่อไว้เขียนไว้ที่กฎเดิมของแต่ละตัวโดยตรง (ค้นคำว่า --accent-face)
   ไม่ได้รวมไว้ที่นี่ เพราะกฎเดิมอยู่ท้ายไฟล์กว่าและ specificity เท่ากัน
   เขียนซ้ำตรงนี้จะถูกทับทันทีโดยไม่มีอะไรฟ้อง (พลาดมาแล้วรอบหนึ่ง)          */
.hx-badge {
  display: inline-flex; align-items: center; gap: 8px; margin-bottom: 18px;
  padding: 7px 15px; border-radius: var(--r-pill, 50px);
  background: var(--bg-card); border: 1px solid var(--border-color);
  box-shadow: 0 2px 10px color-mix(in srgb, var(--brand) 12%, transparent);
  font-size: .78rem; font-weight: 800; letter-spacing: .06em;
  color: var(--brand-dark, var(--sk-dk));
}
/* --accent-face = "สีเน้น" ที่แอดมินตั้ง ผ่านเกณฑ์การอ่านบนพื้นขาวมาแล้ว
   (palette.php บังคับ 4.6) ต่างจาก --accent ดิบที่อาจสว่างจนมองไม่เห็น */
.hx-badge i { color: var(--accent-face, var(--gold)); font-size: .95rem; }
html.dark .hx-badge { color: var(--sk-lt); }
.hx-hero h1 {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(2.1rem, 5vw, 3.5rem); line-height: 1.12; letter-spacing: -.03em;
  margin: 0 0 16px; color: var(--text-main);
}
.hx-lead {
  font-size: clamp(1rem, 1.5vw, 1.12rem); line-height: 1.8; font-weight: 500;
  color: var(--text-muted); max-width: 46ch; margin: 0 0 26px;
}
/* เน้นด้วย "น้ำหนัก + สี" ได้ เพราะพื้นเป็นสีสว่าง — ของเดิมทำไม่ได้เลย
   เพราะทุกอย่างเป็นตัวขาวบนน้ำเงินอยู่แล้ว */
.hx-lead b { color: var(--text-main); font-weight: 800; }
.hx-cta { display: flex; gap: 14px; flex-wrap: wrap; }
/* ── ปุ่มหน้าแรก: ออกแบบใหม่ (13 ส.ค. 2569) ───────────────────────────────
   ของเดิมเป็นแคปซูลไล่สีธรรมดา ดูเหมือนปุ่ม bootstrap ที่เปลี่ยนสี
   ชุดใหม่ให้มี "ผิว" จริง: ขอบแสงด้านบน เงาสองชั้น และไอคอนที่ขยับตอนชี้     */
.hx-btn {
  position: relative; overflow: hidden; isolation: isolate;
  display: inline-flex; align-items: center; gap: 10px;
  padding: 15px 30px; border-radius: 16px;
  font-weight: 800; font-size: 1rem; letter-spacing: -.01em; text-decoration: none;
  border: 1px solid transparent;
  transition: transform .18s cubic-bezier(.2,.8,.3,1), box-shadow .18s, background .18s, border-color .18s;
}
.hx-btn i { font-size: 1.05rem; transition: transform .18s; }
.hx-btn:hover { transform: translateY(-3px); }
.hx-btn:active { transform: translateY(-1px); }
.hx-btn:hover i { transform: translateX(2px) scale(1.05); }

/* ปุ่มหลัก — ไล่สีทแยง + เส้นแสงบางด้านบน (inset) ให้ดูเป็นวัตถุมีผิว
   ไม่ใช่แผ่นสีแบน · เงาสองชั้น: ชั้นแคบให้ขอบคม ชั้นกว้างให้ลอย */
.hx-btn-solid {
  background: linear-gradient(135deg,
              color-mix(in srgb, var(--brand) 88%, #fff) 0%,
              var(--brand) 42%,
              var(--brand-dark, var(--sk-dk)) 100%);
  color: #fff;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.34),
              0 2px 6px color-mix(in srgb, var(--brand-dark, var(--sk-dk)) 30%, transparent),
              0 12px 28px color-mix(in srgb, var(--brand) 34%, transparent);
}
/* แถบแสงกวาดผ่านตอนเอาเมาส์ชี้ — อยู่ใต้ตัวหนังสือเพราะ z-index -1 บน isolate */
.hx-btn-solid::after {
  content: ""; position: absolute; inset: 0; z-index: -1; transform: translateX(-120%);
  background: linear-gradient(105deg, transparent 20%, rgba(255,255,255,.28) 50%, transparent 80%);
  transition: transform .55s cubic-bezier(.2,.8,.3,1);
}
.hx-btn-solid:hover::after { transform: translateX(120%); }
.hx-btn-solid:hover {
  color: #fff;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.4),
              0 4px 10px color-mix(in srgb, var(--brand-dark, var(--sk-dk)) 36%, transparent),
              0 18px 38px color-mix(in srgb, var(--brand) 44%, transparent);
}

/* ปุ่มรอง — พื้นการ์ด ขอบชัด · ตอนชี้ค่อยย้อมด้วยสีแบรนด์จาง ๆ
   ไม่ใช่พลิกเป็นพื้นทึบ ซึ่งจะแย่งความสนใจจากปุ่มหลัก */
.hx-btn-soft {
  background: var(--bg-card); color: var(--brand-dark, var(--sk-dk));
  border-color: var(--border-color);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.6), 0 2px 8px rgba(0,0,0,.05);
}
.hx-btn-soft:hover {
  background: color-mix(in srgb, var(--brand) 8%, var(--bg-card));
  border-color: var(--brand); color: var(--brand-dark, var(--sk-dk));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.6), 0 10px 24px color-mix(in srgb, var(--brand) 20%, transparent);
}
html.dark a.hx-btn-soft { color: var(--sk-lt); }
html.dark .hx-btn-soft { box-shadow: inset 0 1px 0 rgba(255,255,255,.06), 0 2px 8px rgba(0,0,0,.3); }
html.dark .hx-btn-soft:hover { box-shadow: inset 0 1px 0 rgba(255,255,255,.06), 0 10px 24px rgba(0,0,0,.45); }
@media (prefers-reduced-motion: reduce) {
  .hx-btn, .hx-btn i, .hx-btn-solid::after { transition: none; }
  .hx-btn:hover { transform: none; }
}
.hx-term {
  display: inline-flex; align-items: center; gap: 9px; margin-top: 22px;
  font-size: .86rem; font-weight: 600; color: var(--text-muted);
}
/* จุดเขียวเต้น = "ระบบเปิดใช้อยู่จริง" อ่านได้ทันทีโดยไม่ต้องมีคำอธิบาย */
.hx-term .dot {
  width: 9px; height: 9px; border-radius: 50%; background: var(--green, #16a34a);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--green, #16a34a) 60%, transparent);
  animation: hxPulse 2.4s ease-out infinite;
}
@keyframes hxPulse {
  70%  { box-shadow: 0 0 0 9px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
@media (prefers-reduced-motion: reduce) { .hx-term .dot { animation: none; } }

/* ── ฝั่งขวา: ตราโรงเรียน + ชิปตัวเลขลอย ─────────────────────────────────── */
.hx-hero-art { position: relative; display: grid; place-items: center; min-height: 300px; }
.hx-medal {
  width: 190px; height: 190px; border-radius: 50%; display: grid; place-items: center;
  background: radial-gradient(circle at 32% 28%, #fff, var(--bg-soft));
  box-shadow: 0 20px 50px color-mix(in srgb, var(--brand) 26%, transparent),
              inset 0 0 0 1px var(--border-color);
}
.hx-medal img { max-width: 96px; height: auto; }
.hx-medal i { font-size: 3.4rem; color: var(--brand); }
html.dark .hx-medal { background: radial-gradient(circle at 32% 28%, var(--bg-card), var(--bg-soft)); }
.hx-chip {
  position: absolute; display: flex; align-items: center; gap: 9px;
  padding: 11px 17px; border-radius: var(--r-pill, 50px);
  background: var(--bg-card); border: 1px solid var(--border-color);
  box-shadow: 0 10px 26px color-mix(in srgb, var(--brand) 16%, transparent);
  white-space: nowrap;
}
.hx-chip i { color: var(--brand); font-size: 1.05rem; }
.hx-chip b { font-family: var(--font-display); font-weight: 800; font-size: 1.22rem; color: var(--text-main); }
.hx-chip span { font-size: .8rem; font-weight: 600; color: var(--text-muted); }
/* วางเยื้องกันสามมุม ไม่ใช่เรียงเป็นแถว — แถวเรียงอ่านเหมือนตาราง */
.hx-chip.c1 { top: 4%;  left: -2%; }
.hx-chip.c2 { top: 44%; right: -6%; }
.hx-chip.c3 { bottom: 4%; left: 6%; }

/* ── เบนโต: ใบครูกินสองแถว เพราะเป็นคนใช้ทุกวัน ─────────────────────────── */
.hx-bento {
  display: grid; gap: 16px; margin-bottom: 44px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-areas: "lead a" "lead b";
}
.hx-tile.is-lead { grid-area: lead; }
.hx-tile {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: 10px;
  padding: 26px 24px; border-radius: var(--r-xl, 20px); text-decoration: none;
  background: var(--bg-card); border: 1px solid var(--border-color);
  color: var(--text-main); transition: transform .18s, box-shadow .18s, border-color .18s;
}
/* แถบสีอยู่ที่ ::before ไม่ใช่ border-top — border-top ทำให้มุมโค้งของการ์ด
   ถูกตัดเป็นเหลี่ยมตรงหัว ซึ่งเห็นชัดมากที่ radius 20px */
/* เส้นผมหนึ่งพิกเซล ไม่ใช่แถบ 4px — แถบสีหนาบนหัว/ข้างการ์ดคือลายเซ็นของ UI
   ที่เจนมา และที่นี่เคยมี 16 จุด · สีของไทล์ยังบอกได้จากไอคอนกับหัวเรื่อง     */
.hx-tile::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 1px;
  background: var(--hm-c);
}
.hx-tile:hover {
  transform: translateY(-4px); border-color: var(--hm-c); color: var(--text-main);
  box-shadow: 0 18px 40px color-mix(in srgb, var(--hm-c) 22%, transparent);
}
.hx-tile .ic {
  width: 52px; height: 52px; border-radius: 16px; display: grid; place-items: center;
  font-size: 1.4rem; color: #fff; margin-bottom: 4px;
  background: linear-gradient(135deg, var(--hm-c), color-mix(in srgb, var(--hm-c) 62%, #000));
  box-shadow: 0 8px 18px color-mix(in srgb, var(--hm-c) 34%, transparent);
}
.hx-tile b { font-family: var(--font-display); font-weight: 800; font-size: 1.2rem; }
.hx-tile.is-lead b { font-size: 1.5rem; }
.hx-tile .d { font-size: .92rem; line-height: 1.75; color: var(--text-muted); }
.hx-tile .tags { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 4px; }
.hx-tile .tags span {
  font-size: .76rem; font-weight: 700; padding: 5px 12px; border-radius: var(--r-pill, 50px);
  background: var(--bg-soft); color: var(--hm-c);
}
.hx-tile .go {
  margin-top: auto; padding-top: 14px; font-weight: 800; font-size: .9rem;
  color: var(--hm-c); display: inline-flex; align-items: center; gap: 7px;
}
.hx-tile .go i { transition: transform .18s; }
.hx-tile:hover .go i { transform: translateX(5px); }
html.dark .hx-tile { background: rgba(255,255,255,.03); }
html.dark a.hx-tile, html.dark a.hx-tile:hover { color: var(--text-main); }

/* ── หัวข้อส่วน ──────────────────────────────────────────────────────────── */
.hx-sec { margin-bottom: 44px; }
.hx-sec-hd { margin-bottom: 20px; }
.hx-sec-hd h2 {
  font-family: var(--font-display); font-weight: 800; font-size: clamp(1.35rem, 2.4vw, 1.75rem);
  letter-spacing: -.02em; margin: 0 0 5px; color: var(--text-main);
  display: flex; align-items: center; gap: 12px;
}
.hx-sec-hd h2::before {
  content: ""; width: 5px; height: 1.05em; border-radius: 3px; flex: 0 0 auto;
  background: linear-gradient(180deg, var(--brand), var(--brand-dark, var(--sk-dk)));
}
.hx-sec-hd span { font-size: .92rem; color: var(--text-muted); padding-left: 17px; display: block; }

/* ── กระดานผล ────────────────────────────────────────────────────────────── */
.hx-board { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); }
.hx-bcard {
  display: flex; flex-direction: column; padding: 22px;
  background: var(--bg-card); border: 1px solid var(--border-color);
  border-radius: var(--r-xl, 20px); transition: box-shadow .18s, transform .18s, border-color .18s;
}
.hx-bcard:hover { box-shadow: 0 14px 34px color-mix(in srgb, var(--brand) 14%, transparent); }
/* การ์ดที่คลิกได้ (คะแนนของวิชานั้นเปิดสาธารณะ) — วิชาที่ไม่ได้เปิดยังเป็น
   <article> ธรรมดาไม่มีลิงก์ ตามแบบ "เพื่อนร่วมวิชา" ใน course_home */
a.hx-bcard { text-decoration: none; color: inherit; cursor: pointer; }
a.hx-bcard:hover { transform: translateY(-4px); border-color: var(--brand); }
html.dark a.hx-bcard, html.dark a.hx-bcard:hover { color: var(--text-main); }
.hx-bcard header { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 16px; }
.hx-bcard h3 { font-family: var(--font-display); font-weight: 800; font-size: 1.1rem; margin: 0 0 8px; color: var(--text-main); }
.hx-bcard .tags { display: flex; gap: 6px; flex-wrap: wrap; }
.hx-bcard .tags span {
  padding: 3px 11px; border-radius: var(--r-pill, 50px);
  background: var(--bg-soft); color: var(--brand-dark, var(--sk-dk));
  font-size: .74rem; font-weight: 700;
}
html.dark .hx-bcard .tags span { color: var(--sk-lt); }
.hx-bcard .term { font-size: .78rem; font-weight: 700; color: var(--text-muted); white-space: nowrap; }
.hx-rank {
  display: grid; grid-template-columns: 28px minmax(0,1fr) auto auto; align-items: center;
  gap: 5px 11px; padding: 10px 0; border-bottom: 1px solid var(--border-color);
}
.hx-rank:last-of-type { border-bottom: 0; }
.hx-rank .n {
  width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center;
  font-family: var(--font-display); font-weight: 800; font-size: .8rem;
  background: var(--bg-soft); color: var(--text-muted);
}
/* เหรียญสามอันดับแรก — ของเดิมเน้นแค่อันดับ 1 ทำให้ที่ 2 กับ 3 จมหายไป */
.hx-rank.m1 .n { background: linear-gradient(135deg,#FFD45A,#E8A21A); color:#4A3000; }
.hx-rank.m2 .n { background: linear-gradient(135deg,#DCE3EA,#AEBBC7); color:#33414E; }
.hx-rank.m3 .n { background: linear-gradient(135deg,#F0C08A,#C98A4B); color:#4A2E12; }
.hx-rank .nm { font-weight: 600; font-size: .92rem; color: var(--text-main); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hx-rank .sc { font-family: var(--font-display); font-weight: 800; font-size: .95rem; color: var(--text-main); font-variant-numeric: tabular-nums; }
.hx-rank .sc small { font-size: .72rem; font-weight: 600; color: var(--text-muted); }
.hx-rank .bar { grid-column: 1 / -1; height: 5px; border-radius: var(--r-pill, 50px); background: var(--bg-soft); overflow: hidden; }
.hx-rank .bar i { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--brand), var(--sk-dk)); }
.hx-rank.m1 .bar i { background: linear-gradient(90deg,#FFD45A,#E8A21A); }
.hx-empty { text-align: center; color: var(--text-muted); font-size: .9rem; padding: 24px 0; margin: 0; }
.hx-bcard footer {
  margin-top: auto; padding-top: 16px; border-top: 1px solid var(--border-color);
  font-size: .82rem; color: var(--text-muted); display: flex; align-items: center; gap: 7px;
}
.hx-bcard footer .go {
  margin-left: auto; font-weight: 800; color: var(--brand-dark, var(--sk-dk));
  display: inline-flex; align-items: center; gap: 5px;
}
.hx-bcard footer .go i { transition: transform .18s; }
a.hx-bcard:hover footer .go i { transform: translateX(4px); }
html.dark .hx-bcard footer .go { color: var(--sk-lt); }
html.dark .hx-bcard { background: rgba(255,255,255,.03); }
html.dark .hx-rank .n, html.dark .hx-rank .bar { background: rgba(255,255,255,.08); }

/* ── คู่มือการใช้งาน ─────────────────────────────────────────────────────── */
.hx-docs { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); }
.hx-doc {
  display: grid; grid-template-columns: 56px minmax(0,1fr); gap: 18px; align-items: start;
  background: var(--bg-card); border: 1px solid var(--border-color);
  border-radius: var(--r-xl, 20px); padding: 24px;
  transition: transform .18s, box-shadow .18s, border-color .18s;
}
.hx-doc:hover {
  transform: translateY(-3px); border-color: var(--hm-c);
  box-shadow: 0 18px 40px color-mix(in srgb, var(--hm-c) 20%, transparent);
}
.hx-doc .ic {
  width: 56px; height: 56px; border-radius: 18px; display: grid; place-items: center;
  font-size: 1.55rem; color: #fff;
  background: linear-gradient(135deg, var(--hm-c), color-mix(in srgb, var(--hm-c) 62%, #000));
  box-shadow: 0 8px 18px color-mix(in srgb, var(--hm-c) 32%, transparent);
}
.hx-doc .tx { min-width: 0; }
.hx-doc b { display: block; font-family: var(--font-display); font-weight: 800; font-size: 1.12rem; color: var(--text-main); margin-bottom: 6px; }
.hx-doc .d { display: block; font-size: .89rem; line-height: 1.75; color: var(--text-muted); }
.hx-doc .dl {
  grid-column: 2; margin-top: 16px; justify-self: start;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 20px; border-radius: var(--r-pill, 50px);
  font-weight: 800; font-size: .88rem; text-decoration: none;
  background: var(--hm-c); color: #fff; border: 2px solid transparent;
  transition: filter .15s, transform .15s;
}
.hx-doc a.dl:hover { filter: brightness(1.08); transform: translateY(-1px); color: #fff; }
/* สถานะ "ยังไม่มีไฟล์" ต้องอ่านเป็นป้ายบอกสถานะ ไม่ใช่ปุ่มที่กดแล้วเงียบ */
.hx-doc .dl.is-soon { background: var(--bg-soft); color: var(--text-muted); border-color: var(--border-color); cursor: default; }
html.dark .hx-doc { background: rgba(255,255,255,.03); }

/* ── ปิดท้าย ─────────────────────────────────────────────────────────────── */
.hx-end {
  position: relative; overflow: hidden; text-align: center;
  padding: 52px 30px; margin-bottom: 30px; border-radius: var(--r-jumbo, 32px);
  color: #fff;
  background: linear-gradient(135deg, var(--brand-dark, var(--sk-dk)) 0%, var(--brand) 100%);
  box-shadow: 0 20px 50px color-mix(in srgb, var(--brand-dark, var(--sk-dk)) 30%, transparent);
}
.hx-end::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(46% 90% at 84% -10%, rgba(255,255,255,.18), transparent 62%);
}
.hx-end > * { position: relative; z-index: 1; }
.hx-end h2 { font-family: var(--font-display); font-weight: 800; font-size: clamp(1.5rem,3vw,2rem); letter-spacing: -.02em; margin: 0 0 10px; color: #fff; }
.hx-end p { color: rgba(255,255,255,.9); max-width: 52ch; margin: 0 auto 26px; font-weight: 500; }
.hx-end .hx-cta { justify-content: center; }
.hx-btn-white { background: #fff; color: var(--brand-dark, var(--sk-dk)); box-shadow: 0 8px 22px rgba(0,0,0,.18); }
.hx-btn-white:hover { color: var(--brand-dark, var(--sk-dk)); }
.hx-btn-outline { background: rgba(255,255,255,.12); color: #fff; border-color: rgba(255,255,255,.55); }
.hx-btn-outline:hover { background: rgba(255,255,255,.2); color: #fff; }
html.dark a.hx-btn-white { color: var(--sk-deep); }
html.dark a.hx-btn-outline { color: #fff; }

@media (max-width: 980px) {
  .hx-hero { grid-template-columns: 1fr; gap: 34px; padding: 34px 0 40px; }
  .hx-hero-art { min-height: 240px; }
  .hx-medal { width: 150px; height: 150px; }
  .hx-medal img { max-width: 74px; }
  .hx-bento { grid-template-columns: 1fr; grid-template-areas: "lead" "a" "b"; }
}
@media (max-width: 575px) {
  .hx-hero { padding: 22px 0 30px; }
  .hx-hero h1 { font-size: 1.85rem; }
  .hx-btn { padding: 11px 20px; font-size: .9rem; }
  /* ชิปลอยทับกันบนจอแคบ — เปลี่ยนเป็นเรียงใต้ตราแทน */
  .hx-hero-art { min-height: auto; gap: 14px; }
  .hx-chip { position: static; }
  .hx-hero-art { display: flex; flex-direction: column; align-items: center; }
  .hx-doc { grid-template-columns: 1fr; }
  .hx-doc .dl { grid-column: 1; }
  .hx-end { padding: 36px 20px; border-radius: var(--r-xl, 20px); }
}

@media (max-width: 900px) {
  .hm-hero { grid-template-columns: 1fr; padding: 32px 24px; gap: 24px; }
  .hm-hero::after { display: none; }
}
@media (max-width: 575px) {
  .hm-hero { padding: 26px 20px; border-radius: var(--r-lg,16px); }
  .hm-hero h1 { font-size: 1.7rem; }
  .hm-btn { padding: 11px 20px; font-size: 0.9rem; }
  .hm-end { padding: 34px 20px; border-radius: var(--r-lg,16px); }
  .hm-end h2 { font-size: 1.4rem; }
  .hm-board { grid-template-columns: 1fr; }
}
.fm-hero { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; padding: 20px 24px; border-radius: var(--r-lg,16px); margin-bottom: 18px; color: rgb(255, 255, 255); background: linear-gradient(135deg, var(--brand-dark, #1e2353), var(--brand)); }
.fm-hero-who { display: flex; align-items: center; gap: 14px; min-width: 0px; }
.fm-av { width: 52px; height: 52px; min-width: 52px; border-radius: 50%; display: grid; place-items: center; background: rgba(255, 255, 255, 0.18); font-size: 1.5rem; }
.fm-hero h4 { margin: 0px; font-family: var(--font-display); font-weight: 800; font-size: 1.25rem; color: rgb(255, 255, 255); }
.fm-hero .fm-sub { font-size: 0.85rem; color: rgba(255, 255, 255, 0.82); }
.fm-stats { display: grid; gap: 12px; margin-bottom: 18px; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.fm-stat { display: grid; gap: 2px; padding: 16px 18px; border-radius: var(--r-lg,16px); background: var(--bg-card); border-top-color: ; border-top-style: ; border-top-width: ; border-right-color: ; border-right-style: ; border-right-width: ; border-bottom-color: ; border-bottom-style: ; border-bottom-width: ; border-image-source: ; border-image-slice: ; border-image-width: ; border-image-outset: ; border-image-repeat: ; border-left: 4px solid var(--fm-c, var(--brand)); }
.fm-stat i { font-size: 1.15rem; color: var(--fm-c, var(--brand)); }
.fm-stat b { font-family: var(--font-display); font-weight: 800; font-size: 1.6rem; line-height: 1.15; }
.fm-stat span { font-size: 0.82rem; color: var(--text-muted); font-weight: 600; }
.fm-stat.s1 { --fm-c: var(--brand); }
.fm-stat.s2 { --fm-c: var(--green-ink, #11793a); }
.fm-stat.s3 { --fm-c: var(--gold-ink, #8a6100); }
.fm-stat.s4 { --fm-c: var(--red-ink, #A63A3A); }
html.dark .fm-stat { background: rgba(255, 255, 255, 0.03); }
.fm-card { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--r-lg,16px); padding: 18px 20px; margin-bottom: 16px; }
html.dark .fm-card { background: rgba(255, 255, 255, 0.03); }
.fm-card-hd h5 { font-family: var(--font-display); font-weight: 800; font-size: 1.05rem; margin: 0px 0px 3px; display: flex; align-items: center; gap: 8px; }
.fm-card-hd span { font-size: 0.84rem; color: var(--text-muted); }
.fm-card-bd { margin-top: 14px; }
.fm-term { font-family: var(--font-display); font-weight: 800; font-size: 1.05rem; margin: 22px 0px 12px; padding-bottom: 8px; border-bottom: 2px solid var(--border-color); }
.fm-subj { display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap; }
.fm-subj-t { min-width: 0px; flex: 1 1 220px; }
.fm-subj-t h5 { font-family: var(--font-display); font-weight: 800; font-size: 1.08rem; margin: 0px 0px 4px; }
.fm-subj .fm-sub { font-size: 0.83rem; color: var(--text-muted); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.fm-subj-g { text-align: center; padding: 10px 16px; border-radius: var(--r-sm,12px); min-width: 112px; background: var(--gt-wash, var(--bg-soft)); color: var(--gt-ink, var(--text-main)); }
.fm-subj-g b { display: block; font-family: var(--font-display); font-weight: 800; font-size: 1.5rem; line-height: 1.1; }
.fm-subj-g span { font-size: 0.74rem; opacity: 0.85; font-weight: 600; }
.fm-outlook { display: flex; align-items: flex-start; gap: 10px; margin-top: 14px; padding: 11px 14px; border-radius: var(--r-sm,12px); background: var(--fm-o-bg, var(--bg-soft)); border-left: 3px solid var(--fm-o, var(--brand)); }
.fm-outlook i { color: var(--fm-o, var(--brand)); font-size: 1.05rem; margin-top: 1px; }
.fm-outlook b { display: block; font-size: 0.9rem; color: var(--fm-o, var(--text-main)); }
.fm-outlook span { font-size: 0.83rem; color: var(--text-muted); line-height: 1.6; }
.fm-outlook.r-safe { --fm-o: var(--green-ink, #11793a); }
.fm-outlook.r-none { --fm-o: var(--green-ink, #11793a); }
.fm-outlook.r-early { --fm-o: var(--sk-dk, #0472ae); }
.fm-outlook.r-unknown { --fm-o: var(--text-muted); }
.fm-outlook.r-watch { --fm-o: var(--gold-ink, #8a6100); }
.fm-outlook.r-high { --fm-o: var(--red-ink, #A63A3A); }
.fm-outlook.r-certain { --fm-o: var(--red-ink, #A63A3A); }
.fm-grid { display: grid; gap: 14px; margin-top: 16px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.fm-box { border: 1px solid var(--border-color); border-radius: var(--r-sm,12px); padding: 13px 15px; min-width: 0px; }
.fm-box h6 { font-family: var(--font-display); font-weight: 800; font-size: 0.9rem; margin: 0px 0px 10px; display: flex; align-items: center; gap: 7px; }
.fm-box h6 i { color: var(--brand); }
.fm-pct { margin-left: auto; font-size: 0.8rem; padding: 2px 9px; border-radius: var(--r-pill,50px); }
.fm-pct.ok { background: color-mix(in srgb, var(--green-ink,#11793a) 14%, transparent); color: var(--green-ink,#11793a); }
.fm-pct.bad { background: color-mix(in srgb, var(--red-ink,#A63A3A) 14%, transparent); color: var(--red-ink,#A63A3A); }
.fm-empty { font-size: 0.85rem; color: var(--text-muted); margin: 0px; padding: 10px 0px; text-align: center; }
.fm-empty.ok { color: var(--green-ink, #11793a); }
.fm-cat { display: flex; justify-content: space-between; gap: 8px; margin: 10px 0px 5px; font-size: 0.8rem; font-weight: 700; color: var(--brand); border-bottom: 1px solid var(--border-color); padding-bottom: 4px; }
.fm-cat:first-of-type { margin-top: 0px; }
.fm-item { display: flex; justify-content: space-between; gap: 10px; font-size: 0.85rem; padding: 4px 0px; }
.fm-item .nm { min-width: 0px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fm-item .sc { white-space: nowrap; font-weight: 600; }
.fm-item .sc.na { color: var(--text-muted); font-weight: 400; font-size: 0.78rem; }
.fm-item .sc small { color: var(--text-muted); font-weight: 400; }
/* "รอครูตรวจข้อเขียน" — ต้องอ่านออกว่าเป็นสถานะ ไม่ใช่คะแนน จึงใช้สีเตือน
   ไม่ใช่สีจาง ๆ แบบ .na ซึ่งแปลว่า "ไม่มีอะไร" */
.fm-item .sc .fm-wait { color: var(--bs-warning-text-emphasis, #997404); background: var(--bs-warning-bg-subtle, #fff3cd);
  border-radius: 6px; padding: 1px 6px; font-size: 0.72rem; font-weight: 600; white-space: nowrap; }
.fm-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }
.fm-chip { font-size: 0.76rem; padding: 3px 10px; border-radius: var(--r-pill,50px); background: var(--bg-soft); font-weight: 600; }
.fm-chip b { font-weight: 800; }
.fm-chip.st-p { background: color-mix(in srgb, var(--green-ink,#11793a) 14%, transparent); color: var(--green-ink,#11793a); }
.fm-chip.st-l { background: color-mix(in srgb, rgb(138, 97, 0) 15%, transparent); color: rgb(138, 97, 0); }
.fm-chip.st-v { background: color-mix(in srgb, var(--sk-dk,#0472ae) 14%, transparent); color: var(--sk-dk,#0472ae); }
.fm-chip.st-a { background: color-mix(in srgb, var(--red-ink,#A63A3A) 14%, transparent); color: var(--red-ink,#A63A3A); }
.fm-chip.st-s { background: color-mix(in srgb, rgb(107, 33, 168) 15%, transparent); color: rgb(107, 33, 168); }
html.dark .fm-chip.st-l { color: rgb(245, 197, 24); }
html.dark .fm-chip.st-s { color: rgb(196, 162, 245); }
.fm-sess { max-height: 230px; overflow-y: auto; margin: 0px -4px; padding: 0px 4px; }
.fm-ses { display: grid; grid-template-columns: 1fr auto auto; gap: 8px; align-items: center; font-size: 0.82rem; padding: 5px 0px; border-bottom: 1px solid var(--border-color); }
.fm-ses:last-child { border-bottom: 0px; }
.fm-ses .tp { color: var(--text-muted); font-size: 0.76rem; }
.fm-ses .sv { font-weight: 700; font-size: 0.78rem; white-space: nowrap; }
.fm-ses.st-p .sv { color: var(--green-ink, #11793a); }
.fm-ses.st-l .sv { color: rgb(138, 97, 0); }
.fm-ses.st-v .sv { color: var(--sk-dk, #0472ae); }
.fm-ses.st-a .sv { color: var(--red-ink, #A63A3A); }
.fm-ses.st-s .sv { color: rgb(107, 33, 168); }
.fm-ses.st-none .sv { color: var(--text-muted); font-weight: 400; }
html.dark .fm-ses.st-l .sv { color: rgb(245, 197, 24); }
html.dark .fm-ses.st-s .sv { color: rgb(196, 162, 245); }
.fm-work { padding: 7px 0px; border-bottom: 1px solid var(--border-color); }
.fm-work:last-child { border-bottom: 0px; }
.fm-work .nm { display: block; font-size: 0.86rem; font-weight: 600; }
.fm-work .dd { font-size: 0.77rem; color: var(--text-muted); display: inline-flex; align-items: center; gap: 5px; margin-top: 2px; }
.fm-work.is-late .dd { color: var(--red-ink, #A63A3A); font-weight: 600; }
.fm-links { display: grid; gap: 8px; margin-bottom: 12px; }
.fm-link { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; padding: 9px 12px; border-radius: var(--r-sm,12px); background: var(--bg-soft); }
.fm-link .nm { font-size: 0.88rem; font-weight: 600; margin-right: auto; min-width: 0px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
html.dark .fm-link { background: rgba(255, 255, 255, 0.05); }
.fm-code { padding: 16px; border-radius: var(--r-sm,12px); text-align: center; border: 2px dashed var(--brand); background: var(--bg-soft); }
html.dark .fm-code { background: rgba(255, 255, 255, 0.04); }
.fm-code-n { font-family: var(--font-display); font-weight: 800; font-size: 2.1rem; letter-spacing: 0.28em; color: var(--brand); margin-bottom: 12px; word-break: break-all; }
.fm-code-steps { text-align: left; max-width: 420px; margin: 0px auto 14px; padding-left: 20px; font-size: 0.86rem; line-height: 1.85; color: var(--text-muted); }
.fm-code-steps b { color: var(--brand); }
.fm-code-note { font-size: 0.76rem; color: var(--text-muted); margin-top: 10px; }
@media (max-width: 575px) {
  .fm-hero { padding: 16px 18px; }
  .fm-card { padding: 14px 15px; }
  .fm-subj-g { min-width: 96px; padding: 8px 12px; }
  .fm-stat b { font-size: 1.35rem; }
  .fm-code-n { font-size: 1.6rem; letter-spacing: 0.2em; }
}
.fs-note { display: flex; align-items: flex-start; gap: 8px; margin-top: 10px; padding: 9px 13px; border-radius: var(--r-sm); font-size: 0.86rem; line-height: 1.6; background: var(--bg-soft); border-left: 3px solid var(--fsn, var(--sk-dk)); color: var(--text-main); }
.fs-note i { color: var(--fsn, var(--sk-dk)); margin-top: 2px; }
.fs-note.is-ok { --fsn: var(--green-ink); }
.fs-note.is-wait { --fsn: var(--sk-dk); }
.fs-note.is-over { --fsn: var(--red-ink); }
html.dark .fs-note { background: rgba(255, 255, 255, 0.05); }
/* --sk-dk (#2C5EAD) เป็นสีพื้น ใช้เป็นหมึกบนการ์ดมืดได้ 2.42 */
html.dark .fs-note.is-wait { --fsn: var(--sk-lt); }
.att-prog.part > span { background: linear-gradient(90deg, var(--sk-dk), var(--sk)); }
.sidebar-link.active { background: linear-gradient(135deg, var(--sk-dk), var(--sk-deep)); box-shadow: rgba(44, 94, 173, 0.35) 0px 6px 16px; }
.stat-1, .card.stat-1 { background: linear-gradient(135deg, rgb(44, 94, 173), rgb(31, 69, 133)); color: rgb(255, 255, 255); }
.stat-2, .card.stat-2 { background: linear-gradient(135deg, rgb(26, 111, 165), rgb(20, 86, 139)); color: rgb(255, 255, 255); }
.stat-3, .card.stat-3 { background: linear-gradient(135deg, rgb(255, 139, 90), rgb(255, 90, 90)); color: rgb(74, 16, 0); }
.stat-4, .card.stat-4 { background: linear-gradient(135deg, rgb(255, 212, 90), rgb(255, 169, 90)); color: rgb(90, 48, 0); }
.stat-1 .stat-label, .stat-2 .stat-label, .stat-1 .stat-val, .stat-2 .stat-val { color: rgb(255, 255, 255); }
.stat-3 .stat-label, .stat-3 .stat-val { color: rgb(255, 255, 255); }
.stat-4 .stat-label, .stat-4 .stat-val { color: rgb(255, 255, 255); }
html.dark .card.stat-1 { background: linear-gradient(135deg, rgb(44, 94, 173), rgb(31, 69, 133)); color: rgb(255, 255, 255); }
html.dark .card.stat-2 { background: linear-gradient(135deg, rgb(26, 111, 165), rgb(20, 86, 139)); color: rgb(255, 255, 255); }
html.dark .card.stat-3 { background: linear-gradient(135deg, rgb(255, 139, 90), rgb(255, 90, 90)); color: rgb(255, 255, 255); }
html.dark .card.stat-4 { background: linear-gradient(135deg, rgb(255, 212, 90), rgb(255, 169, 90)); color: rgb(255, 255, 255); }
html.dark .card.stat-3 .stat-label, html.dark .card.stat-3 .stat-val { color: rgb(255, 255, 255); }
html.dark .card.stat-4 .stat-label, html.dark .card.stat-4 .stat-val { color: rgb(255, 255, 255); }
/* โหมดสว่าง = พาสเทลหมึกเข้ม (โทน backup) · หมึกคือ --rd-ac ตัวเดียวกับที่ใช้
   บนชิปขาวและเป็นพื้นปุ่มหลัก จึงไม่ต้องมีค่าที่สี่ให้หลุด sync ได้อีกค่า */
.rd-course { --rd-ink: var(--rd-ac); border: 1px solid rgba(0, 0, 0, 0.10); background: var(--rd-pb) !important; color: var(--rd-ink) !important; }
.rd-course:hover { color: var(--rd-ink) !important; box-shadow: rgba(31, 42, 68, 0.16) 0px 14px 30px; }
/* วงตกแต่งมุมการ์ด: ขาว 14% มองไม่เห็นบนพาสเทล ต้องย้อมด้วยหมึกแทน */
.rd-course::after { background: color-mix(in srgb, var(--rd-ac) 7%, transparent) !important; }
/* ชิปเป็นแผ่นขาวลอยบนการ์ดสี หมึกจึงต้อง **เข้ม** (--rd-ac) ไม่ใช่ขาวตามการ์ด */
/* ขอบชิปเคยเป็นขาว .96 ซึ่งบนพื้นสดคือ "ขอบเรืองแสง" แต่บนพาสเทลคือขาวบนขาว
   มองไม่เห็นเลย ชิปเลยดูเป็นแผ่นลอย ๆ ไม่มีรูป — ย้อมด้วยหมึกจาง ๆ แทน */
.rd-course .rd-ccode2 { background: rgba(255, 255, 255, 0.9) !important; border-color: color-mix(in srgb, var(--rd-ac) 24%, transparent) !important; color: var(--rd-ac) !important; }
.rd-course .rd-cchip { background: rgba(255, 255, 255, 0.9) !important; border-color: color-mix(in srgb, var(--rd-ac) 24%, transparent) !important; color: var(--rd-ac) !important; }
.rd-course .rd-cgrade { color: var(--rd-ink) !important; }
/* รางกับแท่งความคืบหน้าเคยเป็นขาวบนพื้นสด — บนพาสเทลต้องย้อมด้วยหมึกแทน
   ไม่งั้นทั้งรางและแท่งขาวบนพาสเทลกลายเป็นแถบเปล่า มองไม่ออกว่าเต็มแค่ไหน */
.rd-course .rd-cbar { background: color-mix(in srgb, var(--rd-ac) 24%, transparent) !important; }
.rd-course .rd-cbar span { background: var(--rd-ac) !important; }
/* **ห้ามหรี่ด้วย opacity** — หมึก --rd-ac บนพาสเทลอยู่ที่ 5.85-8.00 ซึ่งดูเหลือ
   เยอะ แต่ .rd-csub เป็น 0.76rem ต้องผ่าน 4.5 ไม่ใช่ 3.0 หรี่ลง 30% เมื่อไหร่
   เฉดที่ต่ำสุด (peach 5.85) ตกทันที · และ getComputedStyle('color') มองไม่เห็น
   opacity เลย ตัวเลขที่วัดได้จึงดูดีกว่าที่ตาเห็นเสมอ
   ลำดับความสำคัญมาจากขนาด/น้ำหนักฟอนต์แทน ไม่ใช่ความจาง                    */
.rd-course .rd-csub, .rd-course .rd-cbarlabel { color: var(--rd-ink) !important; opacity: 1 !important; }
/* โหมดมืดยังเป็นพื้นเข้มหมึกขาว — บล็อก html.dark ข้างล่างเป็นคนกำหนดจริง
   ที่นี่เขียน #fff ตรง ๆ ไม่ใช่ var(--rd-ink) เพราะ --rd-ink คือหมึกเข้มของ
   โหมดสว่าง ถ้าอ้างตัวแปรไว้จะขัดกับกฎข้างล่างและอ่านแล้วเข้าใจผิดได้        */
html.dark .rd-course, html.dark .rd-course:hover { color: #fff !important; }
.rd-c1 { background: none; --rd-pb: #AFD5F0; --rd-ac: #24508F; }
.rd-c2 { background: none; --rd-pb: #FFC2D1; --rd-ac: #93304C; }
.rd-c3 { background: none; --rd-pb: #C8E6B2; --rd-ac: #2F6122; }
.rd-c4 { background: none; --rd-pb: #FFD1AD; --rd-ac: #83430F; }
.rd-c5 { background: none; --rd-pb: #D9C4EC; --rd-ac: #5B3C82; }
.rd-c6 { background: none; --rd-pb: #FCF695; --rd-ac: #6F5A00; }
.stat-1, .card.stat-1 { background: rgb(175, 213, 240); color: rgb(31, 42, 68); }
.stat-2, .card.stat-2 { background: rgb(195, 238, 250); color: rgb(31, 42, 68); }
.stat-3, .card.stat-3 { background: rgb(255, 195, 155); color: rgb(31, 42, 68); }
.stat-4, .card.stat-4 { background: rgb(253, 223, 142); color: rgb(31, 42, 68); }
.stat-1 .stat-label, .stat-2 .stat-label, .stat-3 .stat-label, .stat-4 .stat-label, .stat-1 .stat-val, .stat-2 .stat-val, .stat-3 .stat-val, .stat-4 .stat-val { color: rgb(31, 42, 68); }
.stat-card .stat-icon { opacity: 0.28; }
html.dark .card.stat-1 { background: rgb(175, 213, 240); color: rgb(31, 42, 68); }
html.dark .card.stat-2 { background: rgb(195, 238, 250); color: rgb(31, 42, 68); }
html.dark .card.stat-3 { background: rgb(255, 195, 155); color: rgb(31, 42, 68); }
html.dark .card.stat-4 { background: rgb(253, 223, 142); color: rgb(31, 42, 68); }
html.dark .card.stat-1 .stat-label, html.dark .card.stat-2 .stat-label, html.dark .card.stat-3 .stat-label, html.dark .card.stat-4 .stat-label, html.dark .card.stat-1 .stat-val, html.dark .card.stat-2 .stat-val, html.dark .card.stat-3 .stat-val, html.dark .card.stat-4 .stat-val { color: rgb(31, 42, 68); }

/* ════════════════════════════════════════════════════════════════════════════
   สีการ์ดรายวิชาที่ครูเลือกเอง (migration_v27)
   ────────────────────────────────────────────────────────────────────────────
   ค่าทั้งหมดต้องตรงกับ courseColors() ใน functions.php — tests/CourseColorTest
   เทียบให้ ถ้าฝั่งใดฝั่งหนึ่ง drift เทสต์จะแดงแทนที่จะได้การ์ดสีเพี้ยนเงียบ ๆ

   แต่ละคีย์ให้ 3 ค่า:
     --rd-pb     พื้นโหมดสว่าง (พาสเทล — ตัวหนังสือบนการ์ดเป็น --rd-ac)
     --rd-ac     หมึกของการ์ด + หมึกบน "ชิปขาว" + พื้นปุ่มหลัก (เข้มเสมอ)
     --rd-pb-dk  พื้นโหมดมืด — สีเข้ม หมึกขาว (คนละสัญญากับโหมดสว่าง)

   **โหมดสว่างเป็นพาสเทลหมึกเข้ม โหมดมืดเป็นพื้นเข้มหมึกขาว — คนละสัญญากัน
   โดยตั้งใจ** เจ้าของเลือกโทน backup (13 ส.ค. 2569) ซึ่งเป็นพาสเทลในโหมดสว่าง
   แต่เว็บเก่าปล่อยพาสเทลค้างในโหมดมืดด้วย (`html.dark .proj-box{color:#11161f}`)
   = แผงไฟกลางจอมืด จึงเอาแค่ครึ่งสว่างมา ครึ่งมืดคงของเดิมไว้ทั้งดุ้น

   --rd-ac ไม่ได้เปลี่ยนสักค่าตอนสลับมาพาสเทล — ของเดิมเข้มพอที่จะเลื่อนจาก
   "สีคู่" มาเป็น "หมึกหลัก" ได้เลย จึงไม่มีค่าที่สี่ให้หลุด sync เพิ่ม

   ทุกค่าผ่าน AA 4.5 แล้ว: --rd-ac บน --rd-pb (หมึกการ์ด) 5.14-7.00
   · --rd-ac บนชิปขาว .9 ได้ 7.04-9.62 · ขาวบน --rd-ac (ปุ่มหลัก) 7.31+
   · ขาวบน --rd-pb-dk 7.88+ (ของเดิม ไม่ได้แตะ)
   · --rd-pb เทียบพื้นหน้า #eef6fc 1.20-1.34 — แยกใบออกจากพื้นได้ชัด

   **ห้ามทำพาสเทลอ่อนกว่านี้** ชุดแรกอยู่ที่สว่าง ~92% (ห่างพื้นหน้าแค่ 1.09-1.17)
   ตัวเลข contrast ของหมึกสูงกว่าชุดนี้ด้วยซ้ำ แต่เจ้าของทักว่าตัวหนังสือกลืน
   พื้นหลัง เพราะที่ตาเห็นคือ "การ์ดจมไปกับหน้า" ไม่ใช่ตัวอักษรเทียบพื้นการ์ด
   CourseColorTest จึงบังคับระยะห่างจากพื้นหน้าไว้ที่ 1.18 เป็นเกณฑ์แยกต่างหาก
   ตรวจด้วย scratchpad/contrast.php ทุกครั้งที่แก้ HEX
════════════════════════════════════════════════════════════════════════════ */
.rd-cp-blue     { --rd-pb: #C7DBF0; --rd-ac: #1B4472; --rd-pb-dk: #22548A }
.rd-cp-sky      { --rd-pb: #C4E1F3; --rd-ac: #075985; --rd-pb-dk: #075985 }
.rd-cp-mint     { --rd-pb: #C1E4DF; --rd-ac: #115E59; --rd-pb-dk: #115E59 }
.rd-cp-green    { --rd-pb: #C9E7D1; --rd-ac: #14532D; --rd-pb-dk: #166534 }
.rd-cp-yellow   { --rd-pb: #F2DFB2; --rd-ac: #713F12; --rd-pb-dk: #854D0E }
.rd-cp-sand     { --rd-pb: #EBD7C3; --rd-ac: #6B3E17; --rd-pb-dk: #7C4A1D }
.rd-cp-peach    { --rd-pb: #F9D0BC; --rd-ac: #9A3412; --rd-pb-dk: #9A3412 }
.rd-cp-coral    { --rd-pb: #F9CCCC; --rd-ac: #991B1B; --rd-pb-dk: #991B1B }
.rd-cp-pink     { --rd-pb: #F9CEDE; --rd-ac: #9D174D; --rd-pb-dk: #9D174D }
.rd-cp-purple   { --rd-pb: #DFD0F4; --rd-ac: #6B21A8; --rd-pb-dk: #6B21A8 }
.rd-cp-lavender { --rd-pb: #D2D2F8; --rd-ac: #3730A3; --rd-pb-dk: #4338CA }
.rd-cp-slate    { --rd-pb: #D0D9E4; --rd-ac: #37485C; --rd-pb-dk: #425568 }

/* ค่าตั้งต้น — ถ้าการ์ดไม่มีคลาสสี var() ที่ resolve ไม่ได้จะทำให้ทั้ง declaration
   หลุด (กับดักที่ DESIGN.md §9 เตือนไว้) จึงต้องมีค่าพื้นฐานเสมอ
   **ต้องเป็น :where() เท่านั้น** — เขียน `.rd-course` เฉย ๆ จะได้ specificity (0,1,0)
   เท่ากับ .rd-cp-* แต่อยู่ท้ายไฟล์กว่า → ทับทุกสีจนการ์ดฟ้าเหมือนกันหมด
   (เกิดขึ้นจริงตอนเขียนครั้งแรก ทั้งแปดสีออกมาเป็นฟ้า) */
:where(.rd-course) { --rd-pb:#C7DBF0; --rd-ac:#1B4472; --rd-pb-dk:#22548A }

/* เงาย้อมสีการ์ด — การ์ดฟ้าได้เงาฟ้า การ์ดชมพูได้เงาชมพู แทนเงาเทาเหมือนกันหมด
   สองชั้น: ชั้นแคบให้ขอบคม ชั้นกว้างให้ลอย */
/* เงาถูกถอดออก (15 ส.ค. 2569) ตามกฎ "เงา = ลอยอยู่เหนือหน้ากระดาษ" ซึ่งจริง
   กับ modal · dropdown · toast · popup เท่านั้น · การ์ดรายวิชาคือหน้ากระดาษ
   **สองกฎนี้เคยมี `!important`** จึงชนะกฎ `box-shadow: none` ของบล็อก PASS 3
   ที่อยู่ท้ายไฟล์ — การ์ดจึงยังลอยอยู่ทั้งที่โค้ดข้างล่างบอกว่าแบนแล้ว
   ตรวจไม่เจอจนกระทั่งได้บัญชีครูจริงมาเปิดดู เพราะบัญชีที่ใช้ทดสอบก่อนหน้านั้น
   ไม่มีรายวิชาเลยสักวิชา การ์ดจึงไม่เคยถูกเรนเดอร์ · แก้ที่ต้นเหตุแทนการ
   ไล่ยัด `!important` ซ้อนกันไปอีกชั้น                                       */
.rd-course, .rd-course:hover { box-shadow: none !important; }

/* ── ปุ่มในการ์ด: ตามสีการ์ด ไม่ใช่น้ำเงินตายตัว ───────────────────────────
   **ปุ่มพวกนี้เป็น .btn-53 ต้องคุมด้วย token ห้ามยัด background/color ทับ**
   main.js แปลงปุ่มเป็นสองชั้น: `.original` (ที่เห็นตอนปกติ พื้น = `--face`)
   ซ้อนบนตัวปุ่มเอง (ที่โผล่ตอน hover พื้น = `--face-dk`) และ `.letters`
   **ถูกบังคับเป็นสีขาวที่บรรทัด 143** เขียนทับ `background` ของตัวปุ่มเป็น
   `#fff` เมื่อไหร่ = **ขาวบนขาว ตัวหนังสือหายทั้งปุ่มตอนเอาเมาส์ไปชี้**
   (เกิดจริงกับปุ่ม .btn-light 6 จาก 8 ปุ่มในทุกการ์ด) และการทับ `background`
   ก็ไม่ได้แก้ปัญหาเดิมด้วย เพราะสิ่งที่ตาเห็นตอนปกติคือ `.original` = `--face`
   ซึ่งยังเป็นน้ำเงินค่าตั้งต้นอยู่ ปุ่มจึงไม่เคยตามสีการ์ดจริงสักที             */
.rd-course .btn-53.btn-brand {
  --face: var(--rd-ac); --ink: #fff;
  --face-dk: color-mix(in srgb, var(--rd-ac) 74%, #000);
}
.rd-course .btn-53.btn-light {
  --face: color-mix(in srgb, #fff 88%, var(--rd-pb)); --ink: var(--rd-ac);
  --face-dk: var(--rd-ac);   /* ต้องเข้มพอ — ตัวอักษรตอน hover เป็นขาวเสมอ */
}
/* ปุ่มพื้นขาวต้องมีขอบ — ตอนพื้นการ์ดเป็นสีสดเข้ม ปุ่มขาวเด้งออกมาเองอยู่แล้ว
   จึงเคยสั่ง `box-shadow: none` ไว้ · พอพื้นเป็นพาสเทล ปุ่มขาวกับการ์ดห่างกัน
   แค่ 1.2-1.3 ปุ่มเลยกลายเป็นรอยจาง ๆ ไม่มีรูปทรง (เจ้าของทักตรงนี้)

   **ขอบต้องทึบ 2px เหมือน .btn-outline-brand** (ปุ่ม "สรุปคะแนนรวม" ที่เจ้าของ
   ชี้เป็นตัวอย่าง — ดูกฎที่บรรทัด ~34) ของเดิมใช้หมึกจาง 34% ซึ่งยังอ่อนไป
   ต่างกันแค่ปุ่มในการ์ดผูกกับ --rd-ac ของใบนั้น ไม่ใช่ --sk ตายตัว

   ใช้ inset ring ไม่ใช่ border เพราะ .btn-53 เป็นปุ่มสองชั้น การใส่ border
   จะดันขนาดชั้นนอกจนสองชั้นเหลื่อมกันตอน hover                              */
.rd-course .btn-53.btn-light > .original {
  box-shadow: inset 0 0 0 1px var(--rd-ac);
}
/* ปุ่มที่ main.js ข้ามไม่แปลง (ไอคอนล้วน/อยู่ใน .btn-group) ยังต้องใช้ทางเดิม */
.rd-course .btn-brand:not(.btn-53),
.rd-course a.btn-brand:not(.btn-53) {
  background: var(--rd-ac) !important; border-color: var(--rd-ac) !important; color: #fff !important;
}
.rd-course .btn-brand:not(.btn-53):hover,
.rd-course a.btn-brand:not(.btn-53):hover {
  background: color-mix(in srgb, var(--rd-ac) 80%, #000) !important;
  border-color: color-mix(in srgb, var(--rd-ac) 80%, #000) !important; color: #fff !important;
}
.rd-course .btn-light:not(.btn-53),
.rd-course a.btn-light:not(.btn-53) {
  /* ขอบขาว .92 หายไปกับพื้นพาสเทล เหมือนกรณี .btn-53 ข้างบน — ย้อมด้วยหมึก
     ปุ่มกลุ่มนี้เป็นปุ่มชั้นเดียว จึงใส่ border ตรง ๆ ได้ ไม่ต้องใช้ ring
     สีทึบเท่า .btn-outline-brand ที่เจ้าของชี้เป็นตัวอย่าง แต่หนา 1px
     (2px เจ้าของบอกว่าหนาไป — การ์ดหนึ่งใบมีปุ่มขาว 5-6 ปุ่ม ขอบหนาเลยหนัก) */
  background: #fff !important;
  border: 1px solid var(--rd-ac) !important;
  color: var(--rd-ac) !important;
}
.rd-course .btn-light:not(.btn-53):hover,
.rd-course a.btn-light:not(.btn-53):hover {
  background: #fff !important; color: color-mix(in srgb, var(--rd-ac) 78%, #000) !important;
}

/* ── โหมดมืด: เฉดเดียวกันแต่เข้มลง หมึกยังเป็นขาว ─────────────────────────
   ของเดิมพลิกเอา --rd-pb (พาสเทล) มาเป็นหมึก ซึ่งใช้กับชุดสีสดไม่ได้เลย —
   #2D68EC บน #1E3A8A ได้ 1.7 คือการ์ดทั้งใบอ่านไม่ออก · ชุดใหม่หมึกขาว
   ทั้งสองโหมด เปลี่ยนแค่ความเข้มของพื้น การ์ดจึงหน้าตาเหมือนกันทั้งสองโหมด */
html.dark .rd-course {
  background: var(--rd-pb-dk) !important;
  color: #fff !important;
  border-color: rgba(255,255,255,.10) !important;
  /* เงาถอดออกแล้ว — ดูกฎ .rd-course ด้านบน */
}
html.dark .rd-course:hover {
  color: #fff !important;
  /* เงาถอดออกแล้ว — ดูกฎ .rd-course ด้านบน */
}
html.dark .rd-course::after { background: rgba(255,255,255,.05) !important }
html.dark .rd-course .rd-ccode2,
html.dark .rd-course .rd-cchip {
  background: rgba(255,255,255,.14) !important; border-color: rgba(255,255,255,.24) !important;
  color: #fff !important;
}
html.dark .rd-course .rd-cgrade      { color: #fff !important }
html.dark .rd-course .rd-cbar        { background: rgba(255,255,255,.18) !important }
html.dark .rd-course .rd-cbar span   { background: #fff !important }
html.dark .rd-course .rd-csub,
html.dark .rd-course .rd-cbarlabel   { color: #fff !important; opacity: 1 !important }
/* ปุ่มรองในโหมดมืด: ของเดิมเป็น "ขาวโปร่ง 16%" ทับพื้นการ์ดสีเข้ม ซึ่งได้ผล
   เป็นแผ่นขุ่น ๆ ไม่มีรูปทรง (เจ้าของทักว่าการ์ดรายวิชาโหมดมืดดูบัค)
   เปลี่ยนเป็น "ดำโปร่ง + วงขอบสว่าง" — ดำโปร่งทำให้ปุ่มเข้มกว่าพื้นการ์ดเสมอ
   ไม่ว่าการ์ดจะเป็นเฉดไหน ส่วนวงขอบเป็นตัวบอกรูปทรง แบบเดียวกับโหมดสว่าง   */
html.dark .rd-course .btn-53.btn-light {
  --face: rgba(0,0,0,.30); --ink: #fff;
  --face-dk: rgba(0,0,0,.48);
}
html.dark .rd-course .btn-53.btn-light > .original {
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.42);
}
/* ปุ่มหลักในการ์ด (โหมดมืด) ต้องอ้าง **พื้นการ์ด** ไม่ใช่ `--rd-ac`
   `--rd-ac` เป็นค่าตายตัวต่อเฉด พอพื้นการ์ดโหมดมืดถูกทำให้อ่อนลง ปุ่มกับการ์ด
   จะไล่เข้าหากันจนกลืน (วัดได้ 1.22-1.55 ซึ่ง contrast กับ "ตัวหนังสือ" ยังผ่าน
   หมด จึงไม่มีอะไรฟ้อง) · การผสมจากพื้นการ์ดทำให้ปุ่มห่างจากการ์ดเท่ากันทุกเฉด
   โดยอัตโนมัติ (1.83-1.88) และจะไม่พังอีกไม่ว่าจะปรับความอ่อนพื้นการ์ดไปแค่ไหน */
html.dark .rd-course .btn-53.btn-brand {
  --face: color-mix(in srgb, var(--rd-pb-dk) 58%, #000); --ink: #fff;
  --face-dk: color-mix(in srgb, var(--rd-pb-dk) 40%, #000);
}
html.dark .rd-course .btn-brand:not(.btn-53),
html.dark .rd-course a.btn-brand:not(.btn-53) {
  background: color-mix(in srgb, var(--rd-pb-dk) 58%, #000) !important;
  border-color: color-mix(in srgb, var(--rd-pb-dk) 45%, #000) !important; color: #fff !important;
}
/* ปุ่มชั้นเดียวในโหมดมืด — เหตุผลเดียวกับ .btn-53 ข้างบน: ขาวโปร่ง 12% บนพื้น
   การ์ดสีเข้มอ่านเป็นแผ่นขุ่น · ที่แย่กว่าคือหมึกใช้ `var(--rd-pb)` ซึ่งตั้งแต่
   เปลี่ยนเป็นโทนพาสเทล ค่านั้นคือ "สีอ่อนของโหมดสว่าง" ไม่ได้ผูกกับพื้นโหมดมืด
   เลย · ใช้ขาวทึบเป็นหมึกแทน แล้วให้ขอบเป็นตัวบอกรูปทรง                     */
html.dark .rd-course .btn-light:not(.btn-53),
html.dark .rd-course a.btn-light:not(.btn-53) {
  background: rgba(0,0,0,.30) !important; border-color: rgba(255,255,255,.42) !important;
  color: #fff !important;
}
html.dark .rd-course .btn-light:not(.btn-53):hover,
html.dark .rd-course a.btn-light:not(.btn-53):hover {
  background: rgba(0,0,0,.48) !important; border-color: rgba(255,255,255,.6) !important;
  color: #fff !important;
}

/* ── ตัวเลือกสีในหน้าแก้ไข/สร้างรายวิชา ─────────────────────────────────── */
.cc-pick { display:flex; flex-wrap:wrap; gap:8px }
.cc-sw {
  display:inline-flex; align-items:center; gap:7px; padding:6px 12px; cursor:pointer;
  border:1px solid var(--border-color); border-radius:var(--r-sm); background:var(--bg-card);
  font-size:.84rem; font-weight:600; color:var(--text-main); transition:.15s;
}
.cc-sw:hover { border-color:var(--sw-ac, var(--sk-dk)) }
.cc-dot { width:17px; height:17px; border-radius:5px; background:var(--sw);
          border:1px solid rgba(0,0,0,.14); flex:none }
.cc-auto .bi { color:var(--text-muted) }
.btn-check:checked + .cc-sw {
  border-color:var(--sw-ac, var(--sk-dk)) !important;
  box-shadow: inset 0 0 0 2px var(--sw-ac, var(--sk-dk));
  color:var(--sw-ac, var(--sk-deep)) !important;
}
html.dark .btn-check:checked + .cc-auto { color:var(--sk-lt) !important }

.cc-preview { display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:14px }
.cc-pv-lb { font-size:.76rem; font-weight:700; color:var(--text-muted); margin-bottom:6px }
.cc-pv-box { border:1px solid var(--border-color); border-radius:var(--r-md); padding:12px; background:var(--bg-base) }
.cc-pv-box.cc-pv-dark { background:#0b1526; border-color:#24405f }
@media (max-width:560px){ .cc-preview{ grid-template-columns:1fr } }

/* ════════════════════════════════════════════════════════════════════════════
   VIVID CARDS — การ์ดสถิติสีสด (แทนชุดพาสเทลเดิม)
   ทุกคู่สีคำนวณ WCAG แล้ว: พื้นเข้ม = หมึกขาว, พื้นสว่าง (ทอง) = หมึกเกือบดำ
   สคริปต์ที่ใช้ตรวจอยู่ที่ scratchpad/contrast.php — วัดจาก HEX ตรง ๆ
   ════════════════════════════════════════════════════════════════════════════ */

/* ── การ์ดสถิติของหน้าที่จัดวางใหม่ (.rd-stat) ─────────────────────────────
   เว็บเก่าไล่สีตาม "ตำแหน่ง" (stat-1..4) ไม่ใช่ตามความหมาย จึงใช้ nth-child
   เป็นฐาน — ใน .rd-stats ตำแหน่งชนะเสมอ (0,3,0 > 0,2,0) ส่วนคลาสความหมาย
   (s-red/s-gold/s-green) เป็นค่าสำรองของ .rd-stat ที่อยู่นอกกริด

   ทุกตำแหน่งต้องประกาศ --rd-sink ของตัวเองให้ครบทั้งสี่ ห้ามเว้นให้ตัวไหน
   ตกไปรับค่าจากคลาสความหมาย — พื้นมาจากกฎหนึ่งแต่หมึกมาจากอีกกฎหนึ่งเมื่อไหร่
   จะได้การ์ดพื้นน้ำเงินหมึกน้ำตาล (#4A1000 บน #1A6FA5 = 1.5 อ่านไม่ออก)
   ซึ่งไม่มีอะไรฟ้อง เพราะทั้งสองกฎถูกของมันเอง                              */
/* พื้นน้ำเงินอยู่ที่กฎฐานด้วย ไม่ใช่แค่ nth-child(1) — ใบที่ 5 ขึ้นไป หรือ
   .rd-stat ที่ไม่ได้อยู่ใน .rd-stats จะได้ background: var(--bg-card) จาก
   กฎเดิม แล้วเจอหมึกขาวของบล็อกนี้ = ขาวบนขาว หายไปทั้งใบ                  */
/* ชุดสีสด: สามใบแรกเป็นพื้นเข้มหมึกขาว ใบ "ทอง" เป็นพื้นสว่างหมึกเกือบดำ
   ตั้งใจให้มีใบสว่างหนึ่งใบในแถว — ทองที่เข้มพอให้ตัวขาวอ่านออกจะกลายเป็น
   น้ำตาล ไม่ใช่ทองอีกต่อไป (ขาวบน #D97706 ได้ 3.19 · บน #FBBF24 ได้ 1.67)   */
/* ── สิบตระกูลสี (NKW BRIGHT 2569) ────────────────────────────────────────
   ต้องตรงกับ statFamilies() ใน functions.php เป๊ะ — tests/StatFamilyTest.php
   เทียบไว้ ถ้าฝั่งใดฝั่งหนึ่ง drift เทสต์จะแดงแทนที่จะได้การ์ดสีเพี้ยนเงียบ ๆ
   (กับดักเดียวกับ .rd-cp-* กับ courseColors() ซึ่งมี CourseColorTest คุมอยู่)

   ตระกูลพวกนี้ **ไม่ผูกกับสีที่แอดมินตั้ง** โดยตั้งใจ — แดงต้องแดงเสมอไม่ว่า
   โรงเรียนจะเลือกสีประจำเป็นอะไร ไม่งั้น "กลุ่มเสี่ยง" อาจกลายเป็นเขียวได้
   ส่วนโครงหน้า (แถบบน/หัวตาราง/ปุ่ม/ลิงก์) ตามสีโรงเรียน ดู palette.php    */
:root {
  --f-indigo: #0369A1; --f-indigo-dk: #075985; --f-indigo-soft: #E8F4FB; --f-indigo-ink: #075985;
  --f-sky:    #0277B0; --f-sky-dk:    #0369A1; --f-sky-soft:    #E0F2FE; --f-sky-ink:    #065F87;
  --f-teal:   #0F766E; --f-teal-dk:   #115E59; --f-teal-soft:   #E6F5F3; --f-teal-ink:   #115E59;
  --f-lime:   #4D7C0F; --f-lime-dk:   #3F6212; --f-lime-soft:   #F1F7E6; --f-lime-ink:   #3F6212;
  --f-amber:  #F2B01E; --f-amber-dk:  #8A5A05; --f-amber-soft:  #FDF3DF; --f-amber-ink:  #8A5A05;
  --f-orange: #C2410C; --f-orange-dk: #9A3412; --f-orange-soft: #FDEEE6; --f-orange-ink: #9A3412;
  --f-rose:   #BE185D; --f-rose-dk:   #9D174D; --f-rose-soft:   #FCEAF1; --f-rose-ink:   #9D174D;
  --f-red:    #C22A2F; --f-red-dk:    #991B1B; --f-red-soft:    #FCEBEB; --f-red-ink:    #991B1B;
  --f-violet: #6D28D9; --f-violet-dk: #5B21B6; --f-violet-soft: #F2EDFD; --f-violet-ink: #5B21B6;
  --f-cyan:   #0E7490; --f-cyan-dk:   #155E75; --f-cyan-soft:   #E4F4F8; --f-cyan-ink:   #155E75;
}

/* ── การ์ดสถิติ: น้ำเงิน → ฟ้า → ส้ม → เหลือง (เว็บเก่าของจริง) ─────────────
   เจ้าของสั่งกลับไปใช้แบบเว็บก่อน REDESIGN (13 ส.ค. 2569) หลังเห็นว่าการไล่สี
   ตาม "ความหมาย" ทำให้แถวการ์ดเป็นสี่สีคนละโทน ซึ่งคือสิ่งที่ทำให้หน้าจอแสบตา

   **รอบแรกอ้างอิงผิดไฟล์** — ไปเทียบกับ `storage/ui_backup_pre_redesign/`
   ซึ่งเป็นภาพถ่ายของ REDESIGN รอบก่อน (ใช้ token --sk อยู่แล้ว) ไม่ใช่เว็บเก่า
   จึงได้ ฟ้าเข้ม → ฟ้าลึก → ฟ้าอ่อน → ทอง ซึ่งเจ้าของบอกว่ายังไม่ใช่
   เว็บเก่าตัวจริงอยู่ที่โฟลเดอร์ `backup/assets/css/style.css:358-361`:
       .stat-1 #1591DC→#2C5EAD · .stat-2 #4BB8FA→#1591DC
       .stat-3 #FF8B5A→#FF5A5A · .stat-4 #FFD45A→#FFA95A
   คือ **น้ำเงิน → ฟ้า → ส้ม → เหลือง** — ครึ่งเย็นครึ่งอุ่น ไม่ใช่ฟ้าล้วน

   **เฉดสว่างของเว็บเก่า + หมึกเข้ม + ตัวหนา (เจ้าของเลือก 13 ส.ค. 2569)**
   หมึกเข้มบนพื้นสว่างผ่าน AA สบาย ๆ (5.94-10.93) ส่วนน้ำหนักตัวอักษรทำหน้าที่
   ให้ตัวหนังสือมีน้ำหนักพอบนพื้นสีจัด — ไม่ใช้ทั้งเงาและขอบ เจ้าของลองแล้วไม่ชอบ

   ลองมาแล้วเจ็ดรอบ **อย่าวนกลับไปข้อไหนอีกโดยไม่ถาม**:
     1. เฉดสว่าง + หมึกขาว (เว็บเก่า)     → 1.42-3.06 อ่านยาก
     2. เฉดสว่าง + หมึกเข้มร่วมเฉด        → 5.02-7.98 ผ่าน AA แต่ "กลืน"
        (#02405F/#4A1000/#5A3000 เข้มกว่าพื้นแต่สีเดียวกัน ตาอ่านเป็นเนื้อเดียว)
     3. เฉดสว่าง + หมึกเกือบดำ            → 5.94-10.93 เจ้าของสั่งให้เป็นตัวขาว
     4. หมึกขาว + เงาใต้ตัวอักษร          → เงาไม่นับเป็นคอนทราสต์ ยังอ่านยาก
     5. หมึกขาว + หรี่พื้นจนผ่าน 4.5      → #0577BC/#D13E00/#946D00 ผ่านหมด
        แต่เจ้าของไม่เอา "โทนนี้" สั่งกลับไปใช้สีเดิม (เข้มแล้วกลายเป็นน้ำตาล
        มะกอก ไม่ใช่ส้มอ่อน/เหลืองสดอีกต่อไป ซึ่งเป็นจุดที่เจ้าของไม่ยอม)
     6. หมึกเข้ม + ขอบขาว (-webkit-text-stroke) → เจ้าของไม่ชอบขอบ
     7. หมึกเข้ม + ตัวหนา 800 (ไม่มีเงา ไม่มีขอบ) → ผ่านเกณฑ์ แต่เจ้าของเทียบ
        กับภาพหน้าจอเว็บเก่าแล้วบอกว่ายังไม่ใช่ ต้องเป็นตัวขาว
     8. ไล่เฉดเว็บเก่า (`backup/assets/css/style.css:358-361`) + หมึกขาว
     9. **ปัจจุบัน: ไล่เฉดเว็บเก่า + หมึกเข้มรายใบ + ตัวหนา 800**
        ใบ 2/3/4 ผ่าน AA ทั้งสองปลาย (4.94-10.93)
        ใบ 1 ผ่านเฉพาะต้นแถบ (5.52) ปลายแถบได้ 2.99 — เป็นข้อจำกัดของไล่เฉด
        `#1591DC → #2C5EAD` เอง ปลายมันเข้มเกินกว่าหมึกเข้มจะเกาะได้
        ถ้าจะให้ใบ 1 ผ่านทั้งใบต้องเลือกอย่างใดอย่างหนึ่ง: หมึกขาว (ได้ 6.34
        ที่ปลาย แต่ต้นแถบเหลือ 3.43) หรือเปลี่ยนไล่เฉดใบ 1 ให้สว่างทั้งใบ    */

   **ตำแหน่งเป็นคนกำหนดสี ไม่ใช่ความหมาย** — กฎ nth-child ที่นี่มี specificity
   (0,3,0)/(0,2,1) จึงชนะคลาสความหมาย `.s-*` (0,1,0) ที่ PHP ยังส่งมาเหมือนเดิม
   คลาสพวกนั้นไม่ได้หายไปไหน แค่ไม่มีผลเมื่ออยู่ในแถวการ์ด (ใช้ได้กับการ์ด
   เดี่ยวนอกกริดตามเดิม) · **แลกไปคือใบ "กลุ่มเสี่ยง" ไม่ได้เป็นสีแดงอีกแล้ว**
   ความหมายอยู่ที่ข้อความใต้ตัวเลขแทน — เป็นการแลกที่เจ้าของตัดสินใจแล้ว
   ไม่ใช่ความพลาด (เคยเป็นแบบนี้มาก่อนและถูกบันทึกไว้ใน CLAUDE.md)

   ใบที่ 3 เป็น "ฟ้าอ่อน หมึกเข้ม" และใบที่ 4 เป็น "ทอง หมึกเกือบดำ" —
   สองใบนี้คือจังหวะสายตาของแถว ถ้าทำให้เข้มทั้งสี่ใบจะกลายเป็นแผงทึบ        */
.rd-stat, .stat-card {
  --card-a: var(--sk); --card-b: var(--sk-dk); --card-ink: #fff;
}
/* ตำแหน่งในแถว — ทั้ง .rd-stats (หน้าที่จัดวางใหม่) และ .row ของ statCards() */
.rd-stats > .rd-stat:nth-child(4n+1),
.row > [class*="col-"]:nth-child(4n+1) .stat-card { --card-a: var(--sk);      --card-b: var(--sk-dk);   --card-ink: #fff; }
.rd-stats > .rd-stat:nth-child(4n+2),
.row > [class*="col-"]:nth-child(4n+2) .stat-card { --card-a: var(--sk-lt);      --card-b: var(--sk);         --card-ink: #fff; }
.rd-stats > .rd-stat:nth-child(4n+3),
.row > [class*="col-"]:nth-child(4n+3) .stat-card { --card-a: var(--warm-orange); --card-b: var(--warm-red);  --card-ink: #fff; }
.rd-stats > .rd-stat:nth-child(4n+4),
.row > [class*="col-"]:nth-child(4n+4) .stat-card { --card-a: var(--warm-yellow); --card-b: var(--warm-amber); --card-ink: #3A1D00; }

/* คลาสความหมายยังใช้ได้กับการ์ดเดี่ยวที่อยู่นอกแถว (เช่นในหน้าผลสอบ)
   ตระกูลสีเต็มชุดยังอยู่ที่ token --f-* สำหรับชิป/ป้าย/สีประจำหน้า          */
.s-indigo { --card-a: var(--f-indigo-dk); --card-b: var(--f-indigo); --card-ink: #fff; }
.s-sky    { --card-a: var(--f-sky-dk);    --card-b: var(--f-sky);    --card-ink: #fff; }
.s-teal   { --card-a: var(--f-teal-dk);   --card-b: var(--f-teal);   --card-ink: #fff; }
.s-lime   { --card-a: var(--f-lime-dk);   --card-b: var(--f-lime);   --card-ink: #fff; }
.s-orange { --card-a: var(--f-orange-dk); --card-b: var(--f-orange); --card-ink: #fff; }
.s-rose   { --card-a: var(--f-rose-dk);   --card-b: var(--f-rose);   --card-ink: #fff; }
.s-red    { --card-a: var(--f-red-dk);    --card-b: var(--f-red);    --card-ink: #fff; }
.s-violet { --card-a: var(--f-violet-dk); --card-b: var(--f-violet); --card-ink: #fff; }
.s-cyan   { --card-a: var(--f-cyan-dk);   --card-b: var(--f-cyan);   --card-ink: #fff; }
.s-amber  { --card-a: var(--f-amber);     --card-b: #D99A10;         --card-ink: #3a2900; }
.s-gold   { --card-a: var(--f-amber);     --card-b: #D99A10;         --card-ink: #3a2900; }
.s-green  { --card-a: var(--f-teal-dk);   --card-b: var(--f-teal);   --card-ink: #fff; }
/* ชื่อตำแหน่งเก่าที่ยังเขียนตรงใน markup — ให้ผลเหมือนอยู่ในแถวปกติ */
.stat-1 { --card-a: var(--sk);          --card-b: var(--sk-dk);      --card-ink: #fff; }
.stat-2 { --card-a: var(--sk-lt);       --card-b: var(--sk);         --card-ink: #fff; }
.stat-3 { --card-a: var(--warm-orange); --card-b: var(--warm-red);   --card-ink: #fff; }
.stat-4 { --card-a: var(--warm-yellow); --card-b: var(--warm-amber); --card-ink: #3A1D00; }

/* กฎวาดจริง — มีชุดเดียวสำหรับทั้ง .rd-stat และ .stat-card
   ต้องเขียนคู่ `.card.stat-card` ด้วย เพราะ style.css มี `html.dark .card`
   ที่ (0,2,1) ชนะ `.stat-card` (0,1,0)                                      */
.rd-stat, .stat-card, .card.stat-card,
html.dark .rd-stat, html.dark .card.stat-card {
  background: linear-gradient(135deg, var(--card-a), var(--card-b));
  border-color: transparent;
  color: var(--card-ink);
}
/* โหมดมืด: หรี่ปลายเข้มลงหนึ่งสเต็ปไม่ให้เป็นแผงไฟกลางจอมืด แต่ไม่พลิกบทบาทสี */
html.dark .rd-stat, html.dark .card.stat-card {
  background: linear-gradient(135deg, color-mix(in srgb, var(--card-a) 86%, #000), var(--card-b));
}

/* แถบสีซ้ายไม่ต้องแล้ว — ทั้งใบเป็นสีของมันเอง */
.rd-stat::before { display: none; }

/* ทุกอย่างในใบเดินตาม --card-ink ใบเดียว จะได้ไม่ต้องเขียนซ้ำสิบชุด
   **ห้ามเว้นตัวใดตัวหนึ่งไว้** — พื้นมาจากกฎหนึ่งแต่หมึกมาจากอีกกฎหนึ่งเมื่อไหร่
   จะได้การ์ดพื้นน้ำเงินหมึกน้ำตาล (1.5 อ่านไม่ออก) ซึ่งไม่มีอะไรฟ้อง เพราะ
   ทั้งสองกฎถูกของมันเอง (พลาดมาแล้วสองรอบสมัยชุด VIVID)                    */
.rd-stat .rd-stat-val,
.rd-stat .rd-stat-label,
.rd-stat .rd-stat-val small,
.rd-stat .rd-n-gold, .rd-stat .rd-n-red,
.rd-stat .rd-n-green, .rd-stat .rd-n-mute,
.stat-card .stat-val, .stat-card .stat-label,
html.dark .card.stat-card .stat-val, html.dark .card.stat-card .stat-label { color: var(--card-ink); }

/* ไม่ใช้ทั้งเงา ขอบ และตัวหนา — เจ้าของลองมาครบทั้งสามแล้วไม่เอาสักอัน
   (13 ส.ค. 2569) ตัวหนา 800 ทำให้ฟอนต์ไทยเส้นชิดกันจนอ่านยากขึ้นแทนที่จะง่ายขึ้น
   โดยเฉพาะสระบนล่างที่ไปติดกับตัวอักษร · ป้ายกลับไปใช้น้ำหนักปกติของมัน
   (.rd-stat-label 600 · .rd-stat-note 700 · ตัวเลขได้ 700 จากกฎฐาน)
   ที่ทำให้อ่านออกคือ "สี" ล้วน ๆ ไม่ใช่ลูกเล่นบนตัวอักษร                    */
.rd-stat .rd-stat-val, .rd-stat .rd-stat-label, .rd-stat .rd-stat-val small,
.rd-stat .rd-stat-note,
.stat-card .stat-val, .stat-card .stat-label {
  -webkit-text-stroke: 0;
  text-shadow: none;
}
/* ป้ายกำกับเป็นตัวเล็ก (0.82rem) จึงต้องผ่าน AA 4.5 ไม่ใช่ 3.0 ของตัวใหญ่ —
   จึงไม่หรี่ด้วย opacity เลย (ที่ 82% มันตกทุกใบที่พื้นสว่าง)              */
/* **ต้องเขียน `.rd-stat.rd-stat` ซ้ำสองครั้ง** — `.rd-stat .rd-stat-ic` เป็น
   (0,2,0) ซึ่งแพ้ `.rd-stat.s-red .rd-stat-ic` (0,3,0) ที่บรรทัด ~785
   กล่องไอคอนของใบที่ PHP ติดคลาสความหมายมาจึงยังใช้สีชุดเก่า (พื้นขาวหมึกแดง)
   ทั้งที่ตัวการ์ดเปลี่ยนสีตามตำแหน่งไปแล้ว = ไอคอนคนละโทนกับใบที่มันอยู่
   เขียนซ้ำให้เป็น (0,3,0) เท่ากัน แล้วชนะด้วยลำดับไฟล์ที่อยู่ท้ายกว่า        */
/* ตั้งแต่ชุด FIGURE RAIL (ท้ายไฟล์) ใบไม่ได้เป็นพื้นสีอีกแล้ว หมึกของไอคอนจึง
   **ห้ามผูกกับ `--card-ink`** — `--card-ink` คือ "หมึกที่อ่านออกบนพื้นสีของใบ"
   ซึ่งตอนนี้เป็น #fff สำหรับใบที่ 1-3 → ขาวบนการ์ดขาว = คอนทราสต์ 1.0
   ไอคอนหายไปทั้งสามใบโดยไม่มี error ให้เห็น (เจอจากการวัด computed style
   ไม่ใช่จากการดูภาพ — ที่ขนาด 16px ตาแยกไม่ออกว่า "ไม่มีไอคอน" กับ "ไอคอนขาว")
   ใช้ `--card-b` (สีตระกูลของใบ) แทน แล้วกล่อง wash ก็ไม่ต้องมี             */
.rd-stat.rd-stat .rd-stat-ic,
html.dark .rd-stat.rd-stat .rd-stat-ic { background: none; color: var(--card-mark); }
a.rd-stat:hover { color: var(--text-main); }
/* html.dark a (0,2,1) ทับ .rd-stat (0,1,0) — การ์ดที่เป็นลิงก์เลยกลายเป็นฟ้าอ่อน
   ทั้งใบในโหมดมืด ต้องทวงคืนด้วยกฎที่เสมอกัน (0,2,1) แล้วชนะด้วยลำดับไฟล์   */
html.dark a.rd-stat, html.dark a.stat-card { color: var(--card-ink); }
.stat-card { border: 1px solid rgba(0,0,0,.10); }
/* ไอคอนตกแต่งหรี่ได้ถึง .38 เพราะเป็นรูปทึบขนาดใหญ่ ไม่ใช่ตัวหนังสือที่ต้องอ่าน */
.stat-card .stat-icon, html.dark .card.stat-card .stat-icon { color: var(--card-ink); opacity: .38; }

/* ── การ์ดรายวิชาชุดวนอัตโนมัติเดิม (.rd-c1..c6) ────────────────────────────
   ไม่มีหน้าไหนใช้แล้ว (ระบบจริงใช้ .rd-cp-* จาก courseColorClass()) แต่ยังเก็บ
   ไว้เผื่อ markup เก่า จึงต้องเป็นสีสด+หมึกขาวชุดเดียวกัน ไม่งั้นวันที่มีคน
   หยิบไปใช้จะได้การ์ดพาสเทลหมึกขาว = อ่านไม่ออก                             */
.rd-c1 { background: none; --rd-pb: #C7DBF0; --rd-ac: #1B4472; --rd-pb-dk: #22548A; }
.rd-c2 { background: none; --rd-pb: #C4E1F3; --rd-ac: #075985; --rd-pb-dk: #075985; }
.rd-c3 { background: none; --rd-pb: #C1E4DF; --rd-ac: #115E59; --rd-pb-dk: #115E59; }
.rd-c4 { background: none; --rd-pb: #F9D0BC; --rd-ac: #9A3412; --rd-pb-dk: #9A3412; }
.rd-c5 { background: none; --rd-pb: #C9E7D1; --rd-ac: #14532D; --rd-pb-dk: #166534; }
.rd-c6 { background: none; --rd-pb: #F2DFB2; --rd-ac: #713F12; --rd-pb-dk: #854D0E; }

/* ════════════════════════════════════════════════════════════════════════════
   VIVID 2569 — โครงหน้าเป็นสีสด (แทนที่บล็อก PASTEL 2569 เดิม)
   ────────────────────────────────────────────────────────────────────────────
   หลักการกลับด้านจากพาสเทล: **พื้นสด หมึกขาว** ทุกค่าที่ใช้เป็นพื้นใต้ตัวหนังสือ
   ในบล็อกนี้ผ่าน AA 4.5 กับสีขาวแล้ว (คำนวณจากค่า HEX ตรง ๆ ไม่ใช่วัดในเบราว์เซอร์)

   **ห้ามทำให้พื้นสว่างขึ้นโดยไม่เปลี่ยนหมึก** — นี่คือกับดักเดิมที่ทำให้ต้องมา
   เขียนบล็อกนี้ทั้งบล็อก: `--sk` (#1591DC) ดูสดตาแต่ขาวทับได้แค่ 3.43
   สีที่ใช้เป็น "พื้นใต้ตัวขาว" จึงต้องหยุดที่ `--sk-dk` (#2C5EAD = 6.34) เสมอ
   ห้ามไล่ gradient ไปจบที่ `--sk`

   **ไอคอนต้องตามหมึกเสมอ** — `<i class="bi">` วาดด้วย ::before ไม่มี text node
   ตัวตรวจ contrast ที่ไล่เฉพาะ element ที่มีข้อความจะข้ามมันทั้งหมด จึงต้อง
   ประกาศ color ให้ไอคอนคู่กับตัวหนังสือทุกกฎ

   แถบข้างยังเป็นสีขาวตามค่า settings.sidebar_color = #ffffff (= "ไม่ override")
   ถ้าแอดมินอยากได้แถบข้างสีน้ำเงินก็ตั้งค่าใน admin_settings ได้ header.php
   จะจัดหมึกขาวให้เอง — ไม่ต้องแก้ไฟล์นี้
════════════════════════════════════════════════════════════════════════════ */

/* ── แถบบน: พื้นขาว หมึกเข้ม (คืนโทนเดิม 12 ส.ค. 2569) ─────────────────────
   เจ้าของสั่งกลับไปใช้โทนของเว็บก่อน REDESIGN (`storage/ui_backup_pre_redesign/`)
   ซึ่ง "สบายตากว่า" — แถบบนที่นั่นเป็นพื้นขาวเหมือนการ์ด ไม่ใช่แถบน้ำเงินทึบ
   จึงเหลือแค่กฎเงาบาง ๆ · กฎฐานที่บรรทัด 213 เป็นคนวาดพื้นให้อยู่แล้ว

   **ที่ต้องเหลือไว้คือกฎกัน "ของที่มีพื้นเป็นของตัวเอง"** — ปุ่ม ☰ กับปุ่มโปรไฟล์
   มีพื้นฝ้าของตัวเองบนแถบ ถ้าไม่กำหนดหมึกให้ชัดมันจะไปหยิบค่าจากที่อื่น
   (นี่คือกับดักที่ทำให้เกิดบั๊ก #10 #11 และลูกศรโปรไฟล์ขาวบนขาวมาแล้ว)      */
html:not(.dark) .app-topbar {
  box-shadow: 0 2px 10px rgba(3,105,161,.06);
}
html:not(.dark) .app-topbar .brand-name { color: var(--sk-ink) !important; }
html:not(.dark) .app-topbar a:not(.dropdown-menu *),
html:not(.dark) .app-topbar i:not(.dropdown-menu *) { color: var(--sk-dk); }
html:not(.dark) .app-topbar .notif-btn svg { fill: var(--sk-dk); color: var(--sk-dk); }
html:not(.dark) .app-topbar .sidebar-toggle { background: transparent; color: var(--sk-dk); }
html:not(.dark) .app-topbar .profile-chip,
html:not(.dark) .app-topbar .profile-chip i { color: var(--sk-ink) !important; }
html:not(.dark) .app-topbar .sidebar-toggle:hover,
html:not(.dark) .app-topbar .notif-btn:hover { background: var(--sk-wash); }
/* บั๊กเดียวกับที่เคยแก้ไว้ใน dark mode ที่บรรทัด 530 แต่กลับมาใหม่ฝั่ง light —
   กฎข้างบนเปลี่ยนพื้นตอน hover เป็น "อ่อน" (--sk-wash) แต่กระดิ่งยังเป็นสีขาว
   จากกฎฐาน (.notif-btn:hover .bell path / .has-unread:hover) ซึ่งออกแบบมาให้
   ตัดกับพื้นเข้มเดิม ขาวบนอ่อนแทบไม่เห็น = กระดิ่ง "จางหายไป" ตอนชี้เมาส์
   ต้องคุมทั้งสองสถานะ (มี/ไม่มีแจ้งเตือนค้าง) ในกฎเดียว — specificity (0,5,2)
   ชนะทั้ง .notif-btn:hover (0,3,1) และ .has-unread:hover (0,4,1) */
html:not(.dark) .app-topbar .notif-btn:hover .bell path,
html:not(.dark) .app-topbar .notif-btn:focus-visible .bell path { fill: var(--sk-deep); }

/* ── หัวตาราง: พื้นอ่อน หมึกน้ำเงินเข้ม (คืนโทนเดิม) ───────────────────────
   ของเดิมก่อน REDESIGN คือหัวตารางสีอ่อนแทบขาว ตัวหนังสือน้ำเงินเข้ม ซึ่งเป็น
   สิ่งที่ทำให้ตารางยาว ๆ อ่านสบายตา — แถบน้ำเงินทึบเต็มความกว้างที่ใช้อยู่
   ก่อนหน้านี้ทำให้ทุกหน้าที่มีตารางดูหนัก
   กฎฐานที่บรรทัด 230/231 วาดแบบนั้นอยู่แล้ว บล็อกนี้จึงเหลือเฉพาะสิ่งที่
   กฎฐานไม่ได้ครอบ: ไอคอนและตัวเล็กในหัวตารางต้องเดินตามหมึกเดียวกัน       */
.table-dark-custom thead th .bi { color: var(--sk-dk); }
html.dark .table-dark-custom thead th .bi { color: var(--sk-lt); }
/* คะแนนเต็ม "(10.00)" ที่ซ้อนอยู่ในหัวตาราง — ห้ามหรี่ด้วย opacity เพราะเป็น
   ตัวเลขที่ครูต้องอ่านจริง (บั๊ก #6/#8) · ต้องมี !important เพราะ Bootstrap
   ประกาศ .text-muted เป็น !important ไว้เอง                                */
.table-dark-custom thead th .text-muted,
.table-dark-custom thead th small { color: var(--sk-deep) !important; }
html.dark .table-dark-custom thead th .text-muted,
html.dark .table-dark-custom thead th small { color: var(--sk-lt) !important; }
/* หัวคอลัมน์แรกของสมุดคะแนน — พื้นเข้มกว่าคอลัมน์อื่นเล็กน้อยเพื่อให้เห็นว่า
   เป็นคอลัมน์ตรึง แต่ยังอยู่ในโทนอ่อนเดียวกัน หมึกจึงเป็นน้ำเงินเข้มเหมือนกัน */
.gradebook-table thead th:first-child {
  background: color-mix(in srgb, var(--sk) 15%, var(--bg-card)) !important;
}

/* ปุ่มแบบลิงก์: --face เป็น transparent พื้นที่โผล่ตอน hover จึงเป็นพื้นหน้าเว็บ
   แต่ชั้น .letters ถูกบังคับเป็นขาว = ขาวบนพื้นอ่อน มองไม่เห็น (บั๊กเดียวกับ
   ปุ่มในการ์ดรายวิชา คนละที่) ปุ่มพวกนี้ไม่มีพื้นของตัวเอง จึงต้องใช้ --ink   */
.btn.btn-53.btn-link > .letters,
.btn.btn-53.btn-link > .letters > .bi { color: var(--ink); }

/* ── แถวที่จัดการไปแล้ว (ลงทะเบียนแล้ว · กู้คืนแล้ว · ประกาศหมดอายุ) ────────
   เดิมใช้ .opacity-50 ทั้งแถว ซึ่งลดคอนทราสต์ของ **ทุกอย่าง** ในแถวพร้อมกัน
   ชื่อนักเรียนเหลือ 2.79 เลขประจำตัว 2.10 และป้าย "ลงแล้ว" 2.55 — อ่านไม่ออก
   ทั้งแถวเพียงเพื่อสื่อว่า "อันนี้ทำแล้ว" · ใช้พื้นหลังจาง ๆ แทน หมึกคงเดิม
   ข้อมูลยังอ่านได้ครบและยังแยกออกจากแถวปกติชัดเจน                          */
.sg-row-done > td, .sg-row-done > th {
  background: color-mix(in srgb, var(--text-muted) 9%, transparent);
}
html.dark .sg-row-done > td, html.dark .sg-row-done > th {
  background: rgba(255,255,255,.05);
}
/* แบบที่ไม่ใช่แถวตาราง (รายการติ๊กในหน้า admin_sections) */
.sg-done-item { background: color-mix(in srgb, var(--text-muted) 9%, transparent); border-radius: var(--r-xs); }
html.dark .sg-done-item { background: rgba(255,255,255,.05); }

/* ── โหมดมืด: พื้นที่เป็นเฉดสว่างต้องสลับไปใช้หมึกเข้ม ─────────────────────
   html.dark เปลี่ยน --red-ink เป็นปลายสว่าง (#FF8888) ซึ่งถูกสำหรับ "หมึกบน
   พื้นเข้ม" แต่ป้าย .badge.bg-danger เอาค่านั้นไปเป็น "พื้น" แล้วยังทับด้วย
   ตัวขาว = 2.30 · ป้ายพื้นสว่างต้องใช้ตัวหนังสือเข้มเสมอ                    */
html.dark .badge.bg-danger { color: #3F0F0F !important; }
/* ไอคอนในฮีโร่โหมดมืด: --sk-lt บนน้ำเงินเข้มได้ 2.88 (ต้องการ 3.0)
   ต้องเจาะจงถึง `h3 > .bi:first-child` — กฎกลางของหัวเรื่องเขียนไว้แบบนั้น
   ซึ่งได้ (0,3,2) ชนะ `html.dark .dash-hero i` (0,2,1) แม้จะอยู่ท้ายไฟล์กว่า */
html.dark .dash-hero i,
html.dark .dash-hero h1 > .bi:first-child,
html.dark .dash-hero h2 > .bi:first-child,
html.dark .dash-hero h3 > .bi:first-child { color: #A9D8FB; }

/* ── พรีวิวสีการ์ดรายวิชา: บอกว่า "เป็นตัวอย่าง" ด้วยป้าย ไม่ใช่การหรี่ ──────
   เดิมหรี่ทั้งกล่องเหลือ opacity .55 ตอนเลือก "อัตโนมัติ" ทำให้ทุกข้อความใน
   พรีวิวเหลือ 2.07-2.88 — ซึ่งย้อนแย้งกับหน้าที่ของพรีวิวเอง ที่มีไว้ให้ครู
   ดูว่า "ตัวหนังสือบนการ์ดจะอ่านออกไหม"                                     */
.cc-pv-box.cc-pv-sample { position: relative; border-style: dashed; }
.cc-pv-box.cc-pv-sample::after {
  content: "ตัวอย่าง"; position: absolute; right: 8px; top: 6px;
  font-size: .64rem; font-weight: 700; letter-spacing: .02em;
  color: var(--text-main); background: var(--bg-card);
  border: 1px solid var(--border-color); padding: 1px 7px; border-radius: var(--r-pill);
}

/* ════════════════════════════════════════════════════════════════════════════
   .btn-push — ปุ่ม 3D กดได้ (ดัดแปลงจาก Uiverse.io by Voxybuns)
   ────────────────────────────────────────────────────────────────────────────
   **เป็น opt-in เท่านั้น** ใส่คลาสเองรายปุ่ม ไม่มีอะไรแปลงให้อัตโนมัติ
   ใช้กับ "ปุ่มหลักหนึ่งเดียวของหน้า" — เข้าสู่ระบบ · ดูผลการเรียน · แลกโค้ด ·
   เริ่ม/ส่งข้อสอบ · CTA หน้าแรก ที่เหลือทั้งเว็บยังเป็น .btn-53 เหมือนเดิม

   **ห้ามเอาไปใส่ในแถวตาราง · .input-group · .btn-group · ปุ่มไอคอนล้วน**
   มันมีฐานทึบสูง 5px ใต้ปุ่ม จึงกินพื้นที่แนวตั้งเกินปุ่มปกติ ในแถวตารางจะดัน
   แถวสูงขึ้นทั้งตาราง และใน .input-group จะไม่ชิดกับช่องกรอกข้างมัน

   ต้นฉบับซ้อน <span class="button_top"> ไว้ข้างในแล้วเลื่อน span
   ที่นี่ใช้ `box-shadow: 0 Npx 0` เป็นฐานแทน ได้ภาพเดียวกันแต่ **ไม่ต้องมี
   markup เพิ่ม** ซึ่งจำเป็น เพราะปุ่มในระบบนี้เป็นทั้ง <button> และ <a>
   และ main.js ก็แปลง innerHTML ของปุ่มอยู่แล้ว (การใส่ span จะชนกัน)

   main.js ข้าม .btn-push ในตัวสร้าง .btn-53 — ถ้าลบบรรทัดนั้นออก ปุ่มจะได้
   สองเอฟเฟกต์ซ้อนกัน แล้ว `.original` ที่เลื่อนลงตอน hover จะบังตัวหนังสือ

   คอนทราสต์: ทุก --push-face ที่ประกาศไว้ล่างนี้ผ่าน AA 4.5 กับหมึกของมัน
   **ห้ามตั้ง --push-face เป็น var(--btn)** — ค่าเริ่มต้นของโรงเรียนคือ #1591DC
   ซึ่งขาวทับได้แค่ 3.43 และแอดมินเปลี่ยนค่านั้นได้ตลอด ปุ่มจะอ่านไม่ออกเงียบ ๆ
════════════════════════════════════════════════════════════════════════════ */
.btn.btn-push {
  /* ผูกกับสีปุ่มที่แอดมินตั้งได้แล้ว — คอมเมนต์เดิมห้ามไว้เพราะตอนนั้น --btn ยัง
     เป็นค่าดิบ (ค่าเริ่มต้น #1591DC ขาวทับได้ 3.43) ตอนนี้ --btn-face ผ่าน 4.6
     มาแล้วเสมอ และ --btn-on ก็พลิกเป็นหมึกเข้มเองถ้าแอดมินเลือกสีอ่อน       */
  --push-face: var(--btn-face, #336DCA);
  --push-ink:  var(--btn-on, #fff);
  --push-edge: color-mix(in srgb, var(--push-face) 52%, #000);
  --push-lift: 5px;

  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  background: var(--push-face) !important;
  color: var(--push-ink) !important;
  border: 2px solid var(--push-edge) !important;
  border-radius: 12px;
  padding: .68rem 1.55rem;
  font-family: var(--font-display);
  font-weight: 800; font-size: 1rem; letter-spacing: -.006em;
  line-height: 1.35;
  box-shadow: 0 var(--push-lift) 0 var(--push-edge);
  /* ฐานทึบยื่นลงมาจากขอบปุ่ม — ถ้าไม่กันที่ไว้ มันจะไปทับสิ่งที่อยู่ใต้ปุ่ม */
  margin-bottom: var(--push-lift);
  transform: translateY(0);
  transition: transform .1s ease, box-shadow .1s ease, filter .15s ease;
}
.btn.btn-push:hover {
  filter: brightness(1.06);
  transform: translateY(-2px);
  box-shadow: 0 calc(var(--push-lift) + 2px) 0 var(--push-edge);
  color: var(--push-ink) !important;
}
/* กดแล้วปุ่มจมลงไปนั่งบนฐานพอดี — ฐานหายไป ปุ่มเลื่อนลงเท่าความสูงฐาน */
.btn.btn-push:active { transform: translateY(var(--push-lift)); box-shadow: 0 0 0 var(--push-edge); }
.btn.btn-push:focus-visible { outline: 3px solid var(--push-face); outline-offset: 3px; }
.btn.btn-push:disabled, .btn.btn-push.disabled {
  filter: grayscale(.5); opacity: .65; transform: none;
  box-shadow: 0 var(--push-lift) 0 var(--push-edge);
}
/* html.dark a (0,2,1) ทับ .btn-push (0,2,0) — ปุ่มที่เป็น <a> จะกลายเป็นฟ้าอ่อน
   ทับพื้นของตัวเองในโหมดมืด (กับดักเดียวกับ .btn-brand ที่ DESIGN.md เตือนไว้) */
html.dark a.btn.btn-push, html.dark a.btn.btn-push:hover { color: var(--push-ink) !important; }

/* ── แบบสี ─────────────────────────────────────────────────────────────────
   ทุกคู่ผ่าน AA: ขาวบนเขียว 5.02 · ขาวบนน้ำเงิน 6.34 · ขาวบนแดง 4.83 ·
   หมึก #422006 บนทอง 8.73 (ทองที่เข้มพอให้ตัวขาวอ่านออกจะไม่ใช่ทองอีกต่อไป) */
.btn.btn-push-green { --push-face: #15823E; }
.btn.btn-push-red   { --push-face: #DC2626; }
.btn.btn-push-gold  { --push-face: #FBBF24; --push-ink: #422006; }
/* แบบพื้นขาว (ใช้บนพื้นสีเข้ม เช่น ฮีโร่หน้าแรก) — ขอบ/ฐานเป็นน้ำเงินเข้ม */
.btn.btn-push-white { --push-face: #fff; --push-ink: #1B3E86; --push-edge: #1B3E86; }
html.dark .btn.btn-push-white { --push-face: #F2F7FC; --push-ink: #16305F; --push-edge: #16305F; }

/* ปุ่มเต็มความกว้าง (ฟอร์มล็อกอิน/ผู้ปกครอง) */
.btn.btn-push.w-100 { width: 100%; }

@media (max-width: 575px) {
  .btn.btn-push { font-size: .95rem; padding: .62rem 1.2rem; }
}
/* คนที่ตั้งค่าลดการเคลื่อนไหวไว้ ยังต้องเห็นว่า "กดแล้ว" — คงการจมลงตอนกดไว้
   ตัดเฉพาะการขยับตอนเอาเมาส์ไปชี้ ซึ่งเป็นการตกแต่งล้วน ๆ                   */
@media (prefers-reduced-motion: reduce) {
  .btn.btn-push { transition: none; }
  .btn.btn-push:hover { transform: none; box-shadow: 0 var(--push-lift) 0 var(--push-edge); }
}


/* ════════════════════════════════════════════════════════════════════════════
   พรีวิวสีในหน้าตั้งค่า (.sgp-*) — มาคู่กับ includes/palette.php
   ────────────────────────────────────────────────────────────────────────────
   **ห้ามแยกสองอย่างนี้ออกจากกัน** การเปิดให้แอดมินเปลี่ยนสีทั้งเว็บโดยไม่มีที่
   ให้ดูก่อนว่าอ่านออกไหม คือการส่งปืนให้แล้วปิดไฟ · ทุกกฎในบล็อกนี้อ่าน token
   จาก inline style ที่ JS ทาไว้บน #sgpStage เท่านั้น จึงไม่กระทบหน้าจริงเลย
   ════════════════════════════════════════════════════════════════════════════ */
.sgp-wrap { border: 1px solid var(--border-color); border-radius: var(--r-lg); padding: 12px; background: var(--bg-card); }
.sgp-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.sgp-title { font-weight: 700; font-size: .86rem; color: var(--text-main); }
.sgp-scores { display: flex; flex-wrap: wrap; gap: 6px; }
.sgp-sc { font-size: .7rem; font-weight: 700; padding: 2px 8px; border-radius: var(--r-pill); display: inline-flex; align-items: center; gap: 4px; }
.sgp-sc.ok { background: color-mix(in srgb, var(--green) 14%, transparent); color: var(--green-ink); }
.sgp-sc.no { background: color-mix(in srgb, var(--red) 16%, transparent); color: var(--red-ink); }
.sgp-warn { display: grid; gap: 6px; margin-bottom: 10px; }
.sgp-warn-row { display: flex; gap: 8px; align-items: flex-start; font-size: .78rem; line-height: 1.45;
  background: color-mix(in srgb, var(--gold) 15%, transparent); color: var(--text-main);
  border-left: 3px solid var(--gold-ink); border-radius: var(--r-xs); padding: 7px 10px; }
.sgp-warn-row i { color: var(--gold-ink); margin-top: 2px; }

/* เวทีพรีวิว — ย่อส่วนของหน้าจริง: แถบบน + แถบข้าง + ฮีโร่ + ตาราง + ปุ่ม + ลิงก์
   เลือกหกอย่างนี้เพราะเป็น "ทุกบทบาทของสี" ที่ระบบมี (พื้นเข้ม-หมึกอ่อน ·
   พื้นอ่อน-หมึกเข้ม · เส้นขอบ · สถานะที่เลือกอยู่) ถ้าหกอย่างนี้อ่านออก
   ที่เหลือทั้งเว็บก็อ่านออก เพราะใช้ token ชุดเดียวกันหมด                    */
.sgp-stage { border: 1px solid var(--border-color); border-radius: var(--r-md); overflow: hidden; background: var(--sgp-page, var(--bg-base)); }
.sgp-bar { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px;
  background: var(--bg-card); border-bottom: 1px solid var(--border-color); color: var(--sk-ink); }
.sgp-brand { font-weight: 800; font-size: .84rem; display: inline-flex; align-items: center; gap: 6px; color: var(--sk-ink); }
.sgp-bar-r { display: inline-flex; gap: 10px; color: var(--sk-dk); }
.sgp-body { display: grid; grid-template-columns: 116px 1fr; min-height: 168px; }
.sgp-side { background: var(--sgp-side, #fff); padding: 8px 6px; display: grid; gap: 4px; align-content: start; }
.sgp-nav { font-size: .72rem; font-weight: 600; padding: 5px 8px; border-radius: var(--r-xs);
  display: flex; align-items: center; gap: 6px; color: var(--sgp-side-ink, var(--sk-ink)); }
.sgp-nav.on { background: var(--brand-dark); color: var(--brand-on); }
.sgp-main { padding: 10px; display: grid; gap: 8px; align-content: start; }
.sgp-hero { border-radius: var(--r-md); padding: 12px 14px; font-weight: 800; font-size: .9rem;
  background: linear-gradient(115deg, var(--brand-deep), var(--brand-dark) 55%, var(--brand-lt));
  color: var(--brand-on); display: grid; gap: 2px; }
.sgp-hero small { font-weight: 500; font-size: .72rem; color: var(--brand-on); opacity: .92; }
.sgp-tb { width: 100%; border-collapse: separate; border-spacing: 0; font-size: .74rem; background: var(--bg-card); border-radius: var(--r-sm); overflow: hidden; }
.sgp-tb th { background: color-mix(in srgb, var(--sk) 7%, var(--bg-card)); color: var(--sk-deep); font-weight: 700; padding: 6px 9px; text-align: left; border-bottom: 2px solid color-mix(in srgb, var(--sk) 32%, transparent); }
.sgp-tb td { padding: 6px 9px; color: var(--text-main); border-top: 1px solid var(--border-color); }
.sgp-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sgp-btn { font-size: .76rem; font-weight: 700; padding: 6px 14px; border-radius: var(--r-sm);
  background: var(--btn-face); color: var(--btn-on); }
.sgp-btn.ghost { background: transparent; color: var(--sk-dk); border: 1.5px solid var(--sk-dk); }
.sgp-link { font-size: .76rem; font-weight: 600; color: var(--sk-dk); text-decoration: underline; }
html.dark .sgp-stage { background: var(--bg-base); }
@media (max-width: 480px) { .sgp-body { grid-template-columns: 1fr; } .sgp-side { display: none; } }

/* ── สีประจำหน้า (--page-accent) ────────────────────────────────────────────
   header.php ใส่คลาส .pg-<key> ให้ <body> ตาม pageAccentKey()
   ใช้แต่พอดี: ไอคอนหัวเรื่อง + แท็บที่เลือกอยู่ **ห้ามลามไปแถบบน/พื้นหน้า**
   ไม่งั้นจะไปชนกับสีโรงเรียนที่แอดมินตั้ง แล้วเว็บดูเหมือนคนละระบบทุกครั้งที่
   เปลี่ยนหน้า ซึ่งตรงข้ามกับที่ต้องการ (จุดประสงค์คือ "รู้ว่าอยู่ตรงไหน"
   ไม่ใช่ "เปลี่ยนธีม")                                                      */
body { --page-accent: var(--f-indigo); --page-accent-ink: var(--f-indigo-ink); --page-accent-soft: var(--f-indigo-soft); }
body.pg-indigo { --page-accent: var(--f-indigo); --page-accent-ink: var(--f-indigo-ink); --page-accent-soft: var(--f-indigo-soft); }
body.pg-sky    { --page-accent: var(--f-sky);    --page-accent-ink: var(--f-sky-ink);    --page-accent-soft: var(--f-sky-soft); }
body.pg-teal   { --page-accent: var(--f-teal);   --page-accent-ink: var(--f-teal-ink);   --page-accent-soft: var(--f-teal-soft); }
body.pg-orange { --page-accent: var(--f-orange); --page-accent-ink: var(--f-orange-ink); --page-accent-soft: var(--f-orange-soft); }
body.pg-red    { --page-accent: var(--f-red);    --page-accent-ink: var(--f-red-ink);    --page-accent-soft: var(--f-red-soft); }
body.pg-violet { --page-accent: var(--f-violet); --page-accent-ink: var(--f-violet-ink); --page-accent-soft: var(--f-violet-soft); }
body.pg-cyan   { --page-accent: var(--f-cyan);   --page-accent-ink: var(--f-cyan-ink);   --page-accent-soft: var(--f-cyan-soft); }
body.pg-lime   { --page-accent: var(--f-lime);   --page-accent-ink: var(--f-lime-ink);   --page-accent-soft: var(--f-lime-soft); }
body.pg-rose   { --page-accent: var(--f-rose);   --page-accent-ink: var(--f-rose-ink);   --page-accent-soft: var(--f-rose-soft); }
body.pg-amber  { --page-accent: var(--f-amber);  --page-accent-ink: var(--f-amber-ink);  --page-accent-soft: var(--f-amber-soft); }

/* ไอคอนหัวเรื่องของหน้า — ต้องเจาะจงถึง `> .bi:first-child` ให้เท่ากับกฎกลาง
   ที่บรรทัด 596 (0,1,2 + type) ไม่งั้นแพ้ · ในฮีโร่/การ์ดสีเข้มยังใช้ inherit
   ตามเดิม เพราะที่นั่นหมึกต้องตามพื้นการ์ด ไม่ใช่ตามสีประจำหน้า            */
.page-inner h1 > .bi:first-child, .page-inner h2 > .bi:first-child,
.page-inner h3 > .bi:first-child, .page-inner h4 > .bi:first-child { color: var(--page-accent-ink); }
html.dark .page-inner h1 > .bi:first-child, html.dark .page-inner h2 > .bi:first-child,
html.dark .page-inner h3 > .bi:first-child, html.dark .page-inner h4 > .bi:first-child { color: var(--page-accent); }
/* ในฮีโร่/การ์ดที่มีพื้นของตัวเอง หมึกต้องตามพื้น ไม่ใช่ตามสีประจำหน้า
   **ต้องเขียนคู่ html.dark ด้วย** — กฎโหมดมืดข้างบนมี type selector `html` เพิ่มมา
   หนึ่งตัว (0,4,2) จึงชนะกฎยกเว้นที่ไม่มี (0,4,1) ทั้งที่อยู่หลังในไฟล์
   ผลคือไอคอนในฮีโร่โหมดมืดได้สีประจำหน้าไปทาบนพื้นน้ำเงิน = แดงบนน้ำเงิน 1.14
   (วัดเจอตอนกวาดจอแคบ — เป็นของที่ผมทำพังเองตอนใส่สีประจำหน้า)               */
.page-inner .dash-hero h1 > .bi:first-child, .page-inner .dash-hero h2 > .bi:first-child,
.page-inner .dash-hero h3 > .bi:first-child, .page-inner .card-header > .bi:first-child,
.page-inner .stat-card .bi, .page-inner .rd-stat .bi,
html.dark .page-inner .dash-hero h1 > .bi:first-child,
html.dark .page-inner .dash-hero h2 > .bi:first-child,
html.dark .page-inner .dash-hero h3 > .bi:first-child,
html.dark .page-inner .card-header > .bi:first-child,
html.dark .page-inner .stat-card .bi, html.dark .page-inner .rd-stat .bi { color: inherit; }
/* แท็บที่เลือกอยู่ในหน้ารายวิชา */
.rd-subnav .rd-tab.active { border-bottom-color: var(--page-accent); color: var(--page-accent-ink); }
html.dark .rd-subnav .rd-tab.active { color: var(--page-accent); }

/* ── hover ในโหมดมืด: พื้นต้องไม่สว่างขึ้น ─────────────────────────────────
   `--sk-wash` เป็นสีฟ้าอ่อนและ **ไม่มีค่าสำหรับโหมดมืดโดยตั้งใจ** (ใช้เป็นชิป
   สีอ่อนหมึกเข้มที่ลอยบนการ์ดมืด 22 จุด) แต่กฎ :hover ที่เอามันไปเป็นพื้นของ
   ปุ่ม/แถวบนพื้นเข้มจะกลายเป็น **พื้นสว่างใต้หมึกขาว = อ่านไม่ออกทันทีที่ชี้**
   อาการชัดสุด: ปุ่มโปรไฟล์ในโหมดมืด ชี้แล้วเม็ดยาเป็นขาว ตัวอักษรขาวหายทั้งอัน
   (วัดได้ 1.14) — เจอจากการกวาด "สถานะที่ต้องคลิก/ชี้" ซึ่งไม่เคยตรวจมาก่อน
   จึงไม่มีใครเห็นมาตลอด เพราะการกวาดหน้าเปล่า ๆ ไม่มีวันเจอสถานะนี้           */
html.dark .profile-chip:hover,
html.dark .app-topbar .profile-chip:hover,
html.dark .sidebar-toggle:hover,
html.dark .rd-row:hover,
html.dark .ecal-ag-item:hover,
html.dark .rd-panel-ft:hover { background: rgba(255,255,255,.12); color: var(--text-main); }
html.dark .profile-chip:hover, html.dark .app-topbar .profile-chip:hover,
html.dark .profile-chip:hover i { color: #fff; }

/* ── สามจุดที่เหลือจากการกวาดจอแคบ + สถานะที่ต้องคลิก (12 ส.ค. 2569) ────────
   1) ชิปชั้น/ห้องที่ติ๊กแล้วในโหมดมืด — `theme.css:1097` พลิก **หมึก** เป็น
      `--sk-lt` แต่ **พื้นยังเป็น `--sk-wash` ซึ่งสว่าง** = สว่างบนสว่าง 1.93
      เป็นกับดัก "พลิกแต่หมึกไม่ได้ ต้องพลิกพื้นด้วย" ที่ CLAUDE.md เตือนไว้
      สำหรับ `.rd-ccode`/`.rd-t-blue` — ตัวนี้มีรูปแบบเดียวกันแต่หลุดไป      */
html.dark .chk-grid-item:hover,
html.dark .chk-grid-item.on,
html.dark .chk-grid-item:has(.form-check-input:checked) {
  background: color-mix(in srgb, var(--sk) 22%, var(--bg-card));
  color: var(--sk-lt); border-color: var(--sk-dk);
}
/* 2) ลูกศร "กดได้" มุมการ์ดสถิติในหน้างาน — `--text-muted` หรี่ .55 เหลือ 1.68
      ซึ่งแปลว่าเครื่องหมายบอกว่า "การ์ดนี้กดได้" มองไม่เห็นเลย · การ์ดยังกดได้
      อยู่ก็จริง แต่คนที่ไม่รู้มาก่อนจะไม่รู้ว่ากดได้ ซึ่งคือหน้าที่เดียวของมัน  */
.cw-stat-go { opacity: .85; }
/* 3) ไอคอน "ไม่มีการแจ้งเตือน" ในกล่องกระดิ่ง — หรี่ .5 เหลือ 2.12
      เป็นไอคอนขนาดใหญ่ที่ทำหน้าที่สื่อสารสถานะว่าง ไม่ใช่ของประดับล้วน       */
.notif-empty i { opacity: .72; }

/* ── โหมดมืด: ไอคอนบนแถบบนต้องเป็นปลายสว่าง ────────────────────────────────
   `.sidebar-toggle` และหัวกล่องแจ้งเตือนใช้ `--sk-dk` (#2C5EAD) ซึ่งเป็นค่าที่
   ออกแบบมาสำหรับ "พื้นขาว" · บนแถบบนของโหมดมืดมันคือกรมท่าบนกรมท่า = 2.44-2.48
   กฎ VIVID ที่ทาไอคอนแถบบนเป็นสีขาวมีเงื่อนไข `html:not(.dark)` จึงไม่ครอบถึง
   ฝั่งมืดเลย — ช่องว่างนี้อยู่มาตั้งแต่ต้นและไม่มีใครเห็นเพราะไม่เคยตรวจโหมดมืด
   บนจอแคบ (ปุ่ม ☰ ซ่อนที่จอกว้าง)                                            */
html.dark .app-topbar .sidebar-toggle,
html.dark .app-topbar .sidebar-toggle i,
html.dark .notif-head i,
html.dark .notif-head-a { color: var(--sk-lt); }
html.dark .notif-head-a:hover { color: #fff; }

/* ── สองจุดจากการกวาดจอกว้าง + สถานะที่ต้องคลิก ────────────────────────────
   1) ปุ่มลบเกณฑ์เกรดใน `course_edit` — `.btn-outline-danger` ใช้ `var(--red)`
      (#FF5A5A) เป็น **หมึกบนพื้นขาว** ได้ 2.10 · ค่านั้นถูกกำหนดให้ตรงกับเว็บเก่า
      ในฐานะ "พื้น" (CLAUDE.md บอกไว้ว่ายอมรับ 3.06 สำหรับขาวบนแดง) แต่เอามาเป็น
      หมึกบนขาวคนละเรื่องกัน — ต้องใช้ระดับ ink ซึ่งมีอยู่แล้วและผ่านเกณฑ์
      **ไม่แตะพื้นแดงของ `.btn-danger`** ที่เจ้าของสั่งให้ตรงกับเว็บเก่า          */
.btn-outline-danger { color: var(--red-ink); border-color: var(--red); }
.btn-outline-danger:hover, .btn-outline-danger:focus { color: #fff; }
/* 2) ป้ายหน้า/หลังช่องกรอก (≥ · = · %) ในตัวแก้เกณฑ์เกรด — `--text-muted` บน
      `--bg-soft` ได้ 2.99 เพราะพื้นเป็นสีอ่อนของแบรนด์ ไม่ใช่ขาว · ป้ายพวกนี้
      คือสิ่งที่บอกว่าช่องข้าง ๆ หมายถึงอะไร ไม่ใช่ของประดับ                    */
.input-group-text { color: var(--sk-ink); }
html.dark .input-group-text { color: #fff; }

/* ── ป้ายสถานะการบันทึกในหน้าตรวจงาน (.ag-state) ───────────────────────────
   หน้าตรวจงานบันทึกทันทีที่กรอกแล้ว ป้ายนี้คือสิ่งเดียวที่บอกครูว่าคะแนนลงจริง
   จึงต้องอ่านออกทั้งสองโหมด และต้องไม่ใช้แค่สี (มีข้อความกำกับเสมอ)         */
.ag-state { font-size: .72rem; font-weight: 700; white-space: nowrap; display: inline-block; min-width: 62px; }
.ag-state.ag-idle    { color: transparent; }
.ag-state.ag-saving  { color: var(--text-muted); }
.ag-state.ag-ok      { color: var(--f-teal-ink); }
.ag-state.ag-error   { color: var(--red-ink); }
html.dark .ag-state.ag-ok    { color: var(--f-teal); }
html.dark .ag-state.ag-error { color: #FF8888; }
/* ช่องกรอกใช้สถานะเดียวกับสมุดคะแนน (.saving/.saved/.error มีอยู่แล้วใน theme.css) */
.score-cell.saved  { border-color: var(--green); background: color-mix(in srgb, var(--green) 7%, var(--bg-input)); }
.score-cell.saving { border-color: var(--gold); }
.score-cell.error  { border-color: var(--red); }

/* ── ห้องที่สอนบนการ์ดรายวิชา (.rd-crooms) ──────────────────────────────────
   ครูที่สอนวิชาเดียวกันหลายห้องมีการ์ดหน้าตาเหมือนกันเป๊ะเรียงต่อกัน — ห้องคือ
   สิ่งเดียวที่แยกมันออกจากกัน จึงต้องเป็นชิปที่อ่านได้ในแวบเดียว ไม่ใช่ข้อความ
   ต่อท้ายชื่อวิชาด้วยจุดคั่น · ใช้แผ่นขาวจาง ๆ บนพื้นการ์ดสี หมึกใช้ --rd-ac
   ซึ่งเป็นค่าที่ courseColors() รับประกันคอนทราสต์บนชิปขาวไว้แล้ว           */
.rd-crooms { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 2px; }
.rd-croom {
  font-size: .74rem; font-weight: 800; letter-spacing: -.004em;
  padding: 3px 10px; border-radius: var(--r-pill);
  background: rgba(255,255,255,.9); color: var(--rd-ac, var(--sk-ink));
  display: inline-flex; align-items: center; white-space: nowrap;
}
/* ชิป "ชั้น" จางกว่าชิป "ห้อง" ได้ แต่ห้ามต่ำกว่า .82 — ที่ .62 หมึก accent
   ของบางเฉด (ส้ม/ชมพู/มินต์) เหลือ 4.00-4.32 ซึ่งตกเกณฑ์ (วัดเจอตอนกวาดจริง
   หลังเปลี่ยนชุดสี) · ความต่างของลำดับความสำคัญได้จากตัวหนา/ไอคอนอยู่แล้ว    */
.rd-croom.alt { background: rgba(255,255,255,.84); font-weight: 700; }
html.dark .rd-croom { background: rgba(255,255,255,.92); }

/* ── ตารางสัปดาห์ (.ms-*) ──────────────────────────────────────────────────
   ตารางนี้ต้องอ่านได้ในแวบเดียว จึงคุมความสูงแถวให้เท่ากันและให้ชื่อวิชาตัดคำ
   แทนการดันตารางกว้างออกไป · หัวคาบเป็น sticky ซ้ายเพราะบนมือถือต้องเลื่อน   */
.ms-grid { width: 100%; border-collapse: separate; border-spacing: 0; font-size: .82rem; }
.ms-grid th, .ms-grid td { border-bottom: 1px solid var(--border-color); border-right: 1px solid var(--border-color); vertical-align: top; }
.ms-grid thead th {
  position: sticky; top: 0; z-index: 3; text-align: center; padding: 10px 8px; font-weight: 800;
  background: color-mix(in srgb, var(--sk) 9%, var(--bg-card)); color: var(--sk-deep);
  border-bottom: 2px solid color-mix(in srgb, var(--sk) 32%, transparent);
}
.ms-hcorner { left: 0; z-index: 4 !important; }
.ms-period {
  position: sticky; left: 0; z-index: 2; background: var(--bg-card); min-width: 92px;
  padding: 10px 8px; text-align: center; display: table-cell;
}
.ms-pnum  { display: block; font-weight: 800; color: var(--sk-ink); }
.ms-ptime { display: block; font-size: .7rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.ms-grid td { padding: 6px; min-width: 132px; background: var(--bg-card); }
.ms-grid td.ms-today, .ms-grid thead th.ms-today { background: color-mix(in srgb, var(--sk) 8%, var(--bg-card)); }
.ms-grid thead th.ms-today { background: color-mix(in srgb, var(--sk) 20%, var(--bg-card)); }
.ms-todaytag { display: block; font-size: .64rem; font-weight: 700; opacity: .95; }
.ms-empty { display: block; text-align: center; color: var(--text-muted); padding: 8px 0; }
/* ช่องวิชาในตารางสอน/ตารางเรียน ใช้ --rd-pb ร่วมกับการ์ดรายวิชา — ตอนที่
   --rd-pb ยังเป็นสีสดเข้ม หมึกขาวจึงถูก · พอเปลี่ยนเป็นพาสเทล ขาวบนพาสเทล
   เท่ากับหายไปทั้งช่อง (เจ้าของเจอที่หน้าตารางสอน) หมึกต้องเดินตาม --rd-ac
   เหมือน .rd-course · โหมดมืดยังเป็นพื้นเข้มหมึกขาวตามกฎถัดไป             */
.ms-item {
  display: block; text-decoration: none; border-radius: var(--r-sm); padding: 7px 9px; margin-bottom: 5px;
  background: var(--rd-pb, var(--sk-dk)); color: var(--rd-ac, #fff);
}
.ms-item:last-child { margin-bottom: 0; }
.ms-item:hover { filter: brightness(1.06); color: var(--rd-ac, #fff); }
html.dark .ms-item, html.dark a.ms-item { background: var(--rd-pb-dk, var(--sk-deep)); color: #fff; }
.ms-code { display: block; font-weight: 800; font-size: .72rem; letter-spacing: .02em; opacity: .95; }
.ms-name { display: block; font-weight: 700; line-height: 1.25; overflow-wrap: anywhere; }
.ms-meta { display: block; font-size: .7rem; opacity: .95; margin-top: 2px; }
/* คาบชน — ต้องเห็นทันที เพราะเป็นสิ่งเดียวที่หน้านี้ตรวจให้ */
.ms-grid td.ms-clash { background: color-mix(in srgb, var(--f-amber) 16%, var(--bg-card)); }
.ms-clashtag { display: inline-block; font-size: .68rem; font-weight: 800; color: var(--f-amber-ink); }
html.dark .ms-clashtag { color: var(--f-amber); }
/* ── ตารางแบบ "แถวเป็นวัน คอลัมน์เป็นคาบ" (.ms-byday) ─────────────────────
   หัวแถวย้ายจาก .ms-period (คาบ) มาเป็น .ms-day (วัน) — ต้อง sticky ซ้าย
   เหมือนกัน เพราะตารางกว้างกว่าจอเสมอเมื่อมี 8 คอลัมน์                     */
/* คอลัมน์วันบีบให้แคบที่สุดเท่าที่ยังอ่านชื่อวันได้ — "พฤหัสบดี" คือคำที่ยาวสุด
   ที่ .78rem พอดีกับ 68px · คอลัมน์นี้ซ้ำค่าเดิมทุกแถวจึงไม่ควรกินพื้นที่เท่า
   ช่องคาบซึ่งมีเนื้อหาจริง (เจ้าของสั่งลดโดยเฉพาะคอลัมน์นี้ 13 ส.ค. 2569)   */
.ms-byday .ms-day {
  position: sticky; left: 0; z-index: 2; background: var(--bg-card);
  min-width: 68px; width: 68px;
  padding: 10px 4px; text-align: center; font-weight: 800; font-size: .78rem;
  color: var(--sk-ink); display: table-cell; vertical-align: middle;
}
.ms-byday .ms-hcorner { min-width: 68px; width: 68px; padding-left: 4px; padding-right: 4px; }
.ms-byday .ms-day.ms-today { background: color-mix(in srgb, var(--sk) 20%, var(--bg-card)); }
/* คอลัมน์พักเที่ยง: แคบกว่าคาบเรียนและไม่มีเนื้อหาให้คลิก จึงบีบความกว้าง
   ไม่ให้กินพื้นที่เท่าคาบจริง · ตัวหนังสือหมุนแนวตั้งบนจอกว้างพอ */
/* สัดส่วนคอลัมน์ (ปรับตามที่เจ้าของสั่ง 13 ส.ค. 2569 — สองรอบ)
   รอบแรกลดคาบ 132→104 · รอบสองลดคอลัมน์วันเหลือ 68 · รอบสามลดช่องคาบอีก 20%
   (88→70) ส่วน **พักเที่ยงคงไว้ที่ 70** ตามที่เจ้าของสั่งย้อนกลับ

   ช่องคาบคุมด้วย min-width ไม่ใช่ width ตายตัว ตารางจึงยังยืดเต็มการ์ดได้เมื่อ
   มีคอลัมน์น้อย (เช่นโรงเรียนที่มี 5 คาบ) แทนที่จะเหลือที่ว่างด้านขวา
   ส่วนคอลัมน์วันกับพักเที่ยงตรึงด้วย width เพราะเนื้อหาซ้ำ/สั้น ไม่ควรยืดตาม */
/* **ต้องปลด width:100% ของ .ms-grid ออก** ไม่งั้น min-width ข้างล่างไม่มีผลเลย
   บนจอกว้าง — ตารางยืดเต็มกรอบแล้วหารความกว้างที่เหลือให้ทุกคอลัมน์เท่า ๆ กัน
   (วัดได้ 131px ทั้งที่ตั้ง min-width ไว้ 70px) · width:auto ทำให้คอลัมน์
   กว้างเท่าที่เนื้อหาต้องการจริง แลกกับมีที่ว่างด้านขวาบนจอกว้าง ซึ่งเป็นสิ่งที่
   เจ้าของเลือก (สั่งลดความกว้างสองรอบ) */
.ms-byday { width: auto; }
.ms-byday td:not(.ms-lunch) { min-width: 70px; }
/* ความสูงขั้นต่ำของช่อง — ของเดิมช่องที่ไม่มีวิชาสูงแค่ตามขีด "—" ทำให้แถวที่
   ทั้งวันว่างบางเฉียบ ตารางเลยดูกระโดด · ใส่ที่ td ไม่ใช่ tr เพราะ height บน tr
   ในตาราง border-collapse:separate ไม่มีผลกับช่องที่เนื้อหาสั้นกว่า          */
.ms-byday td { height: 78px; }
.ms-byday .ms-day { height: 78px; }
.ms-byday .ms-lunch-h, .ms-byday td.ms-lunch {
  min-width: 70px; width: 70px; text-align: center;
  background: color-mix(in srgb, var(--f-amber) 14%, var(--bg-card));
}
.ms-byday .ms-lunch-h { border-bottom-color: color-mix(in srgb, var(--f-amber) 45%, transparent); }
/* "12.00–13.00" ต้องอยู่บรรทัดเดียว — คอลัมน์กว้าง 70px หัก padding 8px สองข้าง
   เหลือ 54px ซึ่งไม่พอสำหรับ .7rem · บีบ padding เหลือ 2px และย่อฟอนต์เฉพาะ
   บรรทัดเวลาของคอลัมน์นี้ (ไม่แตะคอลัมน์คาบซึ่งกว้างกว่าและยังใช้ .7rem ได้) */
.ms-byday .ms-lunch-h { padding-left: 2px; padding-right: 2px; }
/* 0.62rem = 9.9px · ตัวเลขอ่านง่ายกว่าตัวหนังสือไทยที่ขนาดเดียวกันก็จริง
   แต่ยังต่ำกว่าเกณฑ์อยู่ดี · `tabular-nums` ทำให้ทุกหลักกว้างเท่ากัน ช่องจึง
   ไม่ขยับเมื่อขยับขนาดขึ้นเล็กน้อย และ `nowrap` กันไม่ให้ช่วงเวลาแตกบรรทัด */
.ms-byday .ms-lunch-h .ms-ptime { font-size: .7rem; white-space: nowrap; }
/* ชั้น + ห้อง อยู่แถวเดียวกัน — .ms-meta เป็น block โดยกำเนิด ถ้าไม่ห่อด้วย flex
   มันจะดันตัวเองลงบรรทัดใหม่เสมอ · margin-top ย้ายมาอยู่ที่ตัวห่อ ลูกจึงไม่ต้อง
   มี margin ของตัวเองให้ชนกัน */
.ms-item .ms-sub {
  display: flex; align-items: center; flex-wrap: wrap; gap: 5px; margin-top: 3px;
}
.ms-item .ms-sub .ms-meta { display: inline; margin-top: 0; }
.ms-item .ms-lv {
  display: inline-block; padding: 1px 7px; border-radius: var(--r-pill, 50px);
  font-size: .66rem; font-weight: 800; line-height: 1.5; white-space: nowrap;
  background: rgba(255, 255, 255, .55); color: var(--rd-ac, inherit);
}
html.dark .ms-item .ms-lv { background: rgba(255, 255, 255, .18); color: #fff; }
.ms-byday td.ms-lunch { vertical-align: middle; }
.ms-byday td.ms-lunch span {
  display: block; font-size: .72rem; font-weight: 800; line-height: 1.2;
  color: var(--f-amber-ink);
}
html.dark .ms-byday td.ms-lunch span, html.dark .ms-byday .ms-lunch-h .ms-pnum { color: var(--f-amber); }
/* หัวคอลัมน์ของตารางแบบนี้มีสองบรรทัด (ชื่อคาบ + เวลา) เหมือน .ms-period เดิม */
.ms-byday thead th .ms-pnum { color: inherit; }

@media (max-width: 575px) {
  .ms-grid td { min-width: 116px; } .ms-name { font-size: .78rem; }
  .ms-byday .ms-day, .ms-byday .ms-hcorner { min-width: 56px; width: 56px; font-size: .72rem; }
  .ms-byday td:not(.ms-lunch) { min-width: 61px; }
  .ms-byday .ms-lunch-h, .ms-byday td.ms-lunch { min-width: 46px; width: 46px; }
  .ms-byday td.ms-lunch span { font-size: .64rem; }
}

/* ── การ์ดตัวเลขในหน้าเทียบผล (.gc-nums) ───────────────────────────────────
   สี่ตัวเลขในบรรทัดเดียว: GPA · เฉลี่ย · ผ่าน · จำนวนนักเรียน
   ตัวเลขใหญ่กว่าป้ายเพราะเป็นสิ่งที่คนมาอ่าน ป้ายเป็นแค่คำอธิบาย            */
.gc-nums { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-top: 6px; }
.gc-nums > div { text-align: center; background: var(--bg-soft); border-radius: var(--r-sm); padding: 8px 4px; }
.gc-v { display: block; font-family: var(--font-display); font-weight: 800; font-size: 1.05rem;
        color: var(--sk-ink); font-variant-numeric: tabular-nums; line-height: 1.15; }
.gc-l { display: block; font-size: .7rem; font-weight: 600; color: var(--text-muted); }
html.dark .gc-v { color: var(--sk-lt); }
@media (max-width: 400px) { .gc-nums { grid-template-columns: repeat(2, 1fr); } }

/* ═══════════════════════════════════════════════════════════════════════
   FIGURE RAIL — การ์ดสถิติเลิกเป็น "แผ่นไล่เฉด 4 ใบ" (15 ส.ค. 2569)
   ═══════════════════════════════════════════════════════════════════════
   สิ่งที่ทำให้เว็บนี้ถูกอ่านว่า "เทมเพลตที่ AI เจน" ไม่ใช่สีฟ้าและไม่ใช่เลย์เอาต์
   แต่คือ **แถวการ์ดตัวเลขสี่ใบไล่เฉดคนละสี** ซึ่งเป็นองค์ประกอบที่ระบุตัวได้
   ง่ายที่สุดของแดชบอร์ดที่เจนมา · มันโผล่ผ่าน statCards() ~15 หน้า และผ่าน
   .rd-stats อีกสามหน้า จึงเป็นจุดเดียวที่แก้แล้วเปลี่ยนทั้งเว็บ

   **ไม่แตะท่อสีเดิมเลย** — `--card-a` / `--card-b` / `--card-ink` ยังคำนวณ
   เหมือนเดิมทุกบรรทัด (ทั้งกฎตำแหน่ง nth-child และคลาสความหมาย .s-*)
   ที่เปลี่ยนคือ **วัสดุที่เอาตัวแปรพวกนั้นไปใช้** ดังนั้น:
     · โหมดมืดยังทำงานตามเดิม (ตัวแปรพลิกเอง)
     · แอดมินเปลี่ยนสีเว็บยังมีผลตามเดิม (สีไหลผ่าน --sk/--f-* เหมือนเดิม)
     · ไม่ต้องแก้ PHP สักบรรทัด ลิงก์/ลำดับ/ตัวเลขอยู่ที่เดิมทุกใบ

   ทำไมถึงอ่านง่ายขึ้น ไม่ใช่แค่ "ดูดีขึ้น":
   ตัวเลขย้ายจาก "ขาวบนพื้นสี" มาเป็น "หมึกเข้มบนพื้นการ์ด" = คอนทราสต์
   ขึ้นจาก 4.6-6.3 เป็น ~11 และ **พ้นจากบั๊กตระกูล "พื้นมาจากกฎหนึ่ง หมึกมา
   จากอีกกฎหนึ่ง"** ที่ไล่แก้กันมาสองรอบสมัย VIVID ไปโดยปริยาย เพราะหมึก
   ไม่ได้ผูกกับพื้นอีกแล้ว

   สีตระกูลไม่ได้หายไป — มันย้ายจาก "ทาทั้งใบ" ไปเป็น "ไอคอน + ขีดใต้ตัวเลข"
   ซึ่งยังแยกใบออกจากกันได้ในหางตา แต่ไม่กลืนตัวเลขและไม่ตะโกนพร้อมกันสี่ใบ
   ═══════════════════════════════════════════════════════════════════════ */

.rd-stat, .stat-card, .card.stat-card,
html.dark .rd-stat, html.dark .card.stat-card {
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  color: var(--text-main);
  box-shadow: none;
  position: relative;
  overflow: hidden;
}

/* ขีดสีประจำใบ — อยู่ใต้ตัวเลข ไม่ใช่ขอบซ้าย
   ขอบซ้ายหนา ๆ (border-left: 4px) คือ tell อีกตัวของ UI ที่เจนมา และที่นี่
   เคยมี 16 จุด · ขีดสั้นใต้ตัวเลขเป็นเครื่องหมายทางตัวอักษรจริง ๆ (rule under
   a figure) ไม่ใช่แถบตกแต่งข้างการ์ด                                        */
/* (ชุดแรก "ท่อน 26px + transition: width" ถูกลบแล้ว —  ของมัน
   ไม่ได้ถูกกฎรุ่นหลังล้าง เพราะรุ่นหลังตั้งแค่ left/right กล่องจึงยังกว้าง
   26px อยู่ตลอด แล้ว scaleX(1) ก็ได้แค่ 26px → "เส้นวิ่งไม่สุด" ที่เจ้าของเจอ
   ต่อไปนี้มีนิยาม ::after ของการ์ดสถิติ **ชุดเดียวเท่านั้น** อยู่ท้ายไฟล์) */

/* ใบที่กดได้ยกตัวน้อยลงและรับสีตระกูลที่ขอบ — เดิมยก 3px + เงาใหญ่ ซึ่งบนแถว
   สี่ใบทำให้ทั้งแถวขยับเวลากวาดเมาส์ผ่าน                                     */
a.rd-stat, a.stat-card, a.card.stat-card { transition: border-color .18s, transform .18s; }
a.rd-stat:hover, a.stat-card:hover, a.card.stat-card:hover,
html.dark a.rd-stat:hover, html.dark a.card.stat-card:hover {
  transform: translateY(-1px);
  border-color: var(--card-mark);
  box-shadow: none;
  color: var(--text-main);
}

/* ── ตัวหนังสือในใบ ────────────────────────────────────────────────────
   ต้องเขียน html.dark คู่ทุกกฎ เพราะกฎเดิมที่ (0,4,1) บังคับ --card-ink ไว้
   และ `html.dark a` (0,2,1) ยังกินหมึกของใบที่เป็นลิงก์อยู่                  */
.rd-stat .rd-stat-val, .stat-card .stat-val,
html.dark .rd-stat .rd-stat-val, html.dark .card.stat-card .stat-val,
html.dark a.rd-stat .rd-stat-val, html.dark a.card.stat-card .stat-val {
  color: var(--text-main);
  font-size: 2.35rem; font-weight: 700; line-height: 1.05;
  letter-spacing: -0.03em;
}
.rd-stat .rd-stat-val small, html.dark .rd-stat .rd-stat-val small {
  color: var(--text-muted); font-size: .5em; font-weight: 600; letter-spacing: 0;
}
.rd-stat .rd-stat-label, .stat-card .stat-label,
html.dark .rd-stat .rd-stat-label, html.dark .card.stat-card .stat-label,
html.dark a.rd-stat .rd-stat-label, html.dark a.card.stat-card .stat-label {
  color: var(--text-muted);
  font-size: .78rem; font-weight: 600; line-height: 1.3;
}

/* ป้ายอยู่ "เหนือ" ตัวเลข = ท่าอ่านของแบบฟอร์ม/ทะเบียน ไม่ใช่ของการ์ด KPI
   ใช้ order ล้วน ไม่แตะ DOM — PHP จึงไม่ต้องแก้                             */
.stat-card .card-body {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 2px; padding: 15px 18px 19px;
}
.stat-card .stat-label { order: 1; }
.stat-card .stat-val   { order: 2; }
.rd-stat { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 15px 18px 19px; }
.rd-stat .rd-stat-top   { order: 0; margin-bottom: 3px; }
.rd-stat .rd-stat-label { order: 1; }
.rd-stat .rd-stat-val   { order: 2; }
.rd-stat .rd-stat-note  { order: 3; margin-top: 5px; }

/* ไอคอนเลิกเป็นลายน้ำ 3rem opacity .22 มุมขวา (ท่ามาตรฐานของการ์ด KPI ที่เจนมา)
   → กลายเป็นเครื่องหมายเล็กเต็มความทึบในสีตระกูล อยู่หัวใบเหนือป้าย          */
.stat-card .stat-icon {
  order: 0; position: static; font-size: 1.05rem; opacity: 1;
  color: var(--card-mark); margin-bottom: 3px; line-height: 1;
}
html.dark .stat-card .stat-icon, html.dark a.card.stat-card .stat-icon { color: var(--card-mark); }
.rd-stat .rd-stat-ic {
  background: none; width: auto; height: auto; font-size: 1.05rem; line-height: 1;
  color: var(--card-mark); box-shadow: none;
}


/* บรรทัดสถานะใต้ตัวเลขยังบอกความหมายด้วยสีของมันเอง — ไม่ผูกกับ --card-ink อีก */
.rd-stat .rd-n-green, html.dark .rd-stat .rd-n-green { color: var(--green-ink); }
.rd-stat .rd-n-red,   html.dark .rd-stat .rd-n-red   { color: var(--red-ink); }
.rd-stat .rd-n-gold,  html.dark .rd-stat .rd-n-gold  { color: var(--gold-ink); }
.rd-stat .rd-n-mute,  html.dark .rd-stat .rd-n-mute  { color: var(--text-muted); }
.rd-stat .rd-stat-note, html.dark .rd-stat .rd-stat-note { font-size: .74rem; font-weight: 600; }

/* แถบสีขอบซ้ายของ .rd-stat ตายไปแล้ว (display:none ตั้งแต่ชุด SKY CAMPUS)
   ประกาศซ้ำที่นี่เพื่อให้ตัวตรวจ slop เห็นว่ามันถูกปิดจริง                    */
/* (กฎ content:none เดิมถูกลบ — แถบซ้าย 4px ถอดที่ต้นเหตุไปแล้ว และกฎนี้
   บังเอิญไปปิดเส้นฐานใต้การ์ดของใบที่มีคลาสตระกูลสีด้วย เพราะ (0,2,0) ชนะ) */

/* แถบ 4px หัวไทล์ห้องที่ปรึกษา — เหลือเส้นผมหนึ่งพิกเซล สีย้ายไปอยู่ที่ไอคอน */
.hx-tile::before { height: 1px; }

/* ── --card-mark : สีของ "เครื่องหมาย" บนใบ (ไอคอน + ขีดใต้ตัวเลข + ขอบ hover)
   ─────────────────────────────────────────────────────────────────────────
   **ห้ามใช้ `--card-b` ตรง ๆ กับเครื่องหมายพวกนี้** — `--card-b` คือปลาย
   "สว่าง" ของคู่ไล่เฉดที่ออกแบบมาให้เป็น *พื้น* ไม่ใช่หมึก · บนการ์ดขาวมันตก
   เกณฑ์ทันทีสองใบ: ใบตำแหน่ง 4n+3 ได้ --warm-red (3.06) และ 4n+4 ได้
   --warm-amber (**2.01** ซึ่งคือมองแทบไม่เห็น) · ระดับ ink ของสองโทนนี้มีอยู่
   แล้วในระบบและผ่าน AA มาแล้วทั้งสองโหมด จึงหยิบมาใช้ตรง ๆ

   เกณฑ์ที่ใช้คือ 3:1 (องค์ประกอบ UI ที่ไม่ใช่ตัวหนังสือ) ไม่ใช่ 4.5 —
   แต่ทั้งสองค่าที่แทนเข้าไปผ่าน 4.5 อยู่แล้ว จึงเผื่อไว้สำหรับกรณีที่ต่อไป
   มีใครเอา --card-mark ไปใช้กับตัวหนังสือด้วย                                */
.rd-stat, .stat-card, .card.stat-card { --card-mark-src: var(--card-b, var(--sk-dk)); }

/* ตำแหน่งในแถว — เขียนให้ specificity เท่ากับกฎที่ตั้ง --card-a/-b (0,3,1)
   จะได้ชนะด้วยลำดับไฟล์ ไม่ใช่ด้วยการยัด !important                         */
.rd-stats > .rd-stat:nth-child(4n+3),
.row > [class*="col-"]:nth-child(4n+3) .stat-card { --card-mark-src: var(--red-ink); }
.rd-stats > .rd-stat:nth-child(4n+4),
.row > [class*="col-"]:nth-child(4n+4) .stat-card { --card-mark-src: var(--gold-ink); }

/* คลาสความหมาย + ชื่อตำแหน่งเก่าที่เขียนตรงใน markup */
.s-amber, .s-gold, .stat-4 { --card-mark-src: var(--gold-ink); }
.s-orange, .s-red, .s-rose, .stat-3 { --card-mark-src: var(--red-ink); }
.s-lime, .s-teal, .s-green { --card-mark-src: var(--green-ink); }

/* โหมดมืด: เครื่องหมายต้องมาจาก "ปลายสว่าง" ของเฉดเดียวกัน ไม่ใช่ค่าเดิม
   ─────────────────────────────────────────────────────────────────────
   วัดแล้วเจอจริง: ใบแรกของ .rd-stats ได้ --sk-dk (#2C5EAD) บนการ์ด #132540
   = **2.59** ซึ่งตกเกณฑ์ 3:1 ขององค์ประกอบ UI · ผสมขาว 58% รักษาเฉดไว้
   (ไม่ได้เปลี่ยนเป็นสีเทาหรือสีอื่น) แล้วดันคอนทราสต์ขึ้นเหนือเกณฑ์ทุกตระกูล
   จึงไม่ต้องเขียนกฎแยกรายตระกูลให้โหมดมืดอีกสิบบรรทัด                        */
.rd-stat, .stat-card, .card.stat-card { --card-mark: var(--card-mark-src, var(--sk-dk)); }
html.dark .rd-stat, html.dark .stat-card, html.dark .card.stat-card {
  --card-mark: color-mix(in srgb, var(--card-mark-src, var(--sk)) 58%, #fff);
}

/* ไอคอนต้องชนะ `.stat-card .bi { color: inherit }` (0,2,1) ที่ตั้งไว้ให้ไอคอน
   เดินตามหมึกของใบสมัยที่ใบยังเป็นพื้นสี — เขียน `i.stat-icon` ให้เป็น (0,2,1)
   เท่ากันแล้วชนะด้วยลำดับไฟล์ · ถ้าไม่ทำ ไอคอนจะเป็นสีเดียวกับตัวเลขเป๊ะ
   แล้วสีตระกูลจะหายไปจากใบโดยที่ทุกอย่างยัง "อ่านออก" จึงไม่มีใครเห็นว่าหาย  */
.stat-card i.stat-icon, .card.stat-card i.stat-icon, a.card.stat-card i.stat-icon,
html.dark .card.stat-card i.stat-icon,
html.dark a.card.stat-card i.stat-icon { color: var(--card-mark); }

/* ใบตำแหน่ง 1-2 ได้ --sk (#1591DC) ซึ่งวัดบนการ์ดขาวได้ **2.77** — ตกเกณฑ์
   3:1 · เป็นค่าที่ตั้งใจให้เป็น "พื้น" มาตลอด (คอมเมนต์เก่าในไฟล์นี้เตือนไว้
   แล้วว่าขาวทับมันได้แค่ 3.43) พอเอามาเป็นหมึกบนพื้นขาวจึงตกทันที            */
.rd-stats > .rd-stat:nth-child(4n+1),
.row > [class*="col-"]:nth-child(4n+1) .stat-card { --card-mark-src: var(--sk-dk); }
.rd-stats > .rd-stat:nth-child(4n+2),
.row > [class*="col-"]:nth-child(4n+2) .stat-card { --card-mark-src: var(--sk-ink); }

/* ═══════════════════════════════════════════════════════════════════════
   PASS 2 — "เอกสาร" ไม่ใช่ "แผงไทล์" (15 ส.ค. 2569)
   ═══════════════════════════════════════════════════════════════════════
   หลัง FIGURE RAIL แล้ว สิ่งที่ยังทำให้ทุกหน้าอ่านว่าเทมเพลตคือ **ทุกอย่าง
   เป็นกล่องขาวมุมโค้งเท่ากันลอยอยู่บนพื้นเท่ากันหมด** — หน้า admin_overview
   หนึ่งหน้ามีแถบฟ้า 1 อัน + กล่องขาว 12 อัน ที่มีน้ำหนักเท่ากันทุกใบ
   ตาจึงไม่รู้ว่าควรอ่านอะไรก่อน และภาพรวมออกมาเป็น "แผงการ์ด" ซึ่งเป็น
   ลายเซ็นของหน้าที่เจนมา

   แก้ด้วยการให้ **ความสำคัญมาจากลำดับชั้นของวัสดุ ไม่ใช่จากกล่อง**        */

/* ── 1. การ์ดเลิกลอย ────────────────────────────────────────────────────
   เงาคือคำสัญญาว่า "ของชิ้นนี้ลอยอยู่เหนือหน้ากระดาษ" ซึ่งจริงกับเมนู
   ป็อปอัป และ toast เท่านั้น · แผงเนื้อหาในหน้าไม่ได้ลอยไปไหน มันคือ
   หน้ากระดาษ · เอาเงาออกแล้วเหลือเส้นขอบผมเส้นเดียว หน้าเว็บจะอ่านเป็น
   เอกสารที่มีโครง แทนที่จะเป็นไทล์กองรวมกัน
   **เงายังอยู่ครบกับของที่ลอยจริง** (.modal-content · .dropdown-menu ·
   .sg-toast · .sg-pop) เพราะที่นั่นเงาทำหน้าที่บอกระยะ ไม่ใช่ตกแต่ง       */
.card, html.dark .card { box-shadow: none; }
.card:hover { box-shadow: none; }

/* การ์ดที่กดได้บอกตัวเองด้วยขอบ ไม่ใช่ด้วยการกระโดด — ทั้งแถวจะได้ไม่ขยับ
   เวลากวาดเมาส์ผ่าน (ปัญหาเดิมของแถวการ์ดสี่ใบ)                            */
a.card:hover, .card-link:hover { border-color: var(--sk-lt); box-shadow: none; }

/* หัวแผงเบาลง — เส้นคั่นบางลงและหมึกของหัวเรื่องเป็นตัวบอกลำดับชั้นแทน */
.card-header { border-bottom-color: color-mix(in srgb, var(--border-color) 62%, transparent); padding-top: 14px; padding-bottom: 12px; }

/* ── 2. ฮีโร่: จากแบนเนอร์ไล่เฉด → หัวกระดาษ ──────────────────────────
   ของเดิมคือสี่เหลี่ยมมุมโค้ง 20px ไล่เฉดน้ำเงิน→ฟ้าสว่าง มีวงกลมขาวโปร่ง
   280px โผล่มุมขวาบน และมีเงาสีฟ้าใต้ทั้งแถบ · องค์ประกอบสามอย่างนี้
   (ไล่เฉดกวาดยาว + วงกลมตกแต่ง + เงาสี) คือชุดเดียวกับที่โผล่ในแดชบอร์ด
   ที่เจนมาแทบทุกอัน และไม่มีอันไหนบอกข้อมูลอะไรเลยสักอัน

   ใหม่: พื้นแบรนด์แบน ๆ ไล่เฉดสั้น ๆ ในเฉดเดียวกัน ไม่มีวงกลม ไม่มีเงา
   เตี้ยลง และมีเส้นคาดฐานเป็นตัวปิด — อ่านเป็น "หัวกระดาษของหน่วยงาน"
   ซึ่งตรงกับสิ่งที่มันเป็นจริง ๆ มากกว่า                                    */
.dash-hero {
  background: linear-gradient(160deg, var(--brand-dark) 0%, var(--brand) 100%);
  border-radius: var(--r-lg);
  padding: 22px 26px;
  /* เส้นคาดฐานเป็น inset shadow ไม่ใช่ border-bottom — inset วิ่งตามมุมโค้ง
     ส่วน border จะตัดมุมให้เป็นเหลี่ยมตรงหัวท้าย ซึ่งเห็นชัดมากที่ radius 14px */
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .22);
}
.dash-hero::after { content: none; }
.dash-hero h3, .dash-hero h4 { font-size: clamp(1.35rem, 2.4vw, 1.85rem); }
.dash-hero .sub { opacity: 0.86; font-size: 0.92rem; }
.fm-hero { box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .22); }

/* (แถบ 4px หัวการ์ดบทบาท แก้ที่กฎ .hm-role ต้นทางแล้ว ไม่ override ซ้ำ) */
.hm-role:hover { box-shadow: none; border-color: var(--hm-c); transform: none; }

/* (เส้นใต้แท็บ แก้ที่ style.css ต้นทางแล้ว ไม่ override ซ้ำ) */

/* ── แถบทางลัดผู้ดูแล (.adm-nav) ────────────────────────────────────────
   หนึ่งแถบ คั่นด้วยเส้น ไม่ใช่กล่องหกใบ · ต้องดูเป็น "เครื่องมือนำทาง"
   ให้ต่างจากแถวตัวเลขที่อยู่ถัดลงไปโดยไม่ต้องพึ่งเงาหรือสี                 */
.adm-nav {
  display: flex; flex-wrap: wrap;
  background: var(--bg-card); border: 1px solid var(--border-color);
  border-radius: var(--r-lg); overflow: hidden;
}
.adm-nav-item {
  flex: 1 1 0; min-width: 118px;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 11px 14px; text-decoration: none;
  font-size: .875rem; font-weight: 600; color: var(--text-main);
  border-left: 1px solid var(--border-color);
  transition: background .16s, color .16s;
}
.adm-nav-item:first-child { border-left: 0; }
.adm-nav-item .bi { font-size: 1.05rem; color: var(--sk-dk); transition: color .16s; }
.adm-nav-item:hover, .adm-nav-item:focus-visible { background: var(--sk-wash); color: var(--sk-ink); }
.adm-nav-item:hover .bi { color: var(--sk-ink); }
html.dark .adm-nav-item, html.dark a.adm-nav-item { color: var(--text-main); }
html.dark .adm-nav-item .bi { color: var(--sk-lt); }
html.dark .adm-nav-item:hover, html.dark a.adm-nav-item:hover { background: rgba(75, 184, 250, .12); color: var(--sk-lt); }
/* จอแคบ: เลื่อนแนวนอนแทนการพันบรรทัดจนสูงสามแถว */
@media (max-width: 575px) {
  .adm-nav { flex-wrap: nowrap; overflow-x: auto; }
  .adm-nav-item { flex: 0 0 auto; min-width: 0; }
}

/* ── การกระจายเกรดแบบยุบได้ (.gb-dist) ─────────────────────────────────
   แถวหัวสูง ~44px แทนการ์ดสูง ~290px · ตารางสมุดคะแนนจึงขึ้นมาอยู่ในจอแรก */
.gb-dist { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--r-lg); }
.gb-dist > summary {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 16px; cursor: pointer; list-style: none;
  font-weight: 600; color: var(--text-main);
  border-radius: var(--r-lg);
}
.gb-dist > summary::-webkit-details-marker { display: none; }
.gb-dist > summary:hover { background: var(--bg-soft); }
.gb-dist > summary > .bi:first-child { color: var(--sk-dk); flex: 0 0 auto; }
html.dark .gb-dist > summary > .bi:first-child { color: var(--sk-lt); }
.gb-dist-t { flex: 0 0 auto; font-size: .92rem; }
.gb-dist-n { flex: 0 0 auto; font-size: .8rem; font-weight: 600; color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* แถบสัดส่วน — เป็นข้อมูล ไม่ใช่ของประดับ จึงต้องมี title ทุกช่วง */
.gb-mini { flex: 1 1 auto; display: flex; height: 9px; min-width: 90px; border-radius: 999px; overflow: hidden; background: var(--bg-soft); }
.gb-mini > span { display: block; min-width: 3px; }
.gb-dist-chev { flex: 0 0 auto; color: var(--text-muted); font-size: .85rem; transition: transform .22s cubic-bezier(.16,1,.3,1); }
.gb-dist[open] > summary > .gb-dist-chev { transform: rotate(180deg); }
.gb-dist[open] > summary { border-radius: var(--r-lg) var(--r-lg) 0 0; border-bottom: 1px solid var(--border-color); }
.gb-dist[open] > summary .gb-mini { display: none; }
.gb-dist-body { padding: 16px; }
@media (max-width: 575px) {
  .gb-dist-t { display: none; }
  .gb-dist > summary { padding: 10px 12px; gap: 9px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   PASS 3 — ปิดงานที่ PASS 2 ทำค้างไว้ (15 ส.ค. 2569)
   ═══════════════════════════════════════════════════════════════════════
   จากการตรวจอิสระ: PASS 2 เอาเงาออกจาก `.card` แต่ **ผิวอื่นอีกเก้าชนิด
   ยังลอยอยู่** โดยเฉพาะ `.rd-course` ซึ่งเป็น "วัตถุหลัก" ของ my_courses
   กับ dashboard — สองหน้าที่ถูกเปิดบ่อยที่สุดในระบบ
   ที่มองไม่เห็นตอนตรวจเองเพราะบัญชีที่ใช้ถ่ายภาพไม่มีรายวิชาเลยสักวิชา
   **การ์ดรายวิชาจึงไม่เคยถูกเรนเดอร์ออกมาให้เห็นสักครั้ง**
   บทเรียน: ถ่ายภาพหน้าเปล่าแล้วสรุปว่าหน้านั้นเรียบร้อย = ตรวจสิ่งที่ไม่มีอยู่

   กฎเดียวของระบบนี้ตอนนี้: **เงา = "ลอยอยู่เหนือหน้ากระดาษ"**
   จริงกับ modal · dropdown · toast · popup เท่านั้น ที่เหลือคือหน้ากระดาษ  */
.rd-course, .course-tile, .proj-box, .cw-task, .att-card, .att-stat, .grp-card,
.card.ecal-agenda, .ecal-agenda,
html.dark .rd-course, html.dark .course-tile, html.dark .proj-box, html.dark .cw-task,
html.dark .att-card, html.dark .att-stat, html.dark .grp-card, html.dark .card.ecal-agenda {
  box-shadow: none;
}
/* ยกตัว 3-5px ทำให้ทั้งกริดขยับเวลากวาดเมาส์ผ่าน — บอกว่ากดได้ด้วยขอบแทน */
.rd-course:hover, .course-tile:hover, .proj-box:hover, .cw-task:hover,
.att-card:hover, .att-stat:hover, .grp-card:hover, .cw-stat-link:hover,
a.rd-course:hover, html.dark .rd-course:hover, html.dark .card.ecal-agenda:hover {
  transform: none;
  box-shadow: none;
}
.course-tile:hover, .cw-task:hover, .att-card:hover, .grp-card:hover { border-color: var(--sk); }
/* .rd-course มีพื้นเป็นสีของตัวเอง — ใช้เส้นขอบเข้มขึ้นแทนการเปลี่ยนเป็นฟ้า */
.rd-course:hover, a.rd-course:hover { border-color: rgba(0, 0, 0, .28); }

/* ── ขีดใต้ตัวเลขในการ์ดสถิติ: ถอดออก ─────────────────────────────────
   ท่อนสี 26×3px ชิดขอบล่างซ้าย มีที่ว่าง ~30px คั่นจากตัวเลข — ในเว็บที่เต็ม
   ไปด้วยแถบความคืบหน้าจริง (`ความคืบหน้าการกรอกคะแนน` ใน course_home อยู่ที่
   ~8% พอดี) มันอ่านเป็น **มาตรวัดที่ค้างอยู่ใกล้ศูนย์** ไม่ใช่เส้นใต้ตัวเลข
   ของประดับที่ถูกอ่านเป็นข้อมูลผิด ๆ แย่กว่าไม่มีของประดับเลย
   ไอคอนสีตระกูลทำหน้าที่แยกใบได้อยู่แล้ว — เครื่องหมายเดียวพอ ไม่ต้องสอง   */
.rd-stat, .stat-card, .card.stat-card { padding-bottom: 15px; }
.stat-card .card-body { padding-bottom: 15px; }

/* ── แถบสัดส่วนการกระจายเกรดต้องไม่กินเต็มความกว้าง ────────────────────
   ยืดถึง ~870px แล้วอ่านเป็นแถบเตือนเต็มจอ ไม่ใช่ตัวเลขหนึ่งค่า            */
.gb-mini { flex: 0 1 300px; max-width: 300px; }
.gb-dist > summary { justify-content: flex-start; }
.gb-dist > summary > .gb-dist-n { margin-left: auto; }

/* ── .adm-nav เลิกทำหน้าเหมือนแถบแท็บ ─────────────────────────────────
   หกช่องกว้างเท่ากันคั่นด้วยเส้น วางใต้ฮีโร่ = รูปทรงเดียวกับ `.rd-subnav`
   ซึ่งในระบบนี้คือ "แท็บในหน้า" แต่ทุกปุ่มที่นี่พาออกไปหน้าอื่น และไม่มีอัน
   ไหน active ได้เลย · เปลี่ยนเป็นลิงก์เรียงชิดซ้าย ไม่มีเส้นคั่น ไม่ยืดเต็ม */
.adm-nav { flex-wrap: wrap; gap: 6px; padding: 8px; border-radius: var(--r-lg); }
.adm-nav-item {
  flex: 0 0 auto; min-width: 0; border-left: 0;
  border-radius: var(--r-pill); padding: 7px 14px;
}

/* ── .cw-stat เข้าชุดเดียวกับการ์ดสถิติหน้าอื่น ────────────────────────
   วงกลมประดับมุมขวาบน (style.css `.cw-stat::after`) ถอดออก — เป็นของประดับ
   ล้วน และเป็นชิ้นเดียวกับวงกลมที่เพิ่งถอดจากฮีโร่ไป                       */
.cw-stat::after { content: none; }
.cw-stat { padding: 15px 18px; }
.cw-stat .ic { font-size: 1.05rem; margin-bottom: 3px; color: var(--card-mark); line-height: 1; }
.cw-stat .lb { font-size: .78rem; font-weight: 600; color: var(--text-muted); margin-top: 0; }
.cw-stat .num { font-family: var(--font-display); font-size: 2.35rem; font-weight: 700; letter-spacing: -.03em; line-height: 1.05; color: var(--text-main); margin-top: 1px; }
html.dark .cw-stat .num { color: var(--text-main); }
.cw-stat-go { bottom: 14px; }

/* ═══════════════════════════════════════════════════════════════════════
   PASS 4 — แก้ regression ที่ PASS 3 สร้างเอง + ปิดรายการที่กวาดไม่ครบ
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 4.1 การ์ดสถิติหนักท้าย ────────────────────────────────────────────
   ถอด ::after ออกแล้วไม่ได้จัดสมดุลใหม่ เนื้อหาเลยกองอยู่สองในสามบน เหลือ
   ที่ว่าง ~40-50px ใต้ตัวเลข · ท่อนสีเดิมอย่างน้อยก็ "ปิดท้าย" ใบให้
   ตัวการ์ดของ Bootstrap เป็น flex column และ .card-body เป็น flex:1 1 auto
   จึงยืดเต็มความสูงที่ใบสูงสุดในแถวกำหนด — ตรึงด้วย align-self แทนการไล่ padding */
.stat-card .card-body { flex: 0 0 auto; padding: 14px 18px 15px; }
.rd-stat, .stat-card, .card.stat-card { padding-bottom: 0; }
.card.stat-card { justify-content: flex-start; }
.rd-stat { padding: 14px 18px 15px; }

/* ── 4.2 การ์ดสถิติมีสองภาษาในหน้าติดกัน ──────────────────────────────
   .att-stat ยังเป็น "แผ่นไอคอนสี 44px + ตัวเลข + ป้ายใต้ตัวเลข" ส่วนที่อื่น
   เป็น figure rail (เครื่องหมายเล็ก + ป้ายเหนือตัวเลข) · ตอนที่ทั้งคู่ยังมีเงา
   ต่างกันมันพอแยกออกว่าเป็นคนละ component แต่พอแบนเท่ากันหมดแล้วกลายเป็น
   กล่องขาวขอบเส้นผมหน้าตาเดียวกันที่ไวยากรณ์ข้างในขัดกันเอง — ต้องพูดภาษาเดียว */
.att-stat { flex-direction: column; align-items: flex-start; gap: 2px; padding: 14px 18px 15px; }
.att-stat .ico {
  width: auto; height: auto; min-width: 0; background: none !important;
  font-size: 1.05rem; line-height: 1; color: var(--card-mark); margin-bottom: 3px;
}
.att-stat .ico.c1 { --card-mark-src: var(--sk-dk); }
.att-stat .ico.c2 { --card-mark-src: var(--f-cyan-dk); }
.att-stat .ico.c3 { --card-mark-src: var(--gold-ink); }
.att-stat .ico.c4 { --card-mark-src: var(--green-ink); }
.att-stat > div:last-child { display: flex; flex-direction: column; }
.att-stat .lb  { order: 1; font-size: .78rem; font-weight: 600; color: var(--text-muted); margin-top: 0; }
.att-stat .num { order: 2; font-family: var(--font-display); font-size: 2.35rem; font-weight: 700; letter-spacing: -.03em; line-height: 1.05; margin-top: 1px; }

/* ── 4.3 เส้นคั่นในการ์ดงานสว่างกว่าหัวเรื่องในโหมดมืด ─────────────────
   พื้นการ์ดแบนแล้วเส้นคั่น/รางที่เคยกลืนไปกับเงากลายเป็นของที่สว่างที่สุดในใบ
   แย่งสายตาไปจากชื่องาน ซึ่งคือสิ่งเดียวที่ต้องอ่านก่อน                     */
html.dark .cw-task hr,
html.dark .cw-task .cw-sep,
html.dark .cw-task .cw-bar,
html.dark .cw-task .progress { background: rgba(255,255,255,.10); border-color: rgba(255,255,255,.10); }
html.dark .cw-task [style*="dashed"], html.dark .cw-task .border-top { border-top-color: rgba(255,255,255,.12) !important; }

/* ── 4.4 ผิวที่ยังลอยอยู่อีกเจ็ดชนิด (จากรายงานของ documenter) ─────────
   `.card { box-shadow: none }` เอื้อมถึงเฉพาะ element ที่มีคลาส .card เท่านั้น
   แผงที่ประกาศ var(--shadow) ของตัวเองจึงรอดมาทั้งหมด — กฎ "เงา = ลอยจริง"
   จะเป็นกฎได้ก็ต่อเมื่อไม่มีข้อยกเว้นที่ไม่ได้ตั้งใจเหลืออยู่                */
.rd-panel, .gradebook-wrapper, .dc-panel, .ecal-card, .nf-item, .term-picker,
html.dark .rd-panel, html.dark .gradebook-wrapper, html.dark .dc-panel,
html.dark .ecal-card, html.dark .nf-item, html.dark .term-picker { box-shadow: none; }
/* .att-ticket เป็นผลลัพธ์การเช็คชื่อที่เด้งขึ้นมาทับหน้า = ลอยจริง จึงเก็บเงาไว้ */

/* ── เส้นใต้การ์ดสถิติ: คืนเอฟเฟกต์ตอน hover (15 ส.ค. 2569) ─────────────
   รอบแรกเส้นเป็นท่อนสั้น 26px ค้างอยู่มุมล่างซ้ายตลอดเวลา แล้ววิ่งเต็มตอน
   hover · การตรวจอิสระตีตกเพราะ **ท่อนที่ค้างอยู่** อ่านเป็นมาตรวัดที่ค้าง
   ใกล้ศูนย์ (เว็บนี้มีแถบความคืบหน้าจริงเต็มไปหมด) รอบสองเลยถอดทิ้งทั้งอัน
   ซึ่งแก้เกิน — เอฟเฟกต์ตอน hover ไม่เคยเป็นปัญหา ท่อนที่ค้างต่างหาก

   คราวนี้: **ตอนพักไม่มีอะไรเลย** จึงไม่มีอะไรให้อ่านผิดเป็นมาตรวัด
   พอ hover เส้นวิ่งจากซ้ายไปเต็มความกว้างใบ — เส้นที่เต็มความกว้างแล้ว
   ไม่มีทางอ่านเป็นมาตรวัดบางส่วนได้

   ใช้ `transform: scaleX()` ไม่ใช่ `width` — การอนิเมต width บังคับให้
   เบราว์เซอร์คำนวณเลย์เอาต์ใหม่ทุกเฟรม ส่วน transform วิ่งบน compositor
   (ตัวตรวจก็จับ `transition: width` เป็น layout-thrash อยู่แล้ว)          */
/* (นิยามซ้ำถูกลบ — เหลือชุดเดียวท้ายไฟล์) */
/* ใบที่กดไม่ได้ไม่ต้องมีเส้น — เส้นนี้บอกว่า "กดได้" ไม่ใช่ของตกแต่ง */
a.rd-stat:hover::after,       a.stat-card:hover::after,
a.cw-stat:hover::after,       a.att-stat:hover::after,
a.rd-stat:focus-visible::after, a.stat-card:focus-visible::after,
a.cw-stat:focus-visible::after, a.att-stat:focus-visible::after { transform: scaleX(1); }
/* ต้องมี overflow:hidden ไม่งั้นปลายเส้นยื่นพ้นมุมโค้งของใบออกไป */
.cw-stat, .att-stat { position: relative; overflow: hidden; }

/* เส้นฐานตอนพัก — เต็มความกว้าง จาง ๆ อยู่ใต้เส้น hover อีกที
   ทำให้ใบไม่โล่งตอนอยู่เฉย ๆ (ซึ่งคือสิ่งที่หายไปตอนถอดท่อนสั้นออก)
   แต่เพราะมัน **เต็มความกว้างตั้งแต่แรก** จึงไม่มีทางถูกอ่านเป็นมาตรวัด
   ที่เติมไม่เต็ม — ซึ่งเป็นปัญหาเดียวของท่อน 26px เวอร์ชันแรก
   ใบที่กดได้จะมีเส้นสีเข้มวิ่งทับตอน hover · ใบที่กดไม่ได้เหลือแค่เส้นฐาน */
/* เส้นฐานจางเต็มความกว้างตอนพัก — เจ้าของเลือกแบบนี้ (16 ส.ค. 2569)
   เต็มความกว้างตั้งแต่แรกจึงไม่ถูกอ่านเป็นมาตรวัดที่เติมไม่เต็ม
   แล้วเส้นสีเข้ม (::after) วิ่งทับตอน hover                                */
.rd-stat::before, .stat-card::before, .cw-stat::before, .att-stat::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: color-mix(in srgb, var(--card-mark) 32%, transparent);
  pointer-events: none;
}

/* ── บรรทัดสำมะโนของหน้าภาพรวมระบบ (.adm-census) ───────────────────────
   ตัวเลขที่ไม่ขยับทั้งเทอม อยู่เป็นบรรทัดเดียว ไม่ใช่การ์ดสี่ใบ
   ยังกดได้เหมือนเดิมทุกตัว แค่ไม่กินพื้นที่ดีที่สุดของจออีกต่อไป          */
.adm-census {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
  padding: 0 2px; font-size: .9rem; color: var(--text-muted);
}
.adm-census a { color: var(--text-muted); text-decoration: none; border-bottom: 1px solid transparent; transition: color .15s, border-color .15s; }
.adm-census a b { font-family: var(--font-display); font-weight: 700; font-size: 1.06rem; color: var(--text-main); font-variant-numeric: tabular-nums; margin-right: 1px; }
.adm-census a:hover { color: var(--sk-dk); border-bottom-color: var(--sk); }
.adm-census a:hover b { color: var(--sk-dk); }
.adm-census > span { color: var(--border-color); }
html.dark .adm-census a, html.dark .adm-census a b { color: var(--text-muted); }
html.dark .adm-census a b { color: var(--text-main); }
html.dark .adm-census a:hover, html.dark .adm-census a:hover b { color: var(--sk-lt); }

/* ═══════════════════════════════════════════════════════════════════════
   เส้นใต้การ์ดสถิติ — รอบสุดท้าย: สั้นตอนพัก · ยาวเต็มตอน hover
   ═══════════════════════════════════════════════════════════════════════
   **เจ้าของสั่งกลับมาใช้แบบนี้ (15 ส.ค. 2569) หลังการตรวจอิสระตีตกไปแล้ว**
   ผู้ตรวจให้เหตุผลว่าท่อนสั้นที่ค้างอยู่มุมล่างซ้ายอ่านเป็น "มาตรวัดที่เติม
   ไม่เต็ม" เพราะเว็บนี้มีแถบความคืบหน้าจริงอยู่เยอะ · เจ้าของยืนยันสองครั้ง
   ว่าต้องการแบบนี้ จึงทำตาม — **อย่า 'แก้' กลับไปเป็นเส้นเต็มความกว้างเอง**
   สิ่งที่ทำได้เพื่อลดการอ่านผิด: ท่อนสั้นมาก (12% ของความกว้าง) และ
   **ไม่มีรางสีจาง ๆ อยู่ข้างหลัง** — track + fill คู่กันเมื่อไหร่ถึงจะกลาย
   เป็นมาตรวัดจริง ๆ ท่อนเดี่ยว ๆ ยังอ่านเป็นขีดเน้นได้

   ใช้ scaleX ไม่ใช่ width — width บังคับคำนวณเลย์เอาต์ใหม่ทุกเฟรม
   และวิ่งกับ **ทุกใบ ไม่เฉพาะใบที่เป็นลิงก์** (ก่อนหน้านี้เขียน a:hover
   ทำให้การ์ดที่ไม่ใช่ลิงก์อย่าง .stat-card กับ .att-stat เส้นไม่วิ่งเลย
   ซึ่งคือ "เส้นไม่วิ่งด้วยซ้ำ" ที่เจ้าของเจอ)                              */
.rd-stat::after, .stat-card::after, .cw-stat::after, .att-stat::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  width: auto;   /* ต้องเขียนไว้ — ถ้ามีกฎเก่าตั้ง width ไว้ left/right จะไม่ชนะ */
  height: 3px; border-radius: 3px 3px 0 0;
  background: var(--card-mark);
  transform: scaleX(0); transform-origin: left center;
  transition: transform .38s cubic-bezier(.16, 1, .3, 1);
  pointer-events: none;
}
.rd-stat:hover::after,  .stat-card:hover::after,
.cw-stat:hover::after,  .att-stat:hover::after,
.rd-stat:focus-visible::after,  .stat-card:focus-visible::after,
.cw-stat:focus-visible::after,  .att-stat:focus-visible::after,
.rd-stat:focus-within::after,   .stat-card:focus-within::after { transform: scaleX(1); }

/* ── ขอบการ์ดหนาขึ้นอีกนิด ──────────────────────────────────────────────
   ตั้งแต่ถอดเงาออก เส้นขอบกลายเป็นสิ่งเดียวที่บอกขอบเขตของการ์ด
   เส้นผมสีจาง (#d9e9f5) ที่เคยพอเมื่อยังมีเงาช่วย ตอนนี้บางเกินไป
   เพิ่มความเข้มของสี ไม่เพิ่มความหนา — 1px→2px จะดันเลย์เอาต์ทั้งเว็บ    */
/* (ขอบย้ายไปแก้ที่ includes/palette.php แทน — ที่นี่ถูก emit ทับอยู่แล้ว) */

/* ── ลูกเล่นตอนเอาเมาส์ชี้การ์ดรายวิชา ─────────────────────────────────
   ตั้งแต่ถอดเงาและการยกตัวออกไป การ์ดไม่มีอะไรตอบสนองเลยตอนชี้
   ซึ่งบนหน้าที่การ์ดทุกใบกดได้ = ไม่มีอะไรบอกว่ากดได้
   คืนการตอบสนองแบบเบา ๆ ที่ไม่ทำให้ทั้งกริดขยับ:
     · แถบความคืบหน้าสว่างขึ้น (มันคือข้อมูลที่ครูมองอยู่แล้ว)
     · ขอบเข้มขึ้น
     · ป้ายชั้น/ห้องทึบขึ้นเล็กน้อย
   ไม่ใช้ translateY เพราะการ์ดเรียงเป็นกริด ยกใบเดียวแล้วสายตาเห็นทั้งแถวขยับ */
.rd-course { transition: border-color .2s, background .2s; }
.rd-course .rd-cbar span, .rd-course .rd-croom { transition: filter .2s, background .2s; }
.rd-course:hover .rd-cbar span { filter: brightness(1.12); }
.rd-course:hover .rd-croom { background: rgba(255, 255, 255, .98) !important; }
.rd-course:hover, a.rd-course:hover { border-color: rgba(0, 0, 0, .3) !important; }

/* ── hover การ์ดรายวิชาให้เห็นจริง ─────────────────────────────────────
   ชุดก่อนหน้า (filter brightness 1.12 + ขอบเข้มขึ้น) เบาจนมองไม่ออกบนพื้น
   สีเข้มของการ์ด · คราวนี้ใช้ "ฝ้าขาวบาง ๆ ทาบทั้งใบ" ซึ่งเห็นได้ทุกเฉด
   ของการ์ด 12 สี ไม่ว่าใบนั้นจะสว่างหรือเข้ม + ยกเงาเข้ามาเฉพาะตอน hover
   (ตอนพักยังไม่มีเงา จึงไม่ขัดกับกฎ "เงา = ลอยจริง" — ตอน hover มันลอยจริง) */
/* **ห้ามใช้ ::after กับการ์ดรายวิชา** — วงกลมประดับมุมขวาล่างจองไว้แล้ว
   และประกาศ background ด้วย !important ถึงสามที่ (โหมดสว่าง + มืดสองชุด)
   กฎใหม่จึงแพ้เงียบ ๆ · เปลี่ยนพื้นของตัวการ์ดเองแทน เห็นชัดกว่าและ
   ไม่ต้องแย่ง pseudo-element กับใคร                                        */
.rd-course:hover, a.rd-course:hover {
  /* ผสมไปทาง --rd-ac (สีเข้มประจำการ์ดใบนั้น) ไม่ใช่ผสมขาว —
     การ์ดทั้ง 12 สีเป็นพาสเทล ผสมขาวเข้าไปแล้วขยับแค่ ~7 ระดับ มองไม่ออก
     ผสมไปทางสีเข้มของตัวเองเห็นชัดและยังอยู่ในเฉดเดิมของใบนั้น */
  background: color-mix(in srgb, var(--rd-pb) 82%, var(--rd-ac)) !important;
}
html.dark .rd-course:hover, html.dark a.rd-course:hover {
  background: color-mix(in srgb, var(--rd-pb-dk, var(--rd-pb)) 84%, #fff) !important;
}
.rd-course:hover, a.rd-course:hover {
  border-color: rgba(0, 0, 0, .34) !important;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .16) !important;
}

/* ── โมดัลสูงเกินจอแล้วปุ่มล่างโดนตัด ─────────────────────────────────
   อาการ: หน้า "สร้างข้อสอบ" / "ตั้งค่าข้อสอบ" ไม่มีปุ่มยืนยันให้กด
   ที่จอ 900px เห็นปุ่มโดนตัดครึ่ง · ที่จอโน้ตบุ๊ก 768px หายทั้งแถบ

   สาเหตุไม่ใช่ markup (ปุ่มอยู่ครบ) แต่เป็น **flex child ที่ไม่ยอมหด**
   `.modal-content` เป็น flex column และ `.modal-body` เป็นลูกที่ควรหดได้
   แต่ค่าเริ่มต้น `min-height: auto` ของ flex item ห้ามหดต่ำกว่าความสูง
   ของเนื้อหาข้างใน · body จึงยืนยันความสูง 742px แล้วดัน `.modal-footer`
   ทะลุออกนอกกล่อง ซึ่ง `overflow: hidden` ของ `.modal-content` ตัดทิ้งพอดี
   → ปุ่มหายไปโดยไม่มี error และไม่มีสกรอลล์บาร์ให้เอะใจ

   `min-height: 0` คือคำตอบมาตรฐานของอาการนี้ · เติม max-height ให้ยึดกับ
   จอจริงด้วย เพราะ `max-height: 100%` ของ Bootstrap อ้างอิง `.modal-dialog`
   ที่ไม่ได้ถูกจำกัดความสูงไว้เมื่อใช้คู่กับ `.modal-dialog-centered`      */
/* ── โมดัลที่ห่อด้วย <form> — ปุ่มยืนยันหลุดจอ ────────────
   **รากของปัญหาคือ <form> ที่คั่นอยู่** โครงจริงของหน้าข้อสอบคือ
     .modal-content > form > (.modal-header + .modal-body + .modal-footer)
   ไม่ใช่ .modal-content > (header/body/footer) แบบที่ Bootstrap ออกแบบไว้
   ผลสองชั้น:
     1. selector ที่ใช้เครื่องหมายลูกตรงไม่ match อะไรเลย (สองความพยายามแรกจึงไม่ขยับ)
     2. Bootstrap ทำ .modal-dialog-scrollable ด้วย flex column บน .modal-content
        พอมี form มาคั่น header/body/footer ก็ไม่ใช่ flex item อีกต่อไป
        body จึงไม่หด และ footer ถูกดันทะลุออกนอกจอ — ไม่มีสกรอลล์บาร์ให้เอะใจ
        ผู้ใช้เห็นแค่ "ไม่มีปุ่มยืนยัน"
   แก้ที่ต้นเหตุ: ให้ form เป็น flex column แทน .modal-content แล้วทุกอย่าง
   กลับมาทำงานตามที่ Bootstrap ตั้งใจ · เขียนแบบ descendant จึงครอบทั้ง
   โมดัลที่มี form และไม่มี                                                    */
.modal-dialog { max-height: calc(100% - 3.5rem); }
.modal-content { max-height: 100%; }
.modal-content > form { display: flex; flex-direction: column; min-height: 0; max-height: 100%; }
.modal-header, .modal-footer { flex: 0 0 auto; }
.modal-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

/* แถบปุ่มต้องไม่ถูกบีบหายเมื่อเนื้อหายาว */
.modal-content > .modal-footer, .modal-content > .modal-header { flex: 0 0 auto; }

/* ── หน้าแก้ข้อสอบ (xe-*) ───────────────────────────────────────────────
   เดิมหน้านี้มี 96 ช่องกรอกเรียงต่อกันโดยไม่แบ่งขั้นเลย และไม่มีอะไรบอกว่า
   กำลังแก้ข้อสอบของวิชาอะไร (หัวเรื่องเขียนแค่ชื่อข้อสอบ)                 */
.xe-crumb { display: flex; align-items: center; gap: 7px; font-size: .85rem; color: var(--text-muted); flex-wrap: wrap; }
.xe-crumb a { color: var(--text-muted); text-decoration: none; }
.xe-crumb a:hover { color: var(--sk-dk); text-decoration: underline; }
.xe-crumb > .bi { font-size: .7rem; opacity: .55; }
.xe-crumb > span { color: var(--text-main); font-weight: 600; }
html.dark .xe-crumb a:hover { color: var(--sk-lt); }

/* แถบความคืบหน้า — ตัวเลขที่ครูถามบ่อยที่สุดระหว่างออกข้อสอบ
   ใช้ไวยากรณ์เดียวกับการ์ดสถิติทั้งเว็บ (ป้ายเหนือตัวเลข หมึกเข้มบนพื้นขาว) */
.xe-progress { display: flex; flex-wrap: wrap; gap: 10px; }
.xe-pg-item {
  flex: 1 1 130px; min-width: 120px; position: relative; overflow: hidden;
  background: var(--bg-card); border: 1px solid var(--border-color);
  border-radius: var(--r-lg); padding: 11px 16px 13px;
  display: flex; flex-direction: column; gap: 1px;
}
.xe-pg-l { order: 1; font-size: .76rem; font-weight: 600; color: var(--text-muted); }
.xe-pg-n { order: 2; font-family: var(--font-display); font-weight: 700; font-size: 1.6rem;
           line-height: 1.05; letter-spacing: -.03em; color: var(--text-main); font-variant-numeric: tabular-nums; }
.xe-pg-item::before { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
                      background: color-mix(in srgb, var(--sk-dk) 40%, transparent); }
/* ข้อที่ครูต้องตรวจเองเป็นภาระที่มองข้ามไม่ได้ — คะแนนไม่เข้าสมุดจนกว่าจะตรวจ */
.xe-pg-warn::before { background: color-mix(in srgb, var(--gold-ink) 55%, transparent); }
.xe-pg-warn .xe-pg-n { color: var(--gold-ink); }

/* แผงที่ยุบได้ — ใช้ <details> ของ HTML จึงทำงานกับคีย์บอร์ด/screen reader เอง */
.xe-fold > summary { cursor: pointer; list-style: none; }
.xe-fold > summary::-webkit-details-marker { display: none; }
.xe-fold > summary:hover { background: var(--bg-soft); }
.xe-fold-ic { margin-left: auto; color: var(--text-muted); font-size: .85rem;
              transition: transform .22s cubic-bezier(.16,1,.3,1); }
.xe-fold[open] > summary > .xe-fold-ic { transform: rotate(180deg); }

/* ── สองแผงบนหัวหน้าออกข้อสอบต้องแยกออกจากกันด้วยตา (9 ก.ย. 2569) ──────────
   "นำเข้าข้อสอบทีละหลายข้อ" กับ "ชุดสถานการณ์ (PISA)" เคยเป็นแผงพับหน้าตา
   เดียวกันวางซ้อนกันสองอัน ครูจึงเปิดผิดแผคตลอด (เจ้าของแจ้งว่า *"ดูปน ๆ กัน
   ทำให้ดูยากทั้ง 2 ฟังก์ชัน"*) · ให้ไอคอนของแต่ละแผงอยู่ในกรอบสีคนละสี และ
   ขีดสีบางที่ขอบซ้าย — พอที่จะจำได้ว่า "อันฟ้าคือเอาไฟล์เข้า อันเขียวคือ PISA"
   โดยไม่ต้องอ่านหัวข้อซ้ำทุกครั้ง                                            */
.xe-fold-imp > summary > .t > i,
.xe-fold-pisa > summary > .t > i {
  display: inline-grid; place-items: center; width: 30px; height: 30px;
  border-radius: var(--r-sm); margin-right: 2px; font-size: .92rem;
}
.xe-fold-imp  { border-left: 3px solid var(--sk); }
.xe-fold-pisa { border-left: 3px solid var(--green); }
.xe-fold-imp  > summary > .t > i { background: var(--sk-wash);   color: var(--sk-ink); }
.xe-fold-pisa > summary > .t > i { background: var(--rd-wash-green); color: var(--green-ink); }

/* ════════════════════════════════════════════════════════════════════════════
   มือถือ / iPad — แก้สามเรื่องที่ทำให้ "อัตราส่วนเพี้ยนและตัวหนังสือโดนตัด"
   (18 ส.ค. 2569)
════════════════════════════════════════════════════════════════════════════ */

/* ── 1. หน่วย vh บนมือถือไม่ใช่ความสูงที่มองเห็นจริง ────────────────────────
   `100vh` ของเบราว์เซอร์มือถือ = ความสูง **ตอนที่แถบ URL หดหมดแล้ว** ซึ่งเป็น
   ค่าที่สูงที่สุดที่เป็นไปได้ ไม่ใช่ค่าที่เห็นอยู่ตรงหน้า · แอปนี้วาง
   `.app-shell { height:100vh; overflow:hidden }` แล้วให้ `.page-main` เป็นตัวเลื่อน
   ผลคือส่วนล่างของแอปสูงกว่าจอจริงอยู่ประมาณความสูงแถบเบราว์เซอร์ (60-115px)
   และ **เลื่อนลงไปดูไม่ได้เลย** เพราะเอกสารทั้งหน้าไม่ได้เลื่อน มีแต่กล่องข้างใน
   — นี่คืออาการ "เปิดในมือถือแล้วใช้ยาก ของโดนตัด" ที่ผู้ใช้แจ้ง
   `dvh` (dynamic viewport height) คือความสูงที่มองเห็นจริงและอัปเดตตามแถบ URL
   ประกาศ `vh` ไว้ก่อนเป็นทางถอยให้เบราว์เซอร์เก่าที่ไม่รู้จัก dvh             */
body { min-height: 100vh; min-height: 100dvh; }
.app-shell { height: 100vh; height: 100dvh; }
.dc-canvas { min-height: 100vh; min-height: 100dvh; }

/* ── 2. `viewport-fit=cover` ตั้งไว้แล้วแต่ไม่เคยเผื่อ safe-area ────────────
   meta viewport ของ header.php สั่ง `viewport-fit=cover` ซึ่งบอกให้เนื้อหา
   กินพื้นที่ใต้ติ่งกล้องและแถบ home ของ iPhone ด้วย · ถ้าไม่ชดเชยด้วย
   `env(safe-area-inset-*)` ต่อ มันจะแย่กว่าไม่ตั้งเลย เพราะแถวล่างสุดของทุกหน้า
   จะถูกแถบ home ทับ และแนวนอนบน iPhone มีติ่งจะกินขอบซ้าย/ขวาไปอีกข้างละ ~44px */
.app-topbar {
  padding-left: max(18px, env(safe-area-inset-left));
  padding-right: max(18px, env(safe-area-inset-right));
  padding-top: env(safe-area-inset-top);
  height: calc(62px + env(safe-area-inset-top));
}
.app-sidebar { padding-bottom: calc(10px + env(safe-area-inset-bottom)); }
.page-inner {
  padding-left: max(26px, env(safe-area-inset-left));
  padding-right: max(26px, env(safe-area-inset-right));
  padding-bottom: calc(40px + env(safe-area-inset-bottom));
}

/* ── 3. iOS ซูมหน้าทั้งหน้าเมื่อโฟกัสช่องกรอกที่ตัวอักษรเล็กกว่า 16px ───────
   Safari บน iPhone/iPad ขยายหน้าเข้าอัตโนมัติเมื่อแตะช่องที่ font-size < 16px
   แล้ว **ไม่ย่อกลับ** ผู้ใช้ต้องหุบนิ้วเองทุกครั้ง ระหว่างนั้นเลย์เอาต์เลื่อน
   ไปคนละที่ = "สัดส่วนเพี้ยน" ที่เจอบ่อยที่สุดในหน้ากรอกคะแนน/ค้นหา
   16px คือเส้นแบ่งพอดี ไม่ใช่ค่าที่เลือกเพราะสวย                              */
@media (max-width: 991.98px) {
  input:not([type=checkbox]):not([type=radio]):not([type=range]),
  select, textarea,
  .form-control, .form-control-sm, .form-select, .form-select-sm,
  .score-input {
    font-size: 16px !important;
  }
}

@media (max-width: 820px) {
  .app-sidebar { top: calc(62px + env(safe-area-inset-top)); }
  .page-inner {
    padding-left: max(14px, env(safe-area-inset-left));
    padding-right: max(14px, env(safe-area-inset-right));
    padding-bottom: calc(32px + env(safe-area-inset-bottom));
  }
  /* ตารางกว้างต้องเลื่อนใน "กล่องของตัวเอง" ไม่ใช่ดันทั้งหน้า —
     `-webkit-overflow-scrolling` ทำให้การเลื่อนบน iOS ลื่นแทนที่จะกระตุก */
  .table-responsive, .tbl-scroll { -webkit-overflow-scrolling: touch; }
}

@media (max-width: 575px) {
  .page-inner {
    padding-left: max(12px, env(safe-area-inset-left));
    padding-right: max(12px, env(safe-area-inset-right));
    padding-bottom: calc(28px + env(safe-area-inset-bottom));
  }
  /* ── ตัวหนังสือโดนตัด ──────────────────────────────────────────────────
     คำไทยยาว ๆ ("การประเมินคุณลักษณะอันพึงประสงค์") ไม่มีช่องว่างให้เบราว์เซอร์
     ตัดบรรทัด พอจอแคบมันจึงล้นกล่องแล้วโดน overflow:hidden เฉือนหาย
     `overflow-wrap:anywhere` ยอมให้ตัดกลางคำเมื่อ **ไม่มีทางอื่นแล้ว** เท่านั้น
     (ต่างจาก `word-break:break-all` ที่ตัดกลางคำตลอดเวลาจนอ่านยากไปอีกแบบ) */
  .card, .rd-panel, .rd-stat, .cw-task, .modal-content, .list-group-item,
  .alert, .badge, .btn, .rd-stat-note, .rd-stat-label {
    overflow-wrap: anywhere;
  }

  /* ══ ห้ามใส่ `overflow-wrap:anywhere` ให้ td/th เด็ดขาด ═══════════════════
     **นี่คือต้นเหตุที่ตารางทั้งเว็บพังบนมือถือ** (เจอจากภาพที่ครูส่งมา 21 ส.ค.)

     `anywhere` ต่างจาก `break-word` ตรงจุดเดียวแต่เป็นจุดตาย: มันนับรวมเข้าไป
     ใน **min-content** ของกล่องด้วย · พอ td บอกว่า "ตัดตรงไหนก็ได้" ความกว้าง
     ขั้นต่ำของคอลัมน์จึงเหลือ **หนึ่งตัวอักษร** เบราว์เซอร์เลยยินดีบีบทุกคอลัมน์
     ให้ผอมจนตารางพอดีจอ แทนที่จะปล่อยให้ล้นแล้วเลื่อนแนวนอน
     ผลคือ "นางสาวทัศนีย์ แกมจำเริญ" กลายเป็นตัวอักษรเรียงลงมาแนวตั้งทีละตัว
     และ "100.0%" กลายเป็น 1/0/0/./0 คนละบรรทัด — วัดได้ 1.0-1.3 ตัวอักษร/บรรทัด

     `break-word` ให้ผลที่ต้องการ (คำไทยยาวเกินช่องยอมตัดได้) โดย **ไม่แตะ
     min-content** คอลัมน์จึงยังกว้างเท่าคำที่สั้นที่สุดที่ยอมรับได้ แล้วตาราง
     ที่กว้างเกินจอก็ล้นออกไปให้กล่องครอบเลื่อนแทน ซึ่งคือพฤติกรรมที่ถูก        */
  td, th { overflow-wrap: break-word; }

  /* ตารางที่กว้างเกินจอต้องเลื่อนได้ ไม่ใช่ถูกบีบ — `width:100%` ของ Bootstrap
     ทำให้ตารางไม่มีวันกว้างเกินกล่องครอบ กล่องที่ตั้ง overflow-x ไว้จึงไม่เคย
     ได้เลื่อนเลยสักครั้ง · ปล่อยให้ความกว้างมาจากเนื้อหาแทนบนจอแคบ */
  .table-responsive > .table,
  .gradebook-wrapper > .table { width: auto; min-width: 100%; }

  /* บอกให้รู้ว่าเลื่อนได้ — เงาบางที่ขอบขวาหายไปเมื่อเลื่อนสุดแล้ว
     (background-attachment:local ทำให้เงาเลื่อนตามเนื้อหา ไม่ต้องใช้ JS เลย) */
  .table-responsive, .gradebook-wrapper {
    background-image: linear-gradient(to right, transparent, transparent),
                      linear-gradient(to left, color-mix(in srgb, var(--text-muted) 22%, transparent), transparent);
    background-position: left center, right center;
    background-size: 1px 100%, 14px 100%;
    background-repeat: no-repeat;
    background-attachment: local, scroll;
  }
  /* หัวเรื่องยาวบนจอแคบต้องยอมพันบรรทัด ไม่ใช่ถูกตัดท้าย */
  h1, h2, h3, h4, h5, .h1, .h2, .h3, .h4, .h5 { overflow-wrap: anywhere; }

  /* ── ปุ่มกลุ่มยาว ๆ ล้นออกนอกจอ ────────────────────────────────────────
     **กฎเดิมเขียนผิดตำแหน่งจนไม่เคยทำงานเลย** — `flex-wrap` เป็นคุณสมบัติของ
     *ตัวกล่อง* ไม่ใช่ของลูก การเขียน `.d-flex.gap-2 > .btn { flex-wrap: wrap }`
     จึงไปตั้งให้ปุ่มแต่ละใบ (ซึ่งไม่ใช่ flex container) แล้วไม่มีผลอะไร

     ผลคือแถบปุ่มยังกว้างเกินจอจริง (วัดที่ 375px: แถบปุ่มหน้าเช็คชื่อกว้าง 385px
     · แถบปุ่มหน้าสุ่มรายชื่อกว้าง 430px) แล้วโดน `overflow-x:hidden` ของ
     `.page-inner` เฉือนทิ้งเงียบ ๆ ปุ่มขวาสุดหายไปจากจอโดยไม่มีอะไรบอก      */
  .card-header .btn-group,
  .d-flex.gap-2, .d-flex.gap-3,
  .rp-control-pills, .rp-modes-bar { flex-wrap: wrap; }

  /* เป้าแตะขั้นต่ำ 40px ตามแนวทาง WCAG 2.2 (2.5.8) — ปุ่มไอคอนในแถวตาราง
     ปัจจุบันสูง ~28px ซึ่งบนจอสัมผัสกดพลาดเป็นเรื่องปกติ */
  .btn-sm, .sg-del, .btn-icon { min-height: 38px; }
  .table td .btn-sm, .table th .btn-sm { min-height: 34px; }
}

/* ── หมุนจอแนวนอนบนมือถือจอเตี้ย ──────────────────────────────────────────
   แถบบน 62px + แถบแท็บ + hero กินความสูงเกือบหมดจนไม่เหลือที่ให้เนื้อหา       */
@media (max-height: 460px) and (orientation: landscape) {
  .app-topbar { height: calc(52px + env(safe-area-inset-top)); }
  .dash-hero { padding: 12px 14px; }
  .dash-hero h3 { font-size: 1.05rem; }
  .app-sidebar { top: calc(52px + env(safe-area-inset-top)); }
}

/* ── ฉากหลังของลิ้นชักเมนูบนมือถือ ────────────────────────────────────────
   ลิ้นชักกว้าง 200px ทับครึ่งจอบนโทรศัพท์ · ถ้าไม่หรี่ข้างหลังไว้ผู้ใช้แยกไม่ออก
   ว่าเมนูทับอยู่หรือหน้าเปลี่ยนไปแล้ว และไม่มีทางปิดนอกจากกลับไปหาปุ่มแฮมเบอร์เกอร์
   `hidden` เป็นค่าเริ่มต้น จึงต้องมี `[hidden]{display:none}` ระดับที่ชนะกฎนี้ได้     */
.sidebar-scrim {
  position: fixed; inset: 0; z-index: 19;
  background: rgba(6, 26, 43, .42);
  -webkit-backdrop-filter: blur(1px); backdrop-filter: blur(1px);
  animation: sbScrimIn .18s ease;
}
.sidebar-scrim[hidden] { display: none; }
@keyframes sbScrimIn { from { opacity: 0 } to { opacity: 1 } }
/* เหนือ 820px ลิ้นชักเป็นแถบข้างถาวร ไม่ใช่ของที่เปิด-ปิด จึงไม่ต้องมีฉากหลัง */
@media (min-width: 821px) { .sidebar-scrim { display: none !important; } }
/* ล็อกการเลื่อนพื้นหลังระหว่างเปิดลิ้นชัก — นิ้วที่ปัดในเมนูต้องไม่ไปเลื่อนหน้าข้างหลัง */
body.sb-open .page-main { overflow: hidden; }
@media (prefers-reduced-motion: reduce) { .sidebar-scrim { animation: none; } }

/* ══════════════════════════════════════════════════════════════════════════
   ปุ่มดาวน์โหลด `.sg-dlbtn` — แบบที่เจ้าของเลือกมา (18 ส.ค. 2569)
   ขอบหนา + เงาทึบเยื้อง 4px + แผงไอคอนที่กางคลุมทั้งปุ่มตอน hover
══════════════════════════════════════════════════════════════════════════ */

/* **ไม่ใช้ชื่อ `.button` ตามต้นฉบับ** — `.button` เป็นชื่อที่กว้างเกินไป และ
   theme.css โหลดหลัง Bootstrap กฎที่ตั้งความกว้าง/เงา/ขอบให้ `.button` จะไป
   โดนอย่างอื่นที่บังเอิญใช้ชื่อนี้ (บทเรียนเดียวกับ `.btn` `.stat` `.hero`
   ตอน REDESIGN ที่ต้อง prefix `rd-` ทั้งชุด)

   **ไม่ตรึงความกว้างที่ 150px ตามต้นฉบับด้วย** — ต้นฉบับคำนวณ translateX(109px)
   และ hover width 148px จากเลข 150 ตายตัว ซึ่งพอดีกับคำว่า "Download" คำเดียว
   ป้ายไทยในระบบนี้ยาวกว่านั้นมาก ("ดาวน์โหลดไฟล์" · "ติดตั้งแอปเลย") ถ้าตรึงไว้
   ตัวหนังสือจะทะลุออกนอกปุ่มทันที (บั๊กเดียวกับที่เพิ่งแก้ให้ `.sg-back`)
   จึงให้ตัวปุ่มกว้างตามเนื้อหา แล้วเว้นที่ขวาไว้ให้แผงไอคอนด้วย padding-right */
.sg-dlbtn {
  --dl-face:   var(--bg-card);
  --dl-sub:    color-mix(in srgb, var(--sk) 12%, var(--bg-soft));
  --dl-ink:    var(--text-main);
  --dl-line:   var(--sk-deep);
  --dl-pad:    42px;                 /* ความกว้างแผงไอคอน */

  position: relative;
  display: inline-flex;
  align-items: center;
  height: 40px;
  padding: 0 calc(var(--dl-pad) + 14px) 0 16px;
  cursor: pointer;
  border: 2px solid var(--dl-line);
  border-radius: 10px;
  background: var(--dl-face);
  color: var(--dl-ink);
  font-weight: 700;
  font-size: .92rem;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  overflow: hidden;
  box-shadow: 4px 4px 0 var(--dl-line);
  transition: background .3s, box-shadow .25s, transform .12s;
}
.sg-dlbtn__text { position: relative; z-index: 1; transition: color .3s; }
.sg-dlbtn__icon {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: var(--dl-pad);
  display: flex; align-items: center; justify-content: center;
  background: var(--dl-sub);
  border-left: 2px solid var(--dl-line);
  transition: width .3s, border-color .3s;
}
.sg-dlbtn__icon svg { width: 20px; height: 20px; fill: var(--dl-line); transition: fill .3s; }

/* hover: แผงไอคอนกางคลุมทั้งปุ่ม ตัวหนังสือจางหายไปข้างใต้ */
.sg-dlbtn:hover .sg-dlbtn__icon,
.sg-dlbtn:focus-visible .sg-dlbtn__icon { width: 100%; border-left-color: transparent; }
.sg-dlbtn:hover .sg-dlbtn__text,
.sg-dlbtn:focus-visible .sg-dlbtn__text { color: transparent; }
.sg-dlbtn:active { transform: translate(3px, 3px); box-shadow: 0 0 0 var(--dl-line); }
.sg-dlbtn:focus-visible { outline: 3px solid color-mix(in srgb, var(--sk) 55%, transparent); outline-offset: 3px; }

/* โหมดมืด: `html.dark a` (0,2,1) ชนะ `.sg-dlbtn` (0,1,0) — ถ้าไม่ทวงสีคืน
   ปุ่มที่เป็น <a> จะกลายเป็นตัวหนังสือฟ้าอ่อนบนพื้นการ์ด (กับดักเดียวกับ .btn-brand) */
html.dark .sg-dlbtn,
html.dark a.sg-dlbtn,
html.dark a.sg-dlbtn:hover {
  --dl-face: var(--bg-card);
  --dl-sub:  color-mix(in srgb, var(--sk) 26%, var(--bg-soft));
  --dl-ink:  var(--text-main);
  --dl-line: var(--sk);
  color: var(--dl-ink);
}

/* แบบเน้น — ใช้กับปุ่มหลักหนึ่งเดียวของหน้า (เช่น "ติดตั้งแอปเลย") */
.sg-dlbtn.is-primary { --dl-face: var(--btn-face); --dl-ink: var(--btn-on); --dl-line: var(--sk-deep);
                       --dl-sub: color-mix(in srgb, #000 16%, var(--btn-face)); }
.sg-dlbtn.is-primary .sg-dlbtn__icon svg { fill: var(--btn-on); }
html.dark .sg-dlbtn.is-primary, html.dark a.sg-dlbtn.is-primary,
html.dark a.sg-dlbtn.is-primary:hover { color: var(--btn-on); }

/* ขนาดเล็ก — สำหรับแถวไฟล์แนบที่อยู่ข้างปุ่มอื่น */
.sg-dlbtn.is-sm { height: 34px; font-size: .84rem; --dl-pad: 36px; padding-left: 13px;
                  box-shadow: 3px 3px 0 var(--dl-line); border-radius: 9px; }
.sg-dlbtn.is-sm .sg-dlbtn__icon svg { width: 17px; height: 17px; }

@media (prefers-reduced-motion: reduce) {
  .sg-dlbtn, .sg-dlbtn__icon, .sg-dlbtn__text { transition: none; }
}
/* จอสัมผัสไม่มี hover จริง — เอฟเฟกต์กางแผงจะค้างหลังแตะ ทำให้ปุ่มกลายเป็นกล่อง
   ทึบไม่มีตัวหนังสือ ปิดไปเลยบนอุปกรณ์สัมผัส ปุ่มยังกดได้เหมือนเดิมทุกอย่าง */
@media (hover: none), (pointer: coarse) {
  .sg-dlbtn:hover .sg-dlbtn__icon { width: var(--dl-pad); border-left-color: var(--dl-line); }
  .sg-dlbtn:hover .sg-dlbtn__text { color: var(--dl-ink); }
}

/* กระพริบกรอบขั้นตอนติดตั้งเมื่อกดปุ่ม "โหลดแอปลงเครื่อง" บนเครื่องที่ติดตั้ง
   ด้วยโค้ดไม่ได้ (iOS) — บนจอมือถือการเลื่อนหน้าเฉย ๆ แยกไม่ออกว่าขยับเพราะอะไร */
.pwa-flash { animation: pwaFlash 1.6s ease; }
@keyframes pwaFlash {
  0%, 100% { box-shadow: var(--shadow); border-color: var(--border-color); }
  20%, 60% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--sk) 38%, transparent);
             border-color: var(--sk-dk); }
}
@media (prefers-reduced-motion: reduce) { .pwa-flash { animation: none; } }

/* ══════════════════════════════════════════════════════════════════════════
   คัดกรองกลุ่มเสี่ยง `.rk-*` — ออกแบบใหม่ 18 ส.ค. 2569
   ของเดิมเป็นตารางแถวละ "คน×วิชา" พร้อมป้ายสีเปล่า ๆ ("เสี่ยง มส") ที่บอกแค่
   ผลลัพธ์ ไม่บอกที่มา · คนเดียวโผล่หลายแถวติดกันโดยไม่มีอะไรบอกว่าเป็นคนเดียวกัน
   แบบใหม่เป็นการ์ดต่อ "คน" แล้วในนั้นแตกเป็นวิชา และทุกธงมีบรรทัดอธิบายเหตุผล
   ด้วยตัวเลขจริง ("ขาด 1 จาก 8 คาบ = 12.5% เกินเพดาน 20%")
══════════════════════════════════════════════════════════════════════════ */
.rk-rules { border-left: 4px solid var(--sk-dk); }
.rk-rulegrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 14px; }
.rk-rule { display: flex; align-items: center; gap: 10px; }
.rk-rule-n { font-size: 1.5rem; font-weight: 800; color: var(--sk-ink); line-height: 1;
             flex-shrink: 0; min-width: 2.4rem; text-align: center; font-variant-numeric: tabular-nums; }
.rk-rule-l { font-size: .82rem; line-height: 1.35; font-weight: 600; }
/* **ต้องตั้ง color เอง** — `--bg-soft` ในโหมดมืดเป็นพื้นสว่างกว่าการ์ด แต่ตัวหนังสือ
   สืบทอดสีจากการ์ดซึ่งเป็นโทนสว่างอยู่แล้ว = สว่างบนสว่าง อ่านไม่ออกทั้งกล่อง
   (เจอจากภาพหน้าจอโหมดมืด ไม่ใช่จากตัวตรวจ contrast เพราะกล่องนี้ไม่มี text node
   ที่ระดับบนสุด) กฎเดียวกับที่ CLAUDE.md เตือนเรื่อง `.rd-stat` ที่ต้องตั้งพื้น
   กับหมึกให้ครบในกฎเดียวกันเสมอ */
.rk-rulehelp { background: color-mix(in srgb, var(--sk) 9%, var(--bg-card));
               border: 1px solid var(--border-color);
               color: var(--text-main);
               border-radius: var(--r-md); padding: 12px 14px;
               font-size: .84rem; line-height: 1.55; max-width: 420px; }
.rk-rulehelp b { color: var(--sk-ink); }
html.dark .rk-rulehelp { background: color-mix(in srgb, var(--sk) 18%, var(--bg-card));
                         color: var(--text-main); }
.rk-editlink, .rk-customchip {
  display: inline-flex; align-items: center; gap: 4px; text-decoration: none;
  background: var(--bg-card); border: 1px solid var(--border-color);
  border-radius: var(--r-pill); padding: 3px 11px; font-size: .78rem; font-weight: 700;
  color: var(--sk-ink); transition: .15s;
}
.rk-editlink:hover, .rk-customchip:hover { border-color: var(--sk-dk); color: var(--sk-dk); transform: translateY(-1px); }
.rk-customchip { gap: 7px; font-weight: 600; }
.rk-customchip b { color: var(--sk-ink); }
.rk-customchip.off { opacity: .75; }
.rk-customchip.off b { color: var(--text-muted); }
html.dark a.rk-editlink, html.dark a.rk-customchip, html.dark a.rk-mini { color: var(--sk-ink); }

.rk-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; }
.rk-stat { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--r-lg);
           padding: 16px 18px; border-top: 4px solid var(--rk-c, var(--sk-dk)); box-shadow: var(--shadow); }
.rk-stat .n { font-size: 2rem; font-weight: 800; line-height: 1; color: var(--rk-c, var(--sk-ink));
              font-variant-numeric: tabular-nums; }
.rk-stat .l { font-weight: 700; margin-top: 4px; }
.rk-stat .s { font-size: .76rem; color: var(--text-muted); margin-top: 3px; line-height: 1.4; }
.rk-stat.s-people { --rk-c: var(--sk-dk); }
.rk-stat.s-ms     { --rk-c: var(--red-ink); }
.rk-stat.s-fail   { --rk-c: var(--gold-ink); }
.rk-stat.s-miss   { --rk-c: var(--f-violet-ink, #6d28d9); }


.rk-subj { padding: 14px 18px; border-bottom: 1px dashed var(--border-color); }
.rk-subj:last-child { border-bottom: 0; }
.rk-subj-hd { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.rk-code { font-size: .74rem; font-weight: 700; letter-spacing: .01em;
            background: color-mix(in srgb, var(--sk) 14%, var(--bg-card));
            color: var(--sk-ink); border: 1px solid var(--border-color);
            border-radius: var(--r-xs); padding: 2px 8px; flex-shrink: 0; }
.rk-mini { font-size: .76rem; font-weight: 700; text-decoration: none; color: var(--sk-ink);
           border: 1px solid var(--border-color); border-radius: var(--r-pill); padding: 2px 10px; }
.rk-mini:hover { border-color: var(--sk-dk); color: var(--sk-dk); }
html.dark a.rk-mini { color: var(--sk-ink); }

.rk-reasons { display: flex; flex-direction: column; gap: 8px; }
.rk-reason { display: flex; gap: 10px; align-items: flex-start;
             background: var(--rk-wash); border-left: 3px solid var(--rk-line);
             border-radius: var(--r-sm); padding: 9px 12px; }
.rk-reason > i { color: var(--rk-line); font-size: 1rem; line-height: 1.4; flex-shrink: 0; }
.rk-reason b { display: block; font-size: .88rem; color: var(--rk-ink); }
.rk-reason span { display: block; font-size: .8rem; color: var(--text-muted); line-height: 1.5; }
/* ทั้งสามโทนต้องตั้ง --rk-wash/--rk-line/--rk-ink ครบในกฎเดียวกัน — เว้นตัวใดตัวหนึ่ง
   จะไปหยิบค่าของโทนก่อนหน้า แล้วได้กล่องพื้นแดงเส้นทอง (บทเรียนจาก .rd-stat) */
.rk-reason.r-ms   { --rk-wash: color-mix(in srgb, var(--red) 12%, var(--bg-card));
                    --rk-line: var(--red-ink);   --rk-ink: var(--red-ink); }
.rk-reason.r-fail { --rk-wash: color-mix(in srgb, var(--gold) 16%, var(--bg-card));
                    --rk-line: var(--gold-ink);  --rk-ink: var(--gold-ink); }
.rk-reason.r-miss { --rk-wash: color-mix(in srgb, var(--sk) 12%, var(--bg-card));
                    --rk-line: var(--sk-dk);     --rk-ink: var(--sk-ink); }

.rk-facts { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 10px;
            font-size: .82rem; color: var(--text-muted); }
.rk-facts b { color: var(--text-main); }
.rk-facts i { color: var(--sk-dk); margin-right: 3px; }

@media (max-width: 575px) {
  .rk-rulehelp { max-width: 100%; }
  .rk-subj-hd .ms-auto { margin-left: 0 !important; width: 100%; }
}

/* โทน "ปกติ" ของกล่องเหตุผล — ต้องตั้ง --rk-wash/--rk-line/--rk-ink ครบในกฎเดียว
   เหมือนอีกสามโทน ไม่งั้นจะไปหยิบค่าของโทนก่อนหน้าแล้วได้พื้นเขียวเส้นแดง */
.rk-reason.r-ok { --rk-wash: color-mix(in srgb, var(--green) 12%, var(--bg-card));
                  --rk-line: var(--green-ink); --rk-ink: var(--green-ink); }

/* ══════════════════════════════════════════════════════════════════════════
   สมุดคะแนน — ออกแบบใหม่ 19 ส.ค. 2569 (Operate: ครูอยู่หน้านี้ทั้งวัน)
   สามอย่างที่แก้: ตารางขึ้นมาอยู่ในจอแรก · หัวคอลัมน์อ่านออก · เห็นช่องที่ยังว่าง
══════════════════════════════════════════════════════════════════════════ */

/* หมายเหตุ: เคยลองยุบการ์ดสถิติสี่ใบเป็นแถบเดียว (`.gb-summary`) เพื่อดันตาราง
   ขึ้นมาอยู่ในจอแรก — **เจ้าของสั่งคืนหัวเป็นแบบเดิม 19 ส.ค. 2569** จึงถอด CSS
   ชุดนั้นออกทั้งก้อน ไม่ปล่อยไว้เป็นกฎกำพร้าที่ไม่มี HTML ตัวไหนใช้
   ส่วนที่เก็บไว้คือการจัดตารางใหม่ ซึ่งเป็นคนละเรื่องกับหัวหน้า              */

/* ── หัวตารางสองชั้น จัดกลุ่มตามหมวด ─────────────────────────────────────
   สีหมวดเป็น "เครื่องหมาย" ที่เข้ารหัสข้อมูล (หมวดคะแนนตาม ปพ.5) จึงเข้าข่าย
   The Data-Bar Rule ที่ DESIGN.md อนุญาต — ไม่ใช่แถบสีตกแต่ง                */
.gradebook-table thead .gb-cat {
  text-align: center; font-size: .76rem; font-weight: 700; letter-spacing: .01em;
  padding: 7px 10px 6px; white-space: nowrap;
  border-bottom: 2px solid var(--gb-cat, var(--border-color));
  color: var(--gb-cat-ink, var(--text-muted));
  background: var(--gb-cat-wash, transparent);
}
.gradebook-table thead .gb-cat-max { display: block; font-size: .66rem; font-weight: 600; opacity: .8; }
.gb-cat-pre  { --gb-cat: var(--f-sky-dk,   #0369a1); --gb-cat-ink: var(--f-sky-ink,   #024164);
               --gb-cat-wash: color-mix(in srgb, var(--f-sky-dk,#0369a1) 9%, transparent); }
.gb-cat-mid  { --gb-cat: var(--f-violet-dk,#6d28d9); --gb-cat-ink: var(--f-violet-ink,#4c1d95);
               --gb-cat-wash: color-mix(in srgb, var(--f-violet-dk,#6d28d9) 9%, transparent); }
.gb-cat-post { --gb-cat: var(--f-teal-dk,  #0f766e); --gb-cat-ink: var(--f-teal-ink,  #134e4a);
               --gb-cat-wash: color-mix(in srgb, var(--f-teal-dk,#0f766e) 9%, transparent); }
.gb-cat-fin  { --gb-cat: var(--f-orange-dk,#c2410c); --gb-cat-ink: var(--f-orange-ink,#7c2d12);
               --gb-cat-wash: color-mix(in srgb, var(--f-orange-dk,#c2410c) 9%, transparent); }
.gb-cat-sum  { --gb-cat: var(--sk-deep); --gb-cat-ink: var(--sk-ink); }
html.dark .gradebook-table thead .gb-cat { color: var(--text-main); }

/* หัวรายการ: ชื่อเต็มอ่านออก (สองบรรทัด) + เต็ม + ความคืบหน้าการกรอก */
.gradebook-table thead .gb-ith { vertical-align: top; padding: 7px 8px 8px !important; }
.gb-ith-t { font-size: .76rem; font-weight: 600; line-height: 1.25;
  color: var(--text-main); overflow: hidden;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.gb-ith-m { display: block; font-size: .8rem; font-weight: 700; color: var(--sk-ink);
  font-variant-numeric: tabular-nums; margin-top: 2px; }
.gb-ith-f { display: inline-flex; align-items: center; gap: 2px; margin-top: 3px;
  font-size: .64rem; font-weight: 700; padding: 1px 6px; border-radius: 99px;
  font-variant-numeric: tabular-nums; }
.gb-ith-f.is-full { background: color-mix(in srgb, var(--green) 16%, var(--bg-card)); color: var(--green-ink); }
.gb-ith-f.is-part { background: color-mix(in srgb, var(--gold) 20%, var(--bg-card)); color: var(--gold-ink); }
.gb-ith-f.is-none { background: var(--bg-soft); color: var(--text-muted); }
.gb-sum-th { font-size: .78rem; }

/* ── ช่องที่ยังไม่กรอก ต้องมองออกจากหางตา ────────────────────────────────
   ของเดิมทุกช่องเป็นกล่องขอบเหมือนกันหมด 54×5 = 270 กล่อง ช่องว่างกับช่องที่มี
   คะแนนหน้าตาเท่ากัน ครูจึงต้องอ่านตัวเลขทีละช่องเพื่อหาว่ายังขาดใคร
   ช่องว่าง = พื้นลายทางจาง ๆ ไม่มีขอบ · ช่องที่มีคะแนน = กล่องขอบชัดตามเดิม  */
.gradebook-table .score-input:placeholder-shown {
  border-color: transparent;
  background: repeating-linear-gradient(-45deg,
      transparent 0 5px, color-mix(in srgb, var(--text-muted) 11%, transparent) 5px 6px);
  color: var(--text-muted);
}
.gradebook-table .score-input:placeholder-shown:hover,
.gradebook-table .score-input:focus { background: var(--bg-card); border-color: var(--sk-dk); }
/* ไล่เฉดพื้นคอลัมน์ตามหมวด จาง ๆ พอให้ตาแยกบล็อกได้ ไม่ใช่สีเต็ม */
.gradebook-table td.gb-c-pre  { background: color-mix(in srgb, var(--f-sky-dk,#0369a1)    3.5%, transparent); }
.gradebook-table td.gb-c-mid  { background: color-mix(in srgb, var(--f-violet-dk,#6d28d9) 3.5%, transparent); }
.gradebook-table td.gb-c-post { background: color-mix(in srgb, var(--f-teal-dk,#0f766e)   3.5%, transparent); }
.gradebook-table td.gb-c-fin  { background: color-mix(in srgb, var(--f-orange-dk,#c2410c) 3.5%, transparent); }

@media (max-width: 575px) {
  .gb-summary { border-radius: var(--r-md); }
  .gb-sum-item { padding: 8px 12px; }
  .gb-sum-v { font-size: 1.2rem; }
  .gb-sum-dist { margin-left: 0; border-left: 0; border-top: 1px solid var(--border-color); width: 100%; }
  .gb-dist-pop { right: auto; left: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .gb-sum-bar > span, .gb-sum-dist .gb-dist-chev { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   ระบบข้อสอบ — หน้ารายการ `.xh-*` (ออกแบบใหม่ 19 ส.ค. 2569)
   การ์ดหนึ่งใบ = แผงควบคุมของข้อสอบชุดนั้น แบ่งสามโซน:
   เนื้อหา/สถานะ (ซ้าย) · ความคืบหน้า + การกระทำ (ขวา)
══════════════════════════════════════════════════════════════════════════ */
.xh-card {
  display: grid; grid-template-columns: minmax(0,1fr) 268px; gap: 0;
  background: var(--bg-card); border: 1px solid var(--border-color);
  border-radius: var(--r-lg); overflow: hidden;
  transition: border-color .18s cubic-bezier(.16,1,.3,1);
}
.xh-card:hover { border-color: var(--sk-lt); }
/* มีของค้างมือ = ขอบเน้น · ไม่ใช่แถบสีตกแต่ง แต่เข้ารหัสว่า "ใบนี้ต้องลงมือทำ" */
.xh-card.has-todo { border-color: color-mix(in srgb, var(--gold) 62%, var(--border-color)); }

.xh-main { padding: 18px 20px; min-width: 0; }
.xh-title { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font-size: 1.12rem; font-weight: 700; color: var(--text-main); line-height: 1.3; }
.xh-desc { font-size: .86rem; color: var(--text-muted); margin: 6px 0 0; line-height: 1.55; }

/* สเปก: ตัวเลขที่ต้องอ่านจริง — ตัวเลขหนา ป้ายเบา คู่กันเป็นก้อน */
.xh-specs { display: flex; flex-wrap: wrap; gap: 18px; margin-top: 12px; }
.xh-spec { font-size: .82rem; color: var(--text-muted); }
.xh-spec b { font-size: 1.15rem; font-weight: 700; color: var(--text-main);
  font-variant-numeric: tabular-nums; margin-right: 3px; letter-spacing: -.02em; }

/* ตัวเลือกเปิด-ปิด: ชิปเล็ก แยกระนาบจากสเปกชัดเจน */
.xh-opts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.xh-opt { display: inline-flex; align-items: center; gap: 4px;
  font-size: .72rem; font-weight: 600; padding: 2px 9px; border-radius: var(--r-pill);
  background: var(--bg-soft); color: var(--text-muted); }
.xh-opt i { font-size: .74rem; }

.xh-when { display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  margin-top: 12px; padding-top: 11px; border-top: 1px dashed var(--border-color);
  font-size: .8rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.xh-when i { color: var(--sk-dk); }
.xh-arrow { color: var(--text-muted); opacity: .6; }

/* ── โซนขวา: ความคืบหน้า + การกระทำ ─────────────────────────────────────── */
.xh-side { display: flex; flex-direction: column; gap: 10px; padding: 18px 20px;
  border-left: 1px solid var(--border-color);
  background: color-mix(in srgb, var(--sk) 3.5%, var(--bg-card)); }
.xh-prog-l { font-size: .74rem; font-weight: 600; color: var(--text-muted); }
.xh-prog-v { font-size: 1.5rem; font-weight: 700; line-height: 1.1; color: var(--text-main);
  font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.xh-prog-v span { font-size: .82rem; font-weight: 600; color: var(--text-muted); margin-left: 2px; }
.xh-prog-bar { display: block; height: 6px; border-radius: 99px; margin-top: 6px;
  background: var(--bg-soft); overflow: hidden; }
.xh-prog-bar > span { display: block; height: 100%; border-radius: 99px;
  background: var(--sk-dk); transition: width .45s cubic-bezier(.16,1,.3,1); }
.xh-todo { display: flex; align-items: center; gap: 5px; margin-top: 7px;
  font-size: .76rem; font-weight: 700; color: var(--gold-ink); }
.xh-ok { display: flex; align-items: center; gap: 5px; margin-top: 7px;
  font-size: .76rem; color: var(--text-muted); }
.xh-ok i { color: var(--green-ink); }
.xh-best { font-size: .8rem; color: var(--text-muted); margin-top: 6px; }
.xh-best b { font-size: 1.05rem; color: var(--text-main); font-variant-numeric: tabular-nums; }

.xh-primary { width: 100%; justify-content: center; margin-top: auto; }
.xh-primary.is-warn { background: var(--gold-ink) !important; border-color: var(--gold-ink) !important; }
html.dark a.xh-primary.is-warn { color: #fff; }
.xh-closed { display: inline-flex; align-items: center; justify-content: center; width: 100%;
  margin-top: auto; padding: .5rem .8rem; border-radius: var(--r-sm);
  background: var(--bg-soft); color: var(--text-muted); font-size: .84rem; font-weight: 600; }

/* การกระทำรอง: ลิงก์เรียงกัน ไม่ใช่ปุ่มน้ำหนักเท่าปุ่มหลัก */
.xh-more { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px;
  padding-top: 9px; border-top: 1px solid var(--border-color); }
.xh-more a, .xh-more button {
  display: inline-flex; align-items: center; gap: 4px;
  background: none; border: 0; padding: 2px 0; cursor: pointer;
  font-size: .78rem; font-weight: 600; color: var(--sk-ink); text-decoration: none; }
.xh-more a:hover, .xh-more button:hover { color: var(--sk-dk); text-decoration: underline; }
.xh-more i { font-size: .8rem; }
.xh-more .xh-pub { color: var(--green-ink); }
/* ── "ห้องที่ติ๊กไว้ยังไม่มีชุดข้อสอบ" (11 ก.ย. 2569) ─────────────────────────
   ป้ายนี้ต้องอ่านออกว่าเป็น **เรื่องผิดปกติ** ไม่ใช่ป้ายหมวดหมู่อีกอัน — บนการ์ด
   เดียวกันมี "สอบปลายภาค" ที่เป็น .rd-t-red พาสเทลอยู่แล้ว ถ้าใช้สีเดียวกัน
   ตาจะกวาดผ่านไปเหมือนป้ายบอกชนิดข้อสอบ · พื้นทึบจึงเป็นตัวแยกสองอย่างนี้ */
.rd-tag.xh-miss { background: var(--warm-red); color: #fff; }
html.dark .rd-tag.xh-miss { background: #C0392B; color: #fff; }

html.dark .xh-more a, html.dark .xh-more button { color: var(--sk-ink); }
.xh-del { margin-left: auto; }
.xh-del .btn { padding: .1rem .45rem; font-size: .78rem; }

/* ── ขอสอบต่อ (migration_v66 · 9 ก.ย. 2569) ────────────────────────────────
   ฝั่งนักเรียน: บรรทัด "รอครูอนุมัติ" ต้องอ่านออกว่าเป็น **สถานะ ไม่ใช่ปุ่ม**
   ไม่งั้นเด็กจะกดซ้ำไปเรื่อย ๆ เพราะคิดว่ายังไม่ได้ส่ง                        */
.xh-resume-wait {
  display: flex; align-items: center; gap: 6px;
  font-size: .78rem; font-weight: 700; color: var(--gold-ink);
}
/* ฝั่งครู: หนึ่งแถว = หนึ่งคำขอ ตัดสินจบในแถวเดียว ไม่ต้องเปิดหน้าใหม่
   เด็กที่ขอสอบต่อกำลังนั่งรออยู่จริง ทุกคลิกที่เพิ่มมาคือเวลาสอบที่เสียไป */
.xh-resume-panel { border-color: var(--gold); }
.xh-resume-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 11px 0; border-bottom: 1px solid var(--border-color);
}
.xh-resume-row:last-child { border-bottom: 0; padding-bottom: 0; }
.xh-resume-who { flex: 1 1 240px; min-width: 0; }
.xh-resume-act { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.xh-resume-act .form-control { max-width: 240px; }
@media (max-width: 575px) {
  .xh-resume-act { width: 100%; }
  .xh-resume-act .form-control { max-width: none; flex: 1 1 100%; }
}

@media (max-width: 820px) {
  .xh-card { grid-template-columns: 1fr; }
  .xh-side { border-left: 0; border-top: 1px solid var(--border-color); }
  .xh-primary { margin-top: 4px; }
}
@media (prefers-reduced-motion: reduce) {
  .xh-card, .xh-prog-bar > span { transition: none; }
}

/* ── หัวตารางสองแถว: sticky ต้องซ้อนเป็นชั้น ไม่ใช่ทับกัน ─────────────────
   `style.css:238` ตั้ง `thead th { position:sticky; top:0 }` ไว้ตอนที่หัวตาราง
   มีแถวเดียว · พอเพิ่มแถวหมวดเข้าไป **ทั้งสองแถวเกาะที่ 0 เท่ากัน** แถวล่างจึง
   ทับแถวบนหายไปทันทีที่เริ่มเลื่อน · แถวหมวดอยู่ที่ 0 แถวรายการอยู่ใต้มันพอดี */
/* ความสูงแถวหมวดต้องตายตัว เพราะแถวล่างต้องรู้ว่าจะเกาะที่ระยะเท่าไหร่ —
   ถ้าปล่อยให้สูงตามเนื้อหา ค่า top ที่เดาไว้จะคลาดแล้วสองแถวทับกันอีก
   (วัดจริงรอบแรก: แถวหมวดสูง 49px แต่ตั้ง top ไว้ 33px → เหลื่อม 16px) */
.gradebook-table thead tr.gb-catrow th { top: 0; z-index: 4; }
.gradebook-table thead tr:not(.gb-catrow) th { top: var(--gb-cath, 46px); z-index: 3; }
.gradebook-table thead tr.gb-catrow th.col-sticky,
.gradebook-table thead tr:not(.gb-catrow) th:first-child { z-index: 6; }
/* ชื่อรายการต้องพันบรรทัดได้ — `thead th { white-space: nowrap }` ของเดิมทำให้
   ชื่อยาวดันคอลัมน์กว้างจนตารางเลื่อนแนวนอนไกลเกินจำเป็น */
.gradebook-table thead .gb-ith { white-space: normal !important; }
/* theme.css:262 บังคับ uppercase + .7rem + letter-spacing กับ thead th ทุกใบ
   ซึ่งเหมาะกับหัวคอลัมน์คำเดียว แต่บีบชื่อรายการสองบรรทัดจนอ่านยาก */
.gradebook-table thead .gb-ith,
.gradebook-table thead .gb-cat { text-transform: none; letter-spacing: normal; }
.gradebook-table thead .gb-ith .gb-ith-t { font-family: var(--font-body); }

/* ══════════════════════════════════════════════════════════════════════════
   ตารางสมุดคะแนน — จัดเส้นและตัวหนังสือใหม่ (19 ส.ค. 2569)
══════════════════════════════════════════════════════════════════════════ */

/* ── เส้นตาราง: ให้เส้นบอก "โครงสร้าง" ไม่ใช่ขังทุกช่อง ────────────────────
   `style.css:212` ตีเส้นขวาให้ **ทุกช่องทุกคอลัมน์** ผลคือตารางกลายเป็นกรง
   54 แถว × 9 คอลัมน์ = เส้นตั้ง ~480 เส้น ซึ่งไม่ได้แยกอะไรเลยเพราะมันมีทุกที่
   ตารางข้อมูลที่อ่านง่ายใช้ **เส้นนอนเป็นหลัก** ส่วนเส้นตั้งใช้เฉพาะตรงที่
   "เปลี่ยนความหมาย" — ที่นี่คือ ขอบหมวด (ก่อนกลางภาค→กลางภาค) และขอบบล็อกสรุป
   ภายในหมวดเดียวกันไม่ต้องมีเส้น เพราะการจัดกึ่งกลางแยกคอลัมน์ให้อยู่แล้ว   */
.gradebook-table thead th:not(:last-child),
.gradebook-table tbody td:not(:last-child) { border-right: 0; }
.gradebook-table thead th.is-catstart,
.gradebook-table tbody td.is-catstart {
  border-left: 1px solid color-mix(in srgb, var(--border-color) 85%, transparent);
}
/* เส้นนอนบางลงและสีอ่อนลง — 54 แถวที่คั่นด้วยเส้นเข้มอ่านเป็นลายขวาง */
.gradebook-table tbody td {
  border-bottom: 1px solid color-mix(in srgb, var(--border-color) 46%, transparent);
}
.gradebook-table tbody tr:last-child td { border-bottom: 0; }

/* ── จังหวะแถว ─────────────────────────────────────────────────────────── */
.gradebook-table tbody td { padding: 5px 6px; vertical-align: middle; }
.gradebook-table tbody td.col-sticky { padding: 7px 14px; }
.gradebook-table tbody tr:hover td { background: color-mix(in srgb, var(--sk) 6%, var(--bg-card)); }
.gradebook-table tbody tr:hover td.col-sticky {
  background: color-mix(in srgb, var(--sk) 9%, var(--bg-card));
  box-shadow: inset 3px 0 0 var(--sk-dk), 1px 0 0 var(--border-color);
}

/* ── ชื่อนักเรียน: ชื่อนำ รหัส/เลขที่เป็นบรรทัดรอง ──────────────────────── */
.gradebook-table tbody td.col-sticky > div:first-child {
  font-size: .88rem; font-weight: 600; line-height: 1.35; color: var(--text-main);
}
.gradebook-table tbody td.col-sticky > div:last-child {
  font-size: .72rem; color: var(--text-muted); font-variant-numeric: tabular-nums;
}

/* ── หัวตาราง ─────────────────────────────────────────────────────────────
   ลำดับที่ควรอ่าน: **ชื่อรายการ → คะแนนเต็ม → ความคืบหน้า**
   ของเดิมคะแนนเต็มตัวใหญ่กว่าและสีเข้มกว่าชื่อรายการ ซึ่งกลับหัวกับที่ครูมองหา
   (ครูหาคอลัมน์จาก "ชื่อ" ไม่ใช่จาก "เต็มกี่คะแนน")                          */
.gradebook-table thead .gb-ith { text-align: center; padding: 8px 8px 9px !important; }
.gb-ith-t { display: block; font-size: .8rem; font-weight: 700; line-height: 1.3;
  color: var(--text-main); overflow: hidden; letter-spacing: -.005em;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.gb-ith-b { display: flex; align-items: center; justify-content: center; gap: 6px; margin-top: 4px; }
.gb-ith-m { font-size: .7rem; font-weight: 600; color: var(--text-muted);
  font-variant-numeric: tabular-nums; }
.gb-ith-f { display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.5rem; font-size: .66rem; font-weight: 700; padding: 1px 6px;
  border-radius: 99px; font-variant-numeric: tabular-nums; line-height: 1.45; }
/* ครบแล้ว = ติ๊กเงียบ ๆ · ยังไม่ครบ = ตัวเลขที่เห็นชัด (ของที่ต้องทำต่อคือของที่ควรเด่น) */
.gb-ith-f.is-full { background: transparent; color: var(--green-ink); padding: 1px 3px; }
.gb-ith-f.is-part { background: color-mix(in srgb, var(--gold) 22%, var(--bg-card)); color: var(--gold-ink); }
.gb-ith-f.is-none { background: var(--bg-soft); color: var(--text-muted); }

/* แถวหมวด: ตัวหนังสือเล็กลง เว้นวรรคขึ้น อ่านเป็น "ป้ายกำกับกลุ่ม" ไม่ใช่หัวข้อ */
.gradebook-table thead .gb-cat { font-size: .74rem; font-weight: 700; padding: 8px 10px 7px; }
.gradebook-table thead .gb-cat-max { font-size: .68rem; font-weight: 600; opacity: .78; margin-top: 1px; }

/* หัวคอลัมน์สรุป: ตัวเล็ก ตัวหนา สีจาง — เป็นค่าที่คำนวณมา ไม่ใช่ช่องกรอก */
.gb-sum-th { font-size: .72rem !important; letter-spacing: normal !important;
  text-transform: none !important; }

/* ── บล็อกสรุป: อ่านเป็นคนละระนาบกับช่องกรอก ───────────────────────────── */
.gradebook-table td.gb-sum-td { background: color-mix(in srgb, var(--sk) 4.5%, var(--bg-card));
  font-variant-numeric: tabular-nums; }
.gradebook-table tbody tr:hover td.gb-sum-td { background: color-mix(in srgb, var(--sk) 9%, var(--bg-card)); }

/* ── ช่องคะแนน ─────────────────────────────────────────────────────────── */
.gradebook-table .score-input { width: 62px; height: 32px; font-size: .92rem; font-weight: 600;
  font-variant-numeric: tabular-nums; }
/* ช่องว่าง: จุดไข่ปลาจาง ๆ แทนลายทาง — ลายทางที่ 54 แถวอ่านเป็นพื้นผิวรบกวน
   ส่วนช่องที่มีคะแนนยังเป็นกล่องขอบชัดเหมือนเดิม ตาจึงจับ "ที่กรอกแล้ว" ได้ทันที */
.gradebook-table .score-input:placeholder-shown {
  border-color: transparent; background: transparent;
  color: transparent;                 /* ซ่อน "-" ที่ไม่ได้บอกอะไร */
  box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--text-muted) 26%, transparent);
  border-radius: 0;
}
.gradebook-table .score-input:placeholder-shown:hover,
.gradebook-table .score-input:placeholder-shown:focus {
  background: var(--bg-input); border-color: var(--sk-dk); border-radius: 8px;
  color: var(--text-main); box-shadow: none;
}
/* พื้นคอลัมน์ตามหมวด: จางลงอีก เพราะตอนนี้เส้นแบ่งกลุ่มทำหน้าที่แยกบล็อกแล้ว */
.gradebook-table td.gb-c-pre  { background: color-mix(in srgb, var(--f-sky-dk,#0369a1)    2.5%, transparent); }
.gradebook-table td.gb-c-mid  { background: color-mix(in srgb, var(--f-violet-dk,#6d28d9) 2.5%, transparent); }
.gradebook-table td.gb-c-post { background: color-mix(in srgb, var(--f-teal-dk,#0f766e)   2.5%, transparent); }
.gradebook-table td.gb-c-fin  { background: color-mix(in srgb, var(--f-orange-dk,#c2410c) 2.5%, transparent); }

html.dark .gradebook-table .score-input:placeholder-shown {
  box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--text-muted) 34%, transparent);
}
@media (max-width: 575px) {
  .gradebook-table tbody td.col-sticky { padding: 6px 10px; }
  .gb-ith-t { font-size: .74rem; }
}

/* ══════════════════════════════════════════════════════════════════════════
   ความคืบหน้าการกรอกคะแนน `.sg-prog-*` — แจกแจงถึงรายรายการ (19 ส.ค. 2569)
   ของเดิมบอกแค่ยอดรวมต่อหมวด ("2 รายการ · 6/108 ช่อง") ซึ่งตอบไม่ได้ว่า
   รายการไหนยังขาด และเหลือกี่คน · สามชั้น: ทั้งวิชา → หมวด → รายรายการ
══════════════════════════════════════════════════════════════════════════ */
.sg-prog-all { padding: 2px 0 12px; border-bottom: 1px solid var(--border-color); margin-bottom: 10px; }
.sg-prog-all-t { display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  font-size: .84rem; font-weight: 600; color: var(--text-muted); }
.sg-prog-all-t b { font-size: 1.5rem; font-weight: 700; color: var(--text-main);
  font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.sg-prog-all .rd-segbar { margin: 6px 0 5px; }
.sg-prog-all-s { font-size: .76rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.sg-prog-all-s b { color: var(--gold-ink); }

.sg-prog-cats { display: flex; flex-direction: column; gap: 4px; }
.sg-prog-cat > summary {
  display: grid; grid-template-columns: 14px minmax(0,1fr) auto auto; align-items: center;
  gap: 6px 8px; cursor: pointer; list-style: none; padding: 7px 8px;
  border-radius: var(--r-sm); transition: background .16s cubic-bezier(.16,1,.3,1);
}
.sg-prog-cat > summary::-webkit-details-marker { display: none; }
.sg-prog-cat > summary:hover { background: var(--bg-soft); }
.sg-prog-chev { font-size: .72rem; color: var(--text-muted);
  transition: transform .2s cubic-bezier(.16,1,.3,1); }
.sg-prog-cat[open] .sg-prog-chev { transform: rotate(90deg); }
.sg-prog-cat-n { font-size: .84rem; font-weight: 600; color: var(--text-main);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sg-prog-cat-v { font-size: .84rem; font-weight: 700; color: var(--text-main);
  font-variant-numeric: tabular-nums; }
.sg-prog-cat > summary .rd-segbar { grid-column: 2 / -1; margin-top: 1px; }
.sg-prog-cat.is-done .sg-prog-cat-n, .sg-prog-cat.is-done .sg-prog-cat-v { color: var(--text-muted); }

.sg-prog-badge { font-size: .68rem; font-weight: 700; padding: 1px 8px; border-radius: var(--r-pill);
  white-space: nowrap; }
.sg-prog-badge.is-ok   { background: color-mix(in srgb, var(--green) 15%, var(--bg-card)); color: var(--green-ink); }
.sg-prog-badge.is-part { background: color-mix(in srgb, var(--gold) 20%, var(--bg-card));  color: var(--gold-ink); }
.sg-prog-badge.is-none { background: var(--bg-soft); color: var(--text-muted); }

/* ── รายรายการ ─────────────────────────────────────────────────────────── */
.sg-prog-items { list-style: none; margin: 2px 0 8px; padding: 0 0 0 22px;
  border-left: 1px solid var(--border-color); }
.sg-prog-item { display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 2px 10px;
  padding: 6px 8px; border-radius: var(--r-xs); }
.sg-prog-item:hover { background: var(--bg-soft); }
.sg-prog-item > a { display: flex; align-items: baseline; gap: 7px; min-width: 0;
  text-decoration: none; color: var(--text-main); }
.sg-prog-item > a:hover .sg-pi-name { text-decoration: underline; }
.sg-pi-name { font-size: .82rem; font-weight: 600; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.sg-pi-max { font-size: .7rem; color: var(--text-muted); flex-shrink: 0;
  font-variant-numeric: tabular-nums; }
.sg-pi-left { font-size: .76rem; color: var(--text-muted); text-align: right;
  white-space: nowrap; font-variant-numeric: tabular-nums; }
.sg-pi-left small { font-size: .68rem; opacity: .8; }
.sg-pi-n { font-weight: 700; color: var(--gold-ink); font-size: .86rem; }
.sg-prog-item.is-ok .sg-pi-left { color: var(--green-ink); }
.sg-prog-item.is-ok .sg-pi-left i { margin-right: 3px; }
.sg-prog-item.is-ok .sg-pi-name { color: var(--text-muted); font-weight: 500; }
/* แถบของรายการ: บางกว่าแถบหมวด เพื่อให้ลำดับชั้นอ่านออกจากความหนา */
.sg-pi-bar { grid-column: 1 / -1; height: 3px; border-radius: 99px;
  background: var(--bg-soft); overflow: hidden; }
.sg-pi-bar > i { display: block; height: 100%; border-radius: 99px; background: var(--sk-dk);
  transition: width .4s cubic-bezier(.16,1,.3,1); }
.sg-prog-item.is-ok .sg-pi-bar > i { background: var(--green-ink); }
html.dark .sg-prog-item > a { color: var(--text-main); }

@media (max-width: 575px) {
  .sg-prog-items { padding-left: 12px; }
  .sg-prog-item { grid-template-columns: 1fr; }
  .sg-pi-left { text-align: left; }
}
@media (prefers-reduced-motion: reduce) {
  .sg-prog-chev, .sg-pi-bar > i, .sg-prog-cat > summary { transition: none; }
}

/* ── ไทล์เมนูในหน้ารายวิชา: บังคับ 5 ใบต่อแถว (เจ้าของสั่ง 19 ส.ค. 2569) ────
   เดิมเป็น `auto-fill minmax(126px,1fr)` ซึ่งจำนวนใบต่อแถวเปลี่ยนไปตามความกว้าง
   คอลัมน์ซ้าย — ครูเปิดคนละจอเห็นคนละผัง ตำแหน่งของไทล์จึงจำไม่ได้
   ตรึงเป็น 5 คอลัมน์ตายตัวบนจอกว้าง แล้วค่อยลดขั้นเมื่อที่ไม่พอ
   **`minmax(0,1fr)` ไม่ใช่ `1fr`** — `1fr` เป็น min-content ชื่อไทล์ยาว ๆ
   ("ตั้งค่ารายวิชา") จะดันคอลัมน์จนล้นออกนอกกล่อง                            */
.rd-tiles { grid-template-columns: repeat(5, minmax(0, 1fr)); }
@media (max-width: 1400px) { .rd-tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 1100px) { .rd-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 575px)  { .rd-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; } }
@media (max-width: 380px)  { .rd-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* 5 ใบต่อแถวทำให้ใบแคบลง — ไอคอนกับตัวหนังสือต้องย่อตาม ไม่งั้นชื่อพันสามบรรทัด */
@media (min-width: 1101px) {
  .rd-tile { padding: 15px 8px; gap: 7px; }
  .rd-tile-ic { width: 46px; height: 46px; font-size: 1.2rem; }
  .rd-tile-lb { font-size: .84rem; line-height: 1.3; }
}

/* ══════════════════════════════════════════════════════════════════════════
   สามจุดที่เจ้าของสั่งแก้ 19 ส.ค. 2569
══════════════════════════════════════════════════════════════════════════ */

/* ── 1. หัว "ชื่อ-สกุล" จัดกึ่งกลาง ─────────────────────────────────────── */
.gradebook-table thead th.col-sticky {
  text-align: center; vertical-align: middle;
}

/* ── 2. แถวหมวดต้องมีเส้นตารางด้วย ──────────────────────────────────────
   รอบก่อนถอดเส้นตั้งออกหมดแล้วใส่คืนเฉพาะ "ขอบหมวด" — แต่ใส่ไว้ที่แถวรายการ
   กับตัวตารางเท่านั้น แถวหมวดด้านบนจึงลอยเป็นแถบยาวไม่มีเส้นคั่น อ่านไม่ออกว่า
   หมวดไหนครอบคอลัมน์ไหน · เส้นต้องลากตลอดทั้งสามชั้น (หมวด → รายการ → ข้อมูล)
   ไม่งั้นมันไม่ใช่ "เส้นแบ่งกลุ่ม" แต่เป็นเส้นที่โผล่มากลางทาง                */
.gradebook-table thead .gb-cat + .gb-cat,
.gradebook-table thead .gb-cat.gb-cat-sum {
  border-left: 1px solid color-mix(in srgb, var(--border-color) 85%, transparent);
}
/* เส้นแบ่งระหว่างคอลัมน์ชื่อกับบล็อกคะแนน — ลากครบทั้งสามชั้นเช่นกัน */
.gradebook-table thead .gb-cat:first-of-type { border-left: 1px solid color-mix(in srgb, var(--border-color) 85%, transparent); }
.gradebook-table thead th.col-sticky { border-right: 1px solid color-mix(in srgb, var(--border-color) 85%, transparent); }
/* เส้นบาง ๆ คั่นรายการภายในหมวดเดียวกัน — ของเดิมไม่มีเลย ตัวเลขสองคอลัมน์
   ที่อยู่ติดกันจึงอ่านเป็นก้อนเดียว · ใช้สีอ่อนกว่าเส้นแบ่งหมวดชัดเจน       */
.gradebook-table thead .gb-ith:not(.is-catstart),
.gradebook-table tbody td.gb-c-pre:not(.is-catstart),
.gradebook-table tbody td.gb-c-mid:not(.is-catstart),
.gradebook-table tbody td.gb-c-post:not(.is-catstart),
.gradebook-table tbody td.gb-c-fin:not(.is-catstart) {
  border-left: 1px solid color-mix(in srgb, var(--border-color) 34%, transparent);
}
.gradebook-table thead .gb-sum-th + .gb-sum-th,
.gradebook-table tbody td.gb-sum-td + td.gb-sum-td {
  border-left: 1px solid color-mix(in srgb, var(--border-color) 34%, transparent);
}

/* ── 3. ปุ่มหน้าสุ่มรายชื่อ ใช้ระบบปุ่มเดียวกับทั้งเว็บ ─────────────────────
   ตอนนี้ปุ่มพวกนี้มีคลาส `.btn` แล้ว main.js จึงสร้าง `.btn-53` (สไลด์เผยตัวอักษร)
   ให้เหมือนปุ่มอื่นทั้งเว็บ · สีมาจากโทเคน `--face`/`--ink` ของ `.btn-53`

   **กับดักที่ต้องรู้:** JS ของหน้านี้เขียนทับ `innerHTML` ทุกครั้งที่สลับสถานะ
   (เปลี่ยนเป็น "ปิดเสียง"/"ย่อจอ") ซึ่งลบชั้น `.original`/`.letters` ทิ้ง
   จึงต้องเรียก `window.sgRebuildBtn53(el)` หลังเขียนทุกครั้ง ไม่งั้นเอฟเฟกต์
   หายตั้งแต่กดครั้งแรกโดยไม่มีอะไรฟ้อง                                        */
.rp-pill-btn { min-height: 40px; }
.rp-pill-btn.active { --face: var(--sk-dk); --ink: #fff; }
.rp-room-chip { --face: var(--bg-card); --ink: var(--text-main); }
.rp-room-chip.active { --face: var(--btn-face, var(--sk-dk)); --ink: var(--btn-on, #fff); }

/* ── ชิปเลือกห้อง: รูปทรงต้องอยู่ที่นี่ ไม่ใช่ที่ <style> ในหน้า (21 ส.ค. 2569)
   ชิปพวกนี้เป็น `.btn` ด้วย และ theme.css โหลด **ทีหลัง** style.css เสมอ
   กฎ border-radius ที่เขียนไว้ในหน้าจึงแพ้ `.btn` ทุกครั้ง — ที่เจ้าของเห็นว่า
   "ปุ่มเลือกห้องเพี้ยน" คือแคปซูลมุมมนครึ่งวงกลม (50px) ที่ข้างในมีข้อความ
   สองบรรทัด ส่วนโค้งเลยกินที่ของตัวหนังสือทั้งหัวและท้าย ชื่อวิชาจึงดูโดนเฉือน
   มุม 12px รับสองบรรทัดได้พอดีและเข้าชุดกับการ์ดที่เหลือทั้งเว็บ            */
/* ต้องเขียนเป็น `.rp-room-chip.btn-53` ให้ specificity เท่ากับ `.btn.btn-53`
   (0,2,0) ที่ตั้ง `border-radius: var(--r-pill) !important` ไว้ — ถ้าเขียนแค่
   `.rp-room-chip` (0,1,0) จะแพ้แม้ใส่ !important เพราะ !important ทั้งคู่แล้ว
   ตัวตัดสินกลับไปเป็น specificity · กฎนี้อยู่ท้ายไฟล์กว่า จึงชนะเมื่อเสมอกัน */
.rp-room-chip.btn-53 {
  border-radius: var(--r-md) !important;
  box-shadow: none !important;
  align-items: center;
}
.rp-room-chip.btn-53:hover { box-shadow: none !important; transform: none; }
/* ความสูงเท่ากันทุกใบ — ชิป "กรอกรายชื่อ" มีบรรทัดเดียว ส่วนชิปห้องมีสองบรรทัด
   ถ้าปล่อยให้สูงตามเนื้อหา แถบจะดูฟันหลอ ทั้งที่เป็นตัวเลือกระดับเดียวกัน */
.rp-room-scroll { align-items: stretch; }
.rp-room-scroll > .rp-room-chip { min-height: 46px; }
.rp-mode-chip { flex: 1; min-width: 120px; box-shadow: none !important; }
.rp-mode-chip.active { --face: var(--sk-dk); --ink: #fff; }
/* .btn-53 ตั้ง overflow:hidden — ชิปห้องสองบรรทัดต้องสูงพอไม่ให้บรรทัดล่างโดนตัด */
.rp-room-chip .rp-room-txt { line-height: 1.15; }
.rp-room-chip > .letters { height: auto; }

@media (prefers-reduced-motion: reduce) {
  .rp-pill-btn, .rp-room-chip, .rp-mode-chip { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   คัดกรองกลุ่มเสี่ยง — แถวเดียวต่อคน + กรอง/ค้นหา/เรียง (19 ส.ค. 2569)
   เดิมเป็นการ์ด 53 ใบ ใบละ ~200px = กองเดียวยาว ~10,000px ที่ทุกใบเหมือนกันเป๊ะ
══════════════════════════════════════════════════════════════════════════ */
.rk-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px;
  justify-content: space-between; margin-bottom: 14px; }
.rk-filters { display: flex; flex-wrap: wrap; gap: 7px; }
.rk-chip { display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  padding: .4rem .95rem; border-radius: var(--r-pill); font-size: .84rem; font-weight: 700;
  border: 2px solid var(--border-color); background: var(--bg-card); color: var(--text-muted);
  transition: background .18s cubic-bezier(.16,1,.3,1), color .18s, border-color .18s; }
.rk-chip span { font-variant-numeric: tabular-nums; opacity: .85; }
.rk-chip:hover { border-color: var(--sk); color: var(--sk-dk); }
/* ชิปที่เลือกใช้สีของ "ประเภทความเสี่ยง" นั้น ไม่ใช่สีแบรนด์ — สีตรงนี้เข้ารหัส
   ข้อมูล เหมือนกับชิปในแถว ถ้าใช้สีเดียวกันหมดจะเสียความหมายที่แถวสร้างไว้ */
.rk-chip.is-on { background: var(--sk-dk); border-color: var(--sk-dk); color: #fff; }
.rk-chip.rk-f-ms.is-on   { background: var(--red-ink);   border-color: var(--red-ink); }
.rk-chip.rk-f-fail.is-on { background: var(--gold-ink);  border-color: var(--gold-ink); }
.rk-chip.rk-f-miss.is-on { background: var(--sk-deep);   border-color: var(--sk-deep); }

.rk-tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.rk-search { position: relative; display: inline-flex; align-items: center; }
.rk-search i { position: absolute; left: 12px; color: var(--text-muted); font-size: .86rem; pointer-events: none; }
.rk-search input { padding: .42rem .9rem .42rem 32px; border-radius: var(--r-pill);
  border: 1px solid var(--border-color); background: var(--bg-input); color: var(--text-main);
  font-size: .84rem; min-width: 230px; }
.rk-search input:focus { outline: 0; border-color: var(--sk-dk);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sk) 22%, transparent); }
.rk-sort { display: inline-flex; align-items: center; gap: 7px; font-size: .8rem; color: var(--text-muted); }
.rk-sort select { padding: .38rem .7rem; border-radius: var(--r-sm);
  border: 1px solid var(--border-color); background: var(--bg-input); color: var(--text-main);
  font-size: .82rem; font-weight: 600; }

/* ── แถวต่อคน ───────────────────────────────────────────────────────────── */
.rk-list { display: flex; flex-direction: column;
  background: var(--bg-card); border: 1px solid var(--border-color);
  border-radius: var(--r-lg); overflow: hidden; }
.rk-row { border-bottom: 1px solid color-mix(in srgb, var(--border-color) 60%, transparent); }
.rk-row:last-child { border-bottom: 0; }
.rk-row[hidden] { display: none; }
.rk-row > summary {
  display: grid; align-items: center; gap: 12px; cursor: pointer; list-style: none;
  grid-template-columns: 14px 34px minmax(0,1fr) auto repeat(3, 64px) 30px;
  padding: 9px 16px; transition: background .15s cubic-bezier(.16,1,.3,1);
}
.rk-row > summary::-webkit-details-marker { display: none; }
.rk-row > summary:hover { background: color-mix(in srgb, var(--sk) 5%, var(--bg-card)); }
.rk-row[open] > summary { background: color-mix(in srgb, var(--sk) 7%, var(--bg-card)); }
.rk-row-chev { font-size: .74rem; color: var(--text-muted);
  transition: transform .2s cubic-bezier(.16,1,.3,1); }
.rk-row[open] .rk-row-chev { transform: rotate(90deg); }
.rk-row-id { min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.rk-row-id b { font-size: .88rem; font-weight: 600; color: var(--text-main);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rk-row-id small { font-size: .72rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }

.rk-row-kinds { display: flex; gap: 5px; flex-wrap: nowrap; }
.rk-k { display: inline-flex; align-items: center; gap: 3px; white-space: nowrap;
  font-size: .7rem; font-weight: 700; padding: 2px 9px; border-radius: var(--r-pill); }
.rk-k i { font-size: .72rem; }
.rk-k-ms   { background: color-mix(in srgb, var(--red) 15%, var(--bg-card));  color: var(--red-ink); }
.rk-k-fail { background: color-mix(in srgb, var(--gold) 20%, var(--bg-card)); color: var(--gold-ink); }
.rk-k-miss { background: color-mix(in srgb, var(--sk) 15%, var(--bg-card));   color: var(--sk-ink); }

/* ตัวเลขสามช่อง จัดกึ่งกลางและใช้ tabular-nums เพื่อให้สแกนลงคอลัมน์ได้ */
.rk-row-num { display: flex; flex-direction: column; align-items: center; line-height: 1.15; }
.rk-row-num b { font-size: .95rem; font-weight: 700; color: var(--text-main);
  font-variant-numeric: tabular-nums; }
.rk-row-num b.is-bad  { color: var(--red-ink); }
.rk-row-num b.is-warn { color: var(--gold-ink); }
.rk-row-num small { font-size: .66rem; color: var(--text-muted); }
.rk-row-go { display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: var(--r-sm); color: var(--sk-ink);
  text-decoration: none; transition: background .15s; }
.rk-row-go:hover { background: var(--bg-soft); color: var(--sk-dk); }
html.dark a.rk-row-go { color: var(--sk-ink); }

.rk-row-body { padding: 4px 16px 14px 60px;
  background: color-mix(in srgb, var(--sk) 3%, var(--bg-card)); }

.rk-nomatch { display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 34px 16px; color: var(--text-muted); font-size: .9rem; }
.rk-nomatch[hidden] { display: none; }

@media (max-width: 900px) {
  /* จอแคบ: ตัวเลข "วิชา" ตัดออกก่อน เพราะเป็นข้อมูลที่รู้ได้จากการกางแถว */
  .rk-row > summary { grid-template-columns: 14px 34px minmax(0,1fr) auto repeat(2, 58px) 30px; }
  .rk-row-num:nth-of-type(3) { display: none; }
}
@media (max-width: 640px) {
  .rk-row > summary { grid-template-columns: 14px 34px minmax(0,1fr) 30px;
    grid-template-areas: none; row-gap: 6px; }
  .rk-row-kinds { grid-column: 3 / -1; }
  .rk-row-num { flex-direction: row; gap: 4px; align-items: baseline; grid-column: 3 / -1; }
  .rk-row-num:nth-of-type(3) { display: flex; }
  .rk-row-body { padding-left: 16px; }
  .rk-tools { width: 100%; }
  .rk-search { flex: 1 1 auto; }
  .rk-search input { min-width: 0; width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .rk-chip, .rk-row > summary, .rk-row-chev, .rk-row-go { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Breadcrumb — ให้มันอ่านเป็น "เส้นทาง" ไม่ใช่คำสามคำวางเรียงกัน
   (`.rd-crumb` · `.xe-crumb` — เจ้าของสั่งให้สวยขึ้น 19 ส.ค. 2569)
   เดิมทุกชิ้นสีเทาน้ำหนักเท่ากันหมด รวมทั้ง "หน้าปัจจุบัน" ซึ่งควรเป็นตัวที่หนักสุด
   และไม่มีขอบเขตทางสายตา จึงลอยชนหัวเรื่องด้านล่างจนอ่านเป็นบรรทัดเดียวกัน
══════════════════════════════════════════════════════════════════════════ */
/* ══════════════════════════════════════════════════════════════════════════
   แถบเส้นทาง `.rd-crumb` (เจ้าของสั่งให้เด่นขึ้น 20 ส.ค. 2569)
   ══════════════════════════════════════════════════════════════════════════
   ของเดิมเป็น pill จาง ๆ สูง 26px ตัวหนังสือ .78rem ซึ่งอ่านว่า "ของประดับ"
   มากกว่า "ที่กดได้"
   **ปุ่มกลับไม่ได้อยู่ในแถบนี้** — เป็นปุ่มแยก `.sg-back` เหมือนทุกหน้าในระบบ
   แถบนี้บอก "อยู่ตรงไหน" อย่างเดียว ซึ่งคนละเรื่องกับ "ย้อนขึ้นไปหนึ่งชั้น"     */
.rd-crumb, .xe-crumb {
  display: flex; align-items: center; gap: 3px; flex-wrap: wrap;
  padding: 6px 14px; margin-bottom: 16px;
  background: var(--bg-card);
  border: 1px solid color-mix(in srgb, var(--sk) 26%, var(--border-color));
  border-radius: var(--r-pill);
  font-size: .84rem; line-height: 1.6;
  box-shadow: 0 1px 2px rgba(3,105,161,.05), 0 2px 10px rgba(3,105,161,.05);
}
.rd-crumb a, .xe-crumb a {
  color: var(--text-muted); text-decoration: none; font-weight: 600;
  padding: 3px 9px; border-radius: var(--r-pill);
  transition: background .15s cubic-bezier(.16,1,.3,1), color .15s;
}
.rd-crumb a:hover, .xe-crumb a:hover {
  background: var(--sk-wash); color: var(--sk-dk); text-decoration: none;
}
/* หน้าปัจจุบันคือปลายทาง — ต้องหนักกว่าลิงก์ระหว่างทาง ไม่ใช่เท่ากัน */
.rd-crumb > span:last-child, .xe-crumb > span:last-child,
.rd-crumb .rd-crumb-now, .xe-crumb .rd-crumb-now {
  color: var(--text-main); font-weight: 800; padding: 3px 9px;
  max-width: 34ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rd-crumb > .bi, .xe-crumb > .bi { font-size: .64rem !important; opacity: .4; margin: 0 -2px; }

html.dark .rd-crumb, html.dark .xe-crumb {
  background: color-mix(in srgb, var(--sk) 12%, var(--bg-card));
  box-shadow: 0 1px 2px rgba(0,0,0,.3);
}
html.dark .rd-crumb a:hover, html.dark .xe-crumb a:hover { color: var(--sk-ink); background: rgba(255,255,255,.08); }
@media (prefers-reduced-motion: reduce) {
  .rd-crumb a, .xe-crumb a { transition: none; }
}
@media (max-width: 575px) {
  /* บนมือถือเหลือ "ปุ่มกลับ + หน้าปัจจุบัน" — เส้นทางเต็มกินสองบรรทัด
     โดยไม่ได้บอกอะไรที่ปุ่มกลับไม่ได้บอกอยู่แล้ว */
  .rd-crumb, .xe-crumb { font-size: .8rem; padding: 5px 12px; }
  .rd-crumb > a, .xe-crumb > a,
  .rd-crumb > .rd-crumb-sep, .xe-crumb > .bi { display: none; }
  .rd-crumb > span:last-child, .xe-crumb > span:last-child { max-width: 22ch; }
}

/* ══════════════════════════════════════════════════════════════════════════
   แผง "เพิ่มคำถาม" ในหน้าแก้ไขข้อสอบ `.xe-*` (ออกแบบใหม่ 19 ส.ค. 2569)
   Operate: ครูนั่งพิมพ์ข้อสอบ 30 ข้อรวดเดียว — ทุก interaction คูณ 30
══════════════════════════════════════════════════════════════════════════ */

/* ── หัวข้อขั้นตอน — ให้ฟอร์มยาว ๆ ในคอลัมน์แคบมีจังหวะ ─────────────────── */
.xe-step { display: flex; align-items: center; gap: 8px; margin: 18px 0 8px; }
.xe-step:first-of-type { margin-top: 2px; }
.xe-step-n { display: grid; place-items: center; width: 20px; height: 20px; flex-shrink: 0;
  border-radius: var(--r-full); background: var(--sk-dk); color: #fff;
  font-size: .7rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.xe-step-l { font-size: .88rem; font-weight: 700; color: var(--text-main); }
.xe-body { font-size: .95rem; line-height: 1.55; }

/* ── ตัวเลือกชนิดคำถาม: การ์ดพร้อมคำอธิบาย จัดกลุ่มตามวิธีตอบ ───────────── */
.xe-types { display: flex; flex-direction: column; gap: 10px; }
.xe-tgroup-l { display: block; font-size: .7rem; font-weight: 700; letter-spacing: .02em;
  color: var(--text-muted); margin-bottom: 5px; }
.xe-tgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: 6px; }
.xe-tcard {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 9px 6px 8px; cursor: pointer; text-align: center;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-card); color: var(--text-main);
  transition: border-color .16s cubic-bezier(.16,1,.3,1), background .16s;
}
.xe-tcard i { font-size: 1.05rem; color: var(--sk-dk); }
.xe-tcard b { font-size: .78rem; font-weight: 700; line-height: 1.25; }
/* คำอธิบายว่าชนิดนี้ให้นักเรียนทำอะไร — ของเดิมมีแต่ชื่อ ครูที่ไม่เคยใช้
   "จับคู่" กับ "เรียงลำดับ" เดาไม่ออกว่าต่างกันยังไงจนกว่าจะลองสร้างจริง */
.xe-tcard small { font-size: .64rem; color: var(--text-muted); line-height: 1.3; }
.xe-tcard:hover { border-color: var(--sk); background: var(--bg-soft); }
.btn-check:checked + .xe-tcard {
  border-color: var(--sk-dk); background: color-mix(in srgb, var(--sk) 13%, var(--bg-card));
  box-shadow: inset 0 0 0 1px var(--sk-dk);
}
.btn-check:checked + .xe-tcard b { color: var(--sk-ink); }
.btn-check:focus-visible + .xe-tcard { outline: 3px solid color-mix(in srgb, var(--sk) 45%, transparent); outline-offset: 2px; }

.xe-hint { font-size: .76rem; color: var(--text-muted); margin-bottom: 7px; }
/* ── กล่องอธิบาย "โหมดตัวเลข" ในฟอร์มข้อเติมคำ (9 ก.ย. 2569) ────────────────
   เจ้าของอ่านคำอธิบายเดิมแล้วงงสองรอบ · ปัญหาคือมันเป็น `form-text` ตัวจาง ๆ
   ปนอยู่กับช่องกรอกอื่น ๆ จนอ่านข้ามไป — ยกออกมาเป็นกล่องที่ "ดูเหมือนคำอธิบาย"
   แล้วเขียนเป็นตัวอย่างจริงแทนนิยาม (0.5 = 1/2 = 2/4 ถูกหมด) */
.xe-numbox { border: 1px solid var(--border-color); border-left: 4px solid var(--sk);
  background: var(--sk-wash); border-radius: 10px; padding: 10px 13px; }
html.dark .xe-numbox { background: rgba(14, 165, 233, .1); }
.xe-numbox-h { font-weight: 800; font-size: .84rem; color: var(--sk-ink); margin-bottom: 5px;
  display: flex; align-items: center; gap: 7px; }
html.dark .xe-numbox-h { color: var(--sk-lt); }
.xe-numbox-l { margin: 0; padding-left: 18px; font-size: .8rem; line-height: 1.75; }
.xe-numbox-l code { background: var(--bg-card); border: 1px solid var(--border-color);
  border-radius: 5px; padding: 0 5px; font-size: .95em; }
/* บรรทัดตัวอย่างช่วงที่รับ — ต้องเด่นกว่า form-text เพราะมันคือคำตอบของคำถาม
   "ตกลงใส่เลขนี้แล้วเด็กตอบอะไรได้บ้าง" ที่ครูถามอยู่ตอนกรอก */
.xe-numbox-eg { margin: 9px 0; padding: 8px 12px; border-radius: 8px;
  background: var(--green-wash, #eafaf1); color: var(--green-ink, #145c36);
  font-size: .82rem; font-weight: 700; }
html.dark .xe-numbox-eg { background: rgba(22, 163, 74, .16); color: #8ff0b6; }
.xe-numbox-eg:empty { display: none; }

/* ── ตัวเลือกคำตอบ: แถวเดียวขอบเดียว ───────────────────────────────────── */
.xe-choice {
  display: grid; grid-template-columns: 20px 22px minmax(0,1fr); align-items: center; gap: 9px;
  padding: 6px 10px; margin-bottom: 6px; cursor: pointer;
  border: 1px solid var(--border-color); border-radius: var(--r-sm); background: var(--bg-card);
  transition: border-color .16s cubic-bezier(.16,1,.3,1), background .16s;
}
.xe-choice:hover { border-color: var(--sk); }
.xe-choice-k { display: grid; place-items: center; width: 22px; height: 22px;
  border-radius: var(--r-xs); background: var(--bg-soft); color: var(--text-muted);
  font-size: .74rem; font-weight: 700; }
.xe-choice-in { border: 0; background: transparent; color: var(--text-main);
  font-size: .88rem; padding: 3px 0; width: 100%; }
.xe-choice-in:focus { outline: 0; }
/* ข้อที่ถูกได้พื้นเขียวจาง ๆ — เห็นเฉลยจากหางตาโดยไม่ต้องไล่หาวงกลมที่ติ๊กไว้ */
.xe-choice:has(.qc-correct:checked) {
  border-color: var(--green-ink);
  background: color-mix(in srgb, var(--green) 10%, var(--bg-card));
}
.xe-choice:has(.qc-correct:checked) .xe-choice-k {
  background: var(--green-ink); color: #fff;
}

/* ── ถูก/ผิด: สองปุ่มใหญ่ ไม่ใช่ชิปเล็กสองอัน ──────────────────────────── */
.xe-tf { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.xe-tf-b { display: flex; align-items: center; justify-content: center; gap: 6px;
  padding: 11px 8px; cursor: pointer; font-weight: 700; font-size: .9rem;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-card); color: var(--text-muted);
  transition: border-color .16s cubic-bezier(.16,1,.3,1), background .16s, color .16s; }
.xe-tf-b:hover { border-color: var(--sk); color: var(--sk-dk); }
#tfT:checked + .xe-tf-b { border-color: var(--green-ink); color: var(--green-ink);
  background: color-mix(in srgb, var(--green) 12%, var(--bg-card)); }
#tfF:checked + .xe-tf-b { border-color: var(--red-ink); color: var(--red-ink);
  background: color-mix(in srgb, var(--red) 12%, var(--bg-card)); }

/* ── ตั้งค่าเพิ่มเติม: แถวเดียว ไม่ใช่ช่องเต็มความกว้างสองช่อง ───────────── */
.xe-extras { display: flex; flex-wrap: wrap; gap: 10px; }
.xe-extra { display: flex; flex-direction: column; gap: 3px; flex: 0 0 auto; }
.xe-extra.is-wide { flex: 1 1 180px; min-width: 0; }
.xe-extra > span { font-size: .74rem; font-weight: 600; color: var(--text-muted); }
.xe-extra input, .xe-extra select {
  padding: .38rem .65rem; border-radius: var(--r-sm); font-size: .86rem;
  border: 1px solid var(--border-color); background: var(--bg-input); color: var(--text-main);
  font-variant-numeric: tabular-nums; }
.xe-extra input[type=number] { width: 96px; }
.xe-extra input:focus, .xe-extra select:focus { outline: 0; border-color: var(--sk-dk);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sk) 20%, transparent); }

html.dark .xe-tcard { background: rgba(255,255,255,.03); }
html.dark .btn-check:checked + .xe-tcard b { color: var(--sk-ink); }

@media (max-width: 991px) {
  .xe-tgrid { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
}
@media (prefers-reduced-motion: reduce) {
  .xe-tcard, .xe-choice, .xe-tf-b { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   ช่องอัปโหลดแบบลากวาง `.sg-drop` (เจ้าของเลือกแบบนี้ 19 ส.ค. 2569)
   ═══════════════════════════════════════════════════════════════════════
   ต้นแบบที่ส่งมาเป็น `.form`/`.drop-container` สีตายตัว (#fff · #084cdf ·
   rgb(171,202,255)) **ชื่อคลาสกว้างเกินกว่าจะเอามาตรง ๆ** — `.form` จะไปโดน
   ทุกฟอร์มในเว็บ และสีตายตัวจะไม่ตามธีมมืดหรือสีที่แอดมินตั้ง
   จึง prefix `sg-` แล้วเดินด้วย token เดิมทั้งหมด (บทเรียนเดียวกับ `.sg-dlbtn`)

   - `<input type=file>` **ไม่ซ่อนด้วย display:none** — ต้องยังโฟกัสด้วยคีย์บอร์ด
     ได้ จึงวางทับเต็มพื้นที่แบบโปร่งใส แล้วให้ `:focus-visible` วาดขอบที่ตัว label
   - รายการไฟล์ที่เลือกวาดด้วย JS (main.js) เพราะ `<input type=file>` ไม่มีทาง
     แสดงชื่อไฟล์ที่เลือกได้เองเมื่อซ่อนปุ่มของ user-agent ทิ้ง
   - `@media (hover:none)` ตัดเอฟเฟกต์ hover ทิ้ง ไม่งั้นบนจอสัมผัสสถานะ hover
     จะค้างหลังแตะ (บทเรียนเดียวกับ `.sg-dlbtn`) */
.sg-drop {
  position: relative; display: flex; flex-direction: column; gap: 6px;
  align-items: center; justify-content: center; text-align: center;
  padding: 20px 14px; border-radius: var(--r-md);
  border: 2px dashed color-mix(in srgb, var(--sk) 46%, var(--border-color));
  background: color-mix(in srgb, var(--sk) 4%, var(--bg-card));
  color: var(--text-muted); cursor: pointer;
  transition: background .18s ease, border-color .18s ease, transform .18s cubic-bezier(.16,1,.3,1);
}
.sg-drop:hover, .sg-drop.is-over {
  background: color-mix(in srgb, var(--sk) 12%, var(--bg-card));
  border-color: var(--sk-dk);
}
.sg-drop.is-over { transform: scale(1.012); border-style: solid; }
.sg-drop:has(input:focus-visible) {
  outline: 3px solid color-mix(in srgb, var(--sk) 45%, transparent); outline-offset: 2px;
}
.sg-drop input[type=file] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer;
}
.sg-drop-ic {
  display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
  background: color-mix(in srgb, var(--sk) 16%, var(--bg-card));
  color: var(--sk-ink); font-size: 1.2rem;
  transition: transform .2s cubic-bezier(.16,1,.3,1);
}
.sg-drop:hover .sg-drop-ic, .sg-drop.is-over .sg-drop-ic { transform: translateY(-3px); }
.sg-drop-t { font-size: .92rem; font-weight: 700; color: var(--text-main); line-height: 1.35; }
.sg-drop-s { font-size: .75rem; color: var(--text-muted); line-height: 1.4; }
.sg-drop-btn {
  margin-top: 2px; padding: .34rem .9rem; border-radius: var(--r-pill);
  background: var(--btn-face); color: var(--btn-on);
  font-size: .78rem; font-weight: 700;
}

/* รายการไฟล์ที่เลือกไว้ — วาดโดย main.js */
.sg-drop-files { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.sg-drop-file {
  display: flex; align-items: center; gap: 8px; padding: .42rem .6rem;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-card); font-size: .8rem; color: var(--text-main);
}
.sg-drop-file > i:first-child { color: var(--sk-ink); font-size: .95rem; flex-shrink: 0; }
.sg-drop-file .n { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sg-drop-file .sz { flex-shrink: 0; font-size: .72rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.sg-drop-file .rm {
  flex-shrink: 0; border: 0; background: transparent; color: var(--red-ink);
  padding: 2px 4px; border-radius: var(--r-xs); cursor: pointer; line-height: 1;
}
.sg-drop-file .rm:hover { background: color-mix(in srgb, var(--red) 16%, transparent); }
/* เกินเพดานของเซิร์ฟเวอร์ — บอกตั้งแต่ก่อนกดส่ง ไม่ใช่ให้รออัปจนจบแล้วค่อยพัง */
.sg-drop-file.is-big { border-color: var(--red-ink); background: color-mix(in srgb, var(--red) 10%, var(--bg-card)); }
.sg-drop-file.is-big .sz { color: var(--red-ink); font-weight: 700; }

html.dark .sg-drop { background: rgba(255,255,255,.03); }
html.dark .sg-drop:hover, html.dark .sg-drop.is-over { background: rgba(255,255,255,.06); }
@media (hover: none) {
  .sg-drop:hover { background: color-mix(in srgb, var(--sk) 4%, var(--bg-card)); border-color: color-mix(in srgb, var(--sk) 46%, var(--border-color)); }
  .sg-drop:hover .sg-drop-ic { transform: none; }
}
@media (prefers-reduced-motion: reduce) { .sg-drop, .sg-drop-ic { transition: none; } }

/* ═══════════════════════════════════════════════════════════════════════
   หน้าออกข้อสอบ — แถวคำตอบที่เพิ่ม/ลบได้ · เกณฑ์คะแนน · นำเข้าจาก Word
   ═══════════════════════════════════════════════════════════════════════ */
.xe-rows { display: flex; flex-direction: column; gap: 7px; }

/* ปุ่ม "เพิ่มตัวเลือก/เพิ่มคู่/เพิ่มรายการ" — เส้นประเพื่อให้อ่านว่า "ยังไม่มีของ
   ตรงนี้ กดแล้วจะมี" ต่างจากปุ่มทึบที่อ่านว่าเป็นการกระทำกับของที่มีอยู่แล้ว */
.xe-addrow {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%; margin-top: 8px; padding: .48rem; cursor: pointer;
  border: 1.5px dashed color-mix(in srgb, var(--sk) 50%, var(--border-color));
  border-radius: var(--r-sm); background: transparent;
  color: var(--sk-ink); font-size: .82rem; font-weight: 700;
  transition: background .16s, border-color .16s;
}
.xe-addrow:hover { background: color-mix(in srgb, var(--sk) 10%, transparent); border-color: var(--sk-dk); }

.xe-rowx, .xe-rowmv {
  flex-shrink: 0; display: grid; place-items: center; width: 26px; height: 26px;
  border: 0; border-radius: var(--r-xs); background: transparent;
  color: var(--text-muted); cursor: pointer; font-size: .78rem; line-height: 1;
  transition: background .14s, color .14s;
}
.xe-rowx:hover { background: color-mix(in srgb, var(--red) 16%, transparent); color: var(--red-ink); }
.xe-rowmv:hover { background: var(--bg-soft); color: var(--sk-ink); }
.xe-rowx:disabled { opacity: .3; cursor: default; }
.xe-rowx:disabled:hover { background: transparent; color: var(--text-muted); }

/* จับคู่: สองช่องในแถวเดียว ขอบเดียว — ของเดิมเป็น .input-group สามกล่องต่อแถว
   ซึ่งในคอลัมน์แคบทำให้ทั้งสองฝั่งเหลือกว้างฝั่งละ ~70px พิมพ์ข้อความไทยไม่ได้ */
.xe-pair, .xe-ordrow {
  display: flex; align-items: center; gap: 7px; padding: .3rem .45rem;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-card);
  transition: border-color .16s;
}
.xe-pair:focus-within, .xe-ordrow:focus-within { border-color: var(--sk-dk); }
.xe-pair-n {
  flex-shrink: 0; display: grid; place-items: center; width: 22px; height: 22px;
  border-radius: 50%; background: var(--bg-soft); color: var(--text-muted);
  font-size: .7rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.xe-pair-in {
  flex: 1 1 0; min-width: 0; border: 0; background: transparent;
  color: var(--text-main); font: inherit; font-size: .86rem; padding: .3rem .1rem;
}
/* แถวเดียวกันนี้ใช้ได้ทั้ง input และ textarea — ตัวชี้วัดยาวเกินบรรทัดเดียวเป็นปกติ
   จึงต้องยืดได้ (main.js `textarea[data-grow]`) แต่ห้ามมีที่จับลากมุมขวาล่าง
   ของ textarea โผล่มาทับปุ่มลบแถว */
textarea.xe-pair-in { resize: none; overflow: hidden; line-height: 1.5; }
.xe-pair:has(textarea), .xe-ordrow:has(textarea) { align-items: flex-start; }
.xe-ordrow:has(textarea) .xe-pair-n,
.xe-ordrow:has(textarea) .xe-rowx { margin-top: 2px; }
.xe-pair-in:focus { outline: 0; }
.xe-pair-ar { flex-shrink: 0; color: var(--text-muted); font-size: .78rem; }

/* กล่องตัวเลือกปรนัยต้องมีที่ให้ปุ่มลบ — .xe-choice เดิมเป็น flex อยู่แล้ว */
.xe-choice { position: relative; }
.xe-choice .xe-rowx { margin-left: auto; }

/* ── เกณฑ์การให้คะแนน ─────────────────────────────────────────────────── */
.xe-gm { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
.xe-gm-c {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: .6rem .5rem; text-align: center; cursor: pointer;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-card); color: var(--text-main);
  transition: border-color .16s, background .16s;
}
.xe-gm-c i { font-size: 1.05rem; color: var(--sk-dk); }
.xe-gm-c b { font-size: .78rem; font-weight: 700; line-height: 1.25; }
.xe-gm-c small { font-size: .64rem; color: var(--text-muted); line-height: 1.3; }
.xe-gm-c:hover { border-color: var(--sk); background: var(--bg-soft); }
.btn-check:checked + .xe-gm-c {
  border-color: var(--sk-dk); background: color-mix(in srgb, var(--sk) 12%, var(--bg-card));
}
.btn-check:checked + .xe-gm-c b { color: var(--sk-ink); }
.btn-check:focus-visible + .xe-gm-c {
  outline: 3px solid color-mix(in srgb, var(--sk) 45%, transparent); outline-offset: 2px;
}
html.dark .xe-gm-c { background: rgba(255,255,255,.03); }

/* ── สื่อประกอบเป็นวิดีโอ (ทั้งหน้าออกข้อสอบและห้องสอบ) ─────────────────── */
.xm-video {
  position: relative; width: 100%; max-width: 720px; margin: 0 auto;
  aspect-ratio: 16 / 9; border-radius: var(--r-md); overflow: hidden;
  background: #000; border: 1px solid var(--border-color);
}
.xm-video > video, .xm-video > iframe { width: 100%; height: 100%; border: 0; display: block; }
.xm-media-wait {
  display: flex; align-items: center; gap: 8px; padding: .7rem .9rem;
  border: 1px dashed var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-soft); color: var(--text-muted); font-size: .82rem;
}

/* ── นำเข้าหลายข้อจาก Word ────────────────────────────────────────────── */
/* `.xe-bulk-guide` (กล่องตัวอย่างรูปแบบที่กางอยู่หน้าแรก) ถูกถอดออก 9 ก.ย. 2569
   ตัวอย่างย้ายเข้าไปอยู่ในกล่องช่วยเหลือที่พับไว้แทน — ดู `.xe-imp-help` */
.xe-bulk-ta { font-size: .88rem; line-height: 1.6; }
.xe-bulk-bar {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px;
  padding: .7rem .85rem; margin-bottom: 10px; border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--sk) 8%, var(--bg-card));
  border: 1px solid color-mix(in srgb, var(--sk) 30%, var(--border-color));
}
.xe-bulk-n { font-size: .9rem; font-weight: 700; color: var(--sk-ink); }
.xe-bulk-n b { font-family: var(--font-display); font-size: 1.15rem; }
.xe-bulk-list { display: flex; flex-direction: column; gap: 8px; max-height: 62vh; overflow-y: auto; padding-right: 4px; }
.xe-bulk-q {
  display: flex; gap: 10px; padding: .7rem .85rem;
  border: 1px solid var(--border-color); border-radius: var(--r-sm); background: var(--bg-card);
}
/* ข้อที่มีตัวเลือกแต่ยังไม่มีเฉลย = สิ่งเดียวในหน้านี้ที่ต้องลงมือทำ */
.xe-bulk-q.is-nokey { border-color: var(--red-ink); background: color-mix(in srgb, var(--red) 7%, var(--bg-card)); }
/* รูปประกอบที่แกะมาจาก Word — จำกัดความสูงไว้ ไม่งั้นรูปกราฟจากมือถือใบเดียว
   ดันรายการตัวอย่างทั้งชุดยาวจนครูต้องเลื่อนหาข้อถัดไป */
.xe-bulk-img { margin: 6px 0 2px; position: relative; display: inline-block; }
/* ปุ่มเอารูปออก — วางทับมุมรูป ไม่ใช่ใต้รูป เพราะรูปในหน้าตัวอย่างเรียงติดกัน
   ปุ่มที่อยู่ใต้รูปจะดูเหมือนเป็นของรูปใบถัดไป */
.xe-bulk-img-x {
  position: absolute; top: 5px; right: 5px;
  width: 26px; height: 26px; padding: 0; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--red); border-radius: 50%;
  background: var(--bg-card); color: var(--red-ink);
  font-size: .74rem; cursor: pointer; opacity: .85;
  transition: opacity .12s ease, background .12s ease, color .12s ease;
}
.xe-bulk-img-x:hover { opacity: 1; background: var(--red); color: #fff; }
.xe-bulk-img img { max-width: 100%; max-height: 190px; border-radius: var(--r-sm);
  border: 1px solid var(--border-color); background: var(--bg-soft); }
/* ข้อที่ติ๊กออกจะจางลง แต่ยังอ่านออก — ซ่อนไปเลยจะทำให้ครูนับข้อไม่ตรง */
.xe-bulk-q:has(.bulk-keep:not(:checked)) { opacity: .42; }
.xe-bulk-keep { flex-shrink: 0; padding-top: 2px; }
.xe-bulk-main { flex: 1 1 auto; min-width: 0; }
.xe-bulk-head { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-bottom: 5px; }
.xe-bulk-no {
  display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%;
  background: var(--sk-wash); color: var(--sk-ink); font-size: .74rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.xe-bulk-ty {
  padding: .18rem .5rem; border-radius: var(--r-xs); font-size: .74rem; font-weight: 600;
  border: 1px solid var(--border-color); background: var(--bg-input); color: var(--text-main);
}
.xe-bulk-body { font-size: .88rem; font-weight: 600; line-height: 1.5; margin-bottom: 6px; }
.xe-bulk-ch { display: flex; flex-direction: column; gap: 3px; }
.xe-bulk-c { display: flex; align-items: flex-start; gap: 7px; font-size: .82rem; cursor: pointer; padding: .12rem 0; }
.xe-bulk-c .k {
  flex-shrink: 0; display: grid; place-items: center; width: 19px; height: 19px; border-radius: var(--r-xs);
  background: var(--bg-soft); color: var(--text-muted); font-size: .68rem; font-weight: 700;
}
.xe-bulk-c:has(input:checked) .k { background: color-mix(in srgb, var(--green) 22%, transparent); color: var(--green-ink); }
.xe-bulk-c:has(input:checked) .b { font-weight: 700; color: var(--green-ink); }
/* ปุ่มย้ายรูปไปข้ออื่นในหน้าตัวอย่างนำเข้า (10 ก.ย. 2569)
   อยู่มุมซ้ายล่างของกล่องรูป — คนละมุมกับปุ่มกากบาท (มุมขวาบน) เพื่อไม่ให้กดพลาด
   เป็น "ย้าย" ตอนตั้งใจ "ลบ" ซึ่งเป็นคนละผลลัพธ์กันคนละทาง */
.xe-bulk-img { position: relative; }
.xe-bulk-img-mv {
  position: absolute; left: 4px; bottom: 4px; display: inline-flex; gap: 2px;
  opacity: .55; transition: opacity .12s;
}
.xe-bulk-img:hover .xe-bulk-img-mv { opacity: 1; }
.xe-bulk-img-mv button {
  width: 22px; height: 22px; display: grid; place-items: center; padding: 0;
  border: 1px solid var(--border-color); border-radius: var(--r-xs);
  background: var(--bg-card); color: var(--text-main); font-size: .7rem; cursor: pointer;
}
.xe-bulk-img-mv button:hover { background: var(--brand); border-color: var(--brand); color: #fff; }

/* รูปของตัวเลือกในหน้าตัวอย่างนำเข้า (10 ก.ย. 2569) — ข้อสอบดนตรี/ศิลปะที่
   ตัวเลือกเป็นรูปล้วน · เล็กพอให้กวาดตาดูทั้งชุดได้ในจอเดียว แต่ใหญ่พอให้เห็นว่า
   รูปไปอยู่ถูกตัวเลือกไหม ซึ่งเป็นสิ่งเดียวที่ครูต้องตรวจตรงนี้ */
.xe-bulk-cimg {
  position: relative; flex: none; display: inline-flex;
  border: 1px solid var(--border-color); border-radius: var(--r-xs);
  background: var(--bg-card); padding: 2px;
}
.xe-bulk-cimg img { display: block; max-height: 54px; max-width: 150px; object-fit: contain; }
.xe-bulk-cimg-x {
  position: absolute; top: -7px; right: -7px; width: 19px; height: 19px;
  display: grid; place-items: center; padding: 0; font-size: .6rem;
  border: 1px solid var(--border-color); border-radius: 50%;
  background: var(--bg-card); color: var(--red-ink); cursor: pointer; line-height: 1;
}
.xe-bulk-cimg-x:hover { background: var(--red); border-color: var(--red); color: #fff; }
.xe-bulk-key { font-size: .8rem; color: var(--text-muted); display: flex; align-items: center; gap: 4px; }

/* ── ปุ่มแก้รายการในหน้าตัวอย่าง (10 ก.ย. 2569) ──────────────────────────────
   หน้านี้มีได้ 60 ข้อ · ปุ่มลบข้อ/ลบตัวเลือกจึงต้อง **เงียบจนกว่าจะชี้** ไม่งั้น
   ทั้งหน้ากลายเป็นแถวปุ่มสีแดง แล้วสิ่งที่ครูมาทำจริง (กวาดตาหาข้อที่แยกเพี้ยน)
   จะอ่านยากกว่าเดิม — บทเรียนเดียวกับ .xe-bulk-in ข้างบน                     */
/* ── ปุ่มลบข้อ: **ต้องเห็นตลอดเวลา** (เจ้าของแจ้ง 10 ก.ย. 2569 "ปุ่มลบมองไม่เห็น") ──
   รอบแรกทำเป็นไอคอนจาง ๆ (`opacity:.35`) ที่เข้มขึ้นตอนชี้ ด้วยเหตุผลว่าหน้านี้มีได้
   60 ข้อ ปุ่มแดงเรียงกันจะรก · **แต่บนแถวที่ยังไม่ได้เฉลย (`.is-nokey`) พื้นเป็นสีแดงจาง
   อยู่แล้ว** ไอคอนสีเทาจาง ๆ บนพื้นแดงจึงกลืนหายไปเลย — และแถวที่ครูอยากลบมากที่สุด
   ก็คือแถวพวกนั้นพอดี · เปลี่ยนเป็น **ปุ่มมีขอบ + มีคำว่า "ลบ"** เห็นชัดตลอดเวลา
   ไม่ต้องเดาว่ากดตรงไหน และไม่ต้องเอาเมาส์ไปชี้ก่อนถึงจะรู้ว่ามีปุ่ม              */
.xe-bulk-del {
  margin-left: auto; flex: none;
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px; border-radius: var(--r-xs);
  border: 1px solid color-mix(in srgb, var(--red) 45%, transparent);
  background: var(--bg-card); color: var(--red-ink);
  font-size: .74rem; font-weight: 600; line-height: 1.5;
  cursor: pointer; transition: background .12s, border-color .12s, color .12s;
}
.xe-bulk-del:hover { background: var(--red); border-color: var(--red); color: #fff; }
.xe-bulk-cx {
  flex: none; display: grid; place-items: center;
  width: 22px; height: 22px; margin-left: 2px; padding: 0;
  border: 1px solid transparent; border-radius: var(--r-xs);
  background: transparent; color: var(--text-muted); opacity: .55; font-size: .74rem;
  cursor: pointer; transition: opacity .12s, background .12s, color .12s, border-color .12s;
}
.xe-bulk-c:hover .xe-bulk-cx { opacity: 1; border-color: var(--border-color); }
.xe-bulk-cx:hover {
  opacity: 1; color: var(--red-ink);
  border-color: color-mix(in srgb, var(--red) 45%, transparent);
  background: color-mix(in srgb, var(--red) 12%, transparent);
}
.xe-bulk-cadd {
  align-self: flex-start; margin-top: 3px; padding: .16rem .55rem;
  border: 1px dashed var(--border-color); border-radius: var(--r-xs);
  background: transparent; color: var(--text-muted); font-size: .74rem; font-weight: 600;
  cursor: pointer;
}
.xe-bulk-cadd:hover { border-color: var(--brand); color: var(--brand); border-style: solid; }
/* ข้อที่เลือกชนิดปรนัยไว้แต่ยังไม่มีตัวเลือก — ต้องบอกตรงนั้น ไม่ใช่ให้ไปรู้ตอน
   นำเข้าเสร็จแล้วว่าระบบเปลี่ยนเป็น "เติมคำ" ให้เงียบ ๆ */
.xe-bulk-warn {
  margin-bottom: 5px; padding: 5px 9px; border-radius: var(--r-xs);
  background: color-mix(in srgb, var(--gold) 16%, transparent);
  color: var(--gold-ink); font-size: .76rem; font-weight: 600;
}

/* ── ช่องแก้ไขในหน้าตัวอย่างก่อนนำเข้า (2 ก.ย. 2569) ────────────────────────
   ต้อง **ดูเหมือนข้อความ ไม่ใช่ฟอร์ม** จนกว่าจะแตะ — หน้านี้มีได้ 30-60 ข้อ
   ถ้าวาดกรอบช่องกรอกครบทุกช่องตั้งแต่แรก ทั้งหน้าจะกลายเป็นตารางกรอกข้อมูล
   แล้วสิ่งที่ครูมาทำจริง (กวาดตาหาข้อที่แยกเพี้ยน) จะอ่านยากกว่าเดิม        */
.xe-bulk-in {
  width: 100%; font: inherit; color: inherit;
  background: transparent; border: 1px solid transparent; border-radius: var(--r-xs);
  padding: 2px 6px; margin: -2px 0;
}
.xe-bulk-in:hover { border-color: var(--border-color); background: var(--bg-card); }
.xe-bulk-in:focus {
  outline: none; border-color: var(--sk); background: var(--bg-card);
  box-shadow: 0 0 0 3px var(--sk-wash);
}
textarea.xe-bulk-body { resize: vertical; min-height: 2.6em; display: block; }
.xe-bulk-c .b { flex: 1 1 auto; min-width: 0; }
.xe-bulk-key .xe-bulk-in { flex: 1 1 auto; min-width: 0; }

@media (max-width: 575px) {
  .xe-gm { grid-template-columns: 1fr; }
  .xe-pair { flex-wrap: wrap; }
  .xe-pair-ar { display: none; }
  .xe-pair-in { flex: 1 1 100%; }
  .xe-bulk-list { max-height: none; }
}
@media (prefers-reduced-motion: reduce) {
  .xe-addrow, .xe-rowx, .xe-rowmv, .xe-pair, .xe-ordrow, .xe-gm-c { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   หน้าตรวจงาน `.ag-*` (ออกแบบใหม่ 20 ส.ค. 2569 — ของเดิมสำรองไว้ที่
   storage/ui_backup_pre_grade_redesign/ พร้อม restore.php)
   ═══════════════════════════════════════════════════════════════════════
   หลักการเดียวกับ REDESIGN 2569: หน้าเปิดด้วย "เหลืออะไรต้องทำ" ไม่ใช่
   "มีอะไรบ้าง" · ของเดิมขึ้นต้นด้วยป้าย "ส่งแล้ว 12/40" ซึ่งไม่ตอบคำถามที่
   ครูเปิดหน้านี้มาถามจริง ๆ คือ "ยังต้องตรวจอีกกี่คน"                     */
.ag-head {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 14px 18px; margin-bottom: 12px;
  background: var(--bg-card); border: 1px solid var(--border-color);
  border-radius: var(--r-lg); box-shadow: var(--shadow-sm, 0 1px 2px rgba(3,105,161,.05));
}
.ag-head-t { flex: 1 1 260px; min-width: 0; }
.ag-head-t h5 {
  margin: 0 0 3px; font-size: 1.06rem; font-weight: 800; line-height: 1.35;
  display: flex; align-items: center; gap: 8px; color: var(--text-main);
}
.ag-head-t h5 .bi { color: var(--sk-ink); }
.ag-head-s { font-size: .8rem; color: var(--text-muted); }

.ag-prog { flex: 0 0 190px; }
.ag-prog-n { font-family: var(--font-display); font-size: 1.05rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.ag-prog-n b { font-size: 1.7rem; font-weight: 800; color: var(--sk-ink); }
.ag-prog-l { font-size: .72rem; font-weight: 700; color: var(--text-muted); margin-bottom: 6px; }
.ag-prog-bar { height: 7px; border-radius: 50px; background: var(--bg-soft); overflow: hidden; }
.ag-prog-bar > i {
  display: block; height: 100%; border-radius: 50px;
  background: linear-gradient(90deg, var(--sk-deep), var(--sk));
  transition: width .35s cubic-bezier(.2,1,.3,1);
}

.ag-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 12px; }
.ag-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.ag-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-left: auto; }

/* ชิปกรอง — สีของชิปที่เลือกคือ "สีของสถานะนั้น" ไม่ใช่สีแบรนด์
   สีตรงนี้เข้ารหัสข้อมูลเหมือนป้ายสถานะในแถว ถ้าใช้สีเดียวกันหมดจะทิ้ง
   ความหมายที่ตารางสร้างไว้ (บทเรียนเดียวกับชิปในหน้ากลุ่มเสี่ยง) */
.ag-chip {
  --c: var(--sk); --ci: var(--sk-ink);
  display: inline-flex; align-items: center; gap: 6px;
  padding: .38rem .8rem; border-radius: 50px; cursor: pointer;
  border: 1px solid var(--border-color); background: var(--bg-card);
  color: var(--text-muted); font-size: .82rem; font-weight: 600;
  transition: border-color .16s, background .16s, color .16s;
}
.ag-chip > span {
  min-width: 20px; padding: 0 5px; border-radius: 50px;
  background: var(--bg-soft); color: var(--text-muted);
  font-size: .72rem; font-weight: 800; font-variant-numeric: tabular-nums;
}
.ag-chip:hover { border-color: var(--c); color: var(--ci); }
.ag-chip.is-on {
  border-color: var(--ci); color: var(--ci);
  background: color-mix(in srgb, var(--c) 13%, var(--bg-card));
}
.ag-chip.is-on > span { background: var(--ci); color: #fff; }
.ag-chip.c-pending { --c: var(--gold); --ci: var(--gold-ink); }
.ag-chip.c-graded  { --c: var(--green); --ci: var(--green-ink); }
.ag-chip.c-missing { --c: var(--red);  --ci: var(--red-ink); }
.ag-chip.c-late    { --c: var(--orange, var(--gold)); --ci: var(--gold-ink); }
.ag-chip.c-req     { --c: var(--violet, var(--sk)); --ci: var(--sk-ink); }

.ag-search {
  display: flex; align-items: center; gap: 7px; padding: .3rem .8rem;
  border: 1px solid var(--border-color); border-radius: 50px; background: var(--bg-card);
}
.ag-search .bi { color: var(--text-muted); font-size: .85rem; }
.ag-search input {
  border: 0; background: transparent; color: var(--text-main);
  font: inherit; font-size: .84rem; width: 150px; padding: .18rem 0;
}
.ag-search input:focus { outline: 0; }
.ag-search:focus-within { border-color: var(--sk-dk); }

.ag-modes { display: flex; gap: 2px; padding: 3px; border-radius: 50px; background: var(--bg-soft); }
.ag-mode {
  display: inline-flex; align-items: center; gap: 6px; padding: .32rem .8rem;
  border: 0; border-radius: 50px; background: transparent; cursor: pointer;
  color: var(--text-muted); font-size: .8rem; font-weight: 700;
  transition: background .16s, color .16s;
}
.ag-mode.is-on { background: var(--bg-card); color: var(--sk-ink); box-shadow: 0 1px 3px rgba(2,20,40,.12); }
@media (max-width: 575px) { .ag-mode span { display: none; } }

/* ── โหมดตรวจทีละคน ────────────────────────────────────────────────────────
   งานที่ส่งได้พื้นที่มากที่สุดในหน้า เพราะมันคือสิ่งที่ครูต้อง "อ่าน" จริง ๆ
   ในโหมดตารางมันถูกบีบอยู่ในช่องกว้าง 320px ร่วมกับอีกเจ็ดคอลัมน์ที่เป็น
   ข้อมูลประกอบทั้งหมด (เลขที่ ชื่อ กลุ่ม สถานะ) */
.ag-focus {
  background: var(--bg-card); border: 1px solid var(--border-color);
  border-radius: var(--r-lg); overflow: hidden; margin-bottom: 14px;
}
.ag-focus[hidden] { display: none; }
.ag-focus-hd {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  background: color-mix(in srgb, var(--sk) 8%, var(--bg-card));
  border-bottom: 1px solid var(--border-color);
}
.ag-focus-who { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 11px; }
.ag-focus-av { flex: none; display: block; }
.ag-focus-av img, .ag-focus-av .avatar-initial { width: 44px !important; height: 44px !important; }
.ag-focus-name { font-size: 1rem; font-weight: 800; color: var(--text-main); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ag-focus-sub { font-size: .76rem; color: var(--text-muted); }
.ag-focus-pos { flex: none; font-size: .8rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.ag-focus-pos b { font-family: var(--font-display); font-size: 1.15rem; color: var(--sk-ink); }
.ag-nav {
  flex: none; width: 40px; height: 40px; display: grid; place-items: center;
  border: 1px solid var(--border-color); border-radius: 50%;
  background: var(--bg-card); color: var(--text-main); cursor: pointer;
  transition: background .16s, border-color .16s;
}
.ag-nav:hover:not(:disabled) { background: var(--sk-wash); border-color: var(--sk-dk); color: var(--sk-ink); }
.ag-nav:disabled { opacity: .35; cursor: default; }

.ag-focus-body { display: grid; grid-template-columns: minmax(0, 1fr) 320px; }
.ag-focus-work { padding: 14px; min-width: 0; border-right: 1px solid var(--border-color); }
/* ช่องคะแนนต้องตามสายตาไปด้วย — งานที่มีหลายไฟล์สูงเกินหน้าจอ ถ้าไม่ sticky
   ครูต้องเลื่อนกลับขึ้นไปกรอกคะแนนทุกคน ซึ่งลบข้อดีทั้งหมดของโหมดนี้ทิ้ง
   (`align-self:start` จำเป็นในกริด ไม่งั้นช่องจะยืดเต็มความสูงแล้ว sticky ไม่มีผล) */
.ag-focus-side {
  padding: 14px; background: var(--bg-soft);
  align-self: start; position: sticky; top: 0;
}
.ag-focus-lbl {
  display: flex; align-items: center; gap: 6px; margin-bottom: 8px;
  font-size: .74rem; font-weight: 800; letter-spacing: .02em; color: var(--text-muted);
}

.ag-slot .ag-score { display: flex; align-items: center; gap: 8px; }
.ag-slot .score-cell { font-size: 1.35rem !important; font-weight: 800; text-align: center;
  height: auto !important; padding: .5rem !important; font-family: var(--font-display); }
.ag-slot .ag-score-max { font-size: 1.05rem; font-weight: 700; color: var(--text-muted); }
.ag-slot .fb-cell { font-size: .9rem !important; padding: .5rem .7rem !important; height: auto !important; }

.ag-quick { display: flex; gap: 6px; margin-top: 8px; }
.ag-q {
  flex: 1 1 0; display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: .45rem .3rem; cursor: pointer;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-card); color: var(--text-main);
  transition: border-color .16s, background .16s, transform .14s cubic-bezier(.2,1.2,.4,1);
}
.ag-q b { font-family: var(--font-display); font-size: 1rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.ag-q span { font-size: .66rem; color: var(--text-muted); }
.ag-q:hover { border-color: var(--sk-dk); background: var(--sk-wash); transform: translateY(-1px); }
.ag-q:first-child b { color: var(--green-ink); }
.ag-q:last-child b { color: var(--red-ink); }

.ag-focus-act { display: flex; gap: 8px; margin-top: 14px; }
.ag-focus-keys { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;
  font-size: .7rem; color: var(--text-muted); }
.ag-focus-keys kbd {
  display: inline-block; min-width: 18px; padding: 1px 5px; margin-right: 2px;
  border: 1px solid var(--border-color); border-bottom-width: 2px; border-radius: 5px;
  background: var(--bg-card); color: var(--text-main); font-size: .68rem; font-family: inherit;
}

.ag-nowork { color: var(--text-muted); font-size: .82rem; }
.ag-work-cell { max-width: 340px; }
.ag-score { display: flex; align-items: center; gap: 5px; }
/* ══ ช่องคะแนนในตารางตรวจงานหดจนมองไม่เห็นตัวเลขบนมือถือ (เจอจากภาพจริง 21 ส.ค.) ═
   `.score-cell` เป็น `.form-control` ของ Bootstrap ซึ่งตั้ง `width:100%` ไว้
   แต่พ่อแม่ `.ag-score` เป็น flex container ที่ **ไม่ได้กำหนดความกว้างของตัวเอง**
   เกิดเป็นวงจรอ้างอิงกัน: input อยากได้ 100% ของกล่อง ส่วนกล่องอยากหดพอดีเนื้อหา
   เบราว์เซอร์แก้ปัญหานี้ด้วยการหด input ลงจนเหลือแค่ min-content ของมัน
   ซึ่งสำหรับ `<input type=number>` คือ**เล็กมาก** (วัดจริงบนจอ 390px ได้ 21px —
   น้อยกว่าตัวเลขหนึ่งหลักรวมลูกศรขึ้นลง) ตัวเลขที่กรอกไว้จริงจึงมองไม่เห็นเลย
   ทั้งที่ยังอยู่ใน DOM ครบ (สลับกับ `.ag-score-max` "/10" ซึ่งเป็น `nowrap` จึง
   ไม่ยอมหดตาม ช่อง input เลยรับผลกระทบทั้งหมดฝ่ายเดียว)

   บนจอกว้าง (1440px) ไม่เจอเพราะ <td> มีที่ว่างเหลือเฟือ วัดได้ 93px — บั๊กนี้
   จึงโผล่เฉพาะจอแคบที่ตารางต้องเบียดพื้นที่กัน **ห้ามลบ min-width/flex-shrink
   นี้ทิ้ง** ไม่งั้นครูจะกรอกคะแนนแล้วมองไม่เห็นว่ากรอกอะไรไปอีก */
.ag-score .score-cell { min-width: 3.6rem; flex: 0 1 3.6rem; }
.ag-score-max { font-size: .76rem; color: var(--text-muted); white-space: nowrap; flex-shrink: 0; }
.ag-list[hidden] { display: none; }
.ag-fold > summary { cursor: pointer; list-style: none; }
.ag-fold > summary::-webkit-details-marker { display: none; }
.ag-fold[open] > summary > .xe-fold-ic { transform: rotate(180deg); }

html.dark .ag-focus-side { background: rgba(255,255,255,.03); }
html.dark .ag-mode.is-on { background: rgba(255,255,255,.09); }

@media (max-width: 991px) {
  .ag-focus-body { grid-template-columns: minmax(0, 1fr); }
  .ag-focus-work { border-right: 0; border-bottom: 1px solid var(--border-color); }
}
@media (max-width: 575px) {
  .ag-tools { margin-left: 0; width: 100%; }
  .ag-search { flex: 1 1 auto; }
  .ag-search input { width: 100%; }
  .ag-prog { flex: 1 1 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .ag-chip, .ag-mode, .ag-nav, .ag-q, .ag-prog-bar > i { transition: none; }
}

/* ข้อความที่นักเรียนพิมพ์ส่ง — หนีบด้วย CSS ไม่ใช่ตัดตั้งแต่ฝั่งเซิร์ฟเวอร์
   ตัดที่เซิร์ฟเวอร์ = ข้อความส่วนที่เหลือไม่มีอยู่ในหน้าเลย ครูอ่านงานเขียน
   ของนักเรียนไม่ได้ไม่ว่าจะเปิดที่ไหน · หนีบไว้ให้อ่านผ่าน ๆ ในตาราง แล้ว
   คลายหนีบในที่ที่มีพื้นที่ให้อ่านจริง */
.sub-text {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;
  overflow: hidden;
}

/* ── งานแบบเต็มในโหมดตรวจทีละคน ────────────────────────────────────────────
   นี่คือเหตุผลทั้งหมดของโหมดนี้ — ถ้ายังเป็นรูปย่อ 46px เหมือนในตาราง
   ก็ไม่มีอะไรต่างจากโหมดตาราง นอกจากเสียพื้นที่ไปเปล่า ๆ */
.ag-big { display: flex; flex-direction: column; gap: 12px; }
.ag-big:empty { display: none; }
.ag-big-item { border: 1px solid var(--border-color); border-radius: var(--r-md); overflow: hidden; background: var(--bg-card); }
.ag-big-name {
  display: flex; align-items: center; gap: 6px; padding: .45rem .7rem;
  background: var(--bg-soft); border-bottom: 1px solid var(--border-color);
  font-size: .78rem; font-weight: 700; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ag-big-box { min-height: 120px; }
/* ตัวเรนเดอร์ที่ยืมมาจากโมดัลตั้งความสูงเป็น 70vh ซึ่งเหมาะกับกล่องลอยกลางจอ
   แต่ในหน้านี้มันอยู่ในคอลัมน์ที่มีของอื่นอยู่ด้วย จึงหนีบลงมาให้เห็นช่องคะแนน
   พร้อมกันได้โดยไม่ต้องเลื่อน */
.ag-big-box > iframe, .ag-big-box > video { max-height: 58vh !important; height: 58vh !important; display: block; }
.ag-big-box img { max-height: 52vh !important; }
.ag-big-box pre { max-height: 52vh !important; }
.ag-big-box > div[style*="min-height"] { min-height: 0 !important; padding: 1rem !important; }

/* งานที่เป็นข้อความ: ในตารางหนีบไว้ ในโหมดตรวจทีละคนอ่านได้เต็ม */
.ag-work-big .sub-text {
  display: block; -webkit-line-clamp: unset; overflow: visible;
  font-size: .95rem !important; line-height: 1.65; color: var(--text-main) !important;
  padding: .8rem 1rem; border: 1px solid var(--border-color); border-radius: var(--r-md);
  background: var(--bg-card); margin-bottom: 10px;
}
/* แถบปุ่มเล็ก ๆ ใต้ตัวอย่างเต็ม — ยังต้องมีเพราะปุ่มดาวน์โหลดอยู่ที่นั่น */
.ag-work-big .ag-work { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-start; }
.ag-work-big { min-height: 0; }
/* ไฟล์ที่ถูกวาดใหญ่อยู่ด้านบนแล้ว — ซ่อนตัวเปิดพรีวิวซ้ำในแถบล่าง เก็บปุ่มโหลดไว้
   **ต้อง !important** เพราะตัวเปิดพรีวิวของรูปมีคลาส `d-inline-block` ของ Bootstrap
   ซึ่งเป็น utility ที่ประกาศ `display:inline-block !important` — กฎธรรมดาแพ้เสมอ
   ไม่ว่า specificity จะสูงแค่ไหน (แพ้แบบเงียบ ๆ ไม่มีอะไรฟ้อง) */
.ag-work-big .sub-file[data-big-shown] > .btn-preview { display: none !important; }

/* ═══════════════════════════════════════════════════════════════════════
   ชุดสถานการณ์ (PISA) `.xs-*` — ออกแบบใหม่ 20 ส.ค. 2569
   ═══════════════════════════════════════════════════════════════════════
   เดิมขั้นตอนสามอย่างอยู่คนละที่ (สร้างชุด · สร้างคำถาม · ผูกเข้าชุด)
   ตอนนี้ทุกอย่างที่ทำกับชุดหนึ่งอยู่ในการ์ดของชุดนั้น */
.xs-intro {
  display: flex; gap: 14px; align-items: flex-start; margin-bottom: 14px;
  padding: 14px 16px; border-radius: var(--r-md);
  background: color-mix(in srgb, var(--sk) 8%, var(--bg-card));
  border: 1px solid color-mix(in srgb, var(--sk) 26%, var(--border-color));
}
.xs-intro-ic {
  flex: none; display: grid; place-items: center; width: 44px; height: 44px;
  border-radius: var(--r-sm); background: var(--sk-wash); color: var(--sk-ink); font-size: 1.3rem;
}
.xs-intro b { display: block; font-size: .94rem; color: var(--text-main); margin-bottom: 3px; }
.xs-intro p { margin: 0; font-size: .84rem; line-height: 1.6; color: var(--text-muted); }

.xs-cards { display: flex; flex-direction: column; gap: 10px; }
.xs-card {
  padding: 12px 14px; border-radius: var(--r-md);
  border: 1px solid var(--border-color); background: var(--bg-card);
}
.xs-card-hd { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 7px; }
.xs-card-t {
  display: flex; align-items: center; gap: 7px; min-width: 0;
  font-size: .96rem; font-weight: 800; color: var(--text-main);
}
.xs-card-t .bi { color: var(--sk-ink); flex: none; }
.xs-card-body {
  font-size: .84rem; line-height: 1.6; color: var(--text-muted);
  padding: .55rem .75rem; border-radius: var(--r-sm); background: var(--bg-soft);
  margin-bottom: 8px; white-space: pre-line;
}
.xs-card-act { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; }

/* ── ติ๊กเลือกข้อที่ผูกกับชุดสถานการณ์ (9 ก.ย. 2569) ────────────────────────
   แทนที่ช่อง "ผูกข้อที่ _ ถึง _" ซึ่งบังคับให้ครูรู้เลขข้อเองและบังคับให้ข้อที่
   ผูกต้องติดกันเป็นช่วง · **โจทย์แสดงเต็มไม่ตัดท้าย** เพราะข้อของสถานการณ์
   เดียวกันมักขึ้นต้นเหมือนกันทั้งกลุ่ม ("จากข้อมูลข้างต้น…") ตัดท้ายแล้วทุกข้อ
   จะอ่านเหมือนกันหมด · รายการเลื่อนในกล่องของตัวเอง ปุ่มบันทึกจึงไม่หลุดจอ  */
.xs-pick {
  margin-top: 10px; padding: 12px; border-radius: var(--r-md);
  border: 1px solid var(--sk); background: var(--sk-wash);
}
.xs-pick-hd {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  flex-wrap: wrap; margin-bottom: 8px; font-size: .84rem; font-weight: 700;
  color: var(--sk-ink);
}
.xs-pick-n { font-variant-numeric: tabular-nums; color: var(--text-muted); font-weight: 600; }
.xs-pick-list {
  display: flex; flex-direction: column; gap: 2px;
  max-height: 340px; overflow-y: auto; padding-right: 4px;
  background: var(--bg-card); border-radius: var(--r-sm); padding: 6px;
}
.xs-pick-i {
  display: flex; align-items: flex-start; gap: 9px; padding: .4rem .5rem;
  cursor: pointer; border-radius: var(--r-sm); font-size: .84rem; line-height: 1.5;
  transition: background .14s;
}
.xs-pick-i:hover { background: var(--bg-soft); }
.xs-pick-i:has(input:checked) { background: var(--sk-wash); }
.xs-pick-i .form-check-input { margin-top: .28rem; flex: none; }
.xs-pick-no {
  flex: none; min-width: 26px; text-align: center; font-size: .74rem; font-weight: 800;
  color: var(--text-muted); font-variant-numeric: tabular-nums; margin-top: .18rem;
}
.xs-pick-b { min-width: 0; }
.xs-pick-ft { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; margin-top: 10px; }
html.dark .xs-pick-i:hover { background: rgba(255,255,255,.05); }

/* รูปย่อของสื่อ + ปุ่มลบรายไฟล์ (ใช้ทั้งการ์ดชุดสถานการณ์และการ์ดคำถาม) */
.xs-media { display: flex; flex-wrap: wrap; gap: 8px; }
.xs-media-i {
  position: relative; width: 74px; height: 74px; border-radius: var(--r-sm);
  overflow: hidden; border: 1px solid var(--border-color); background: var(--bg-soft);
}
.xs-media-i img { width: 100%; height: 100%; object-fit: cover; display: block; }
.xs-media-vid {
  display: grid; place-items: center; width: 100%; height: 100%;
  color: var(--sk-ink); font-size: 1.5rem; background: var(--sk-wash);
}
.xs-media-x { position: absolute; top: 3px; right: 3px; }
.xs-media-x button {
  display: grid; place-items: center; width: 22px; height: 22px; padding: 0;
  border: 0; border-radius: 50%; cursor: pointer; font-size: .62rem; line-height: 1;
  background: rgba(10,22,40,.62); color: #fff;
  transition: background .15s;
}
.xs-media-x button:hover { background: var(--red-ink); }

.xs-formwrap {
  padding: 14px; border-radius: var(--r-md);
  border: 1px dashed color-mix(in srgb, var(--sk) 40%, var(--border-color));
  background: color-mix(in srgb, var(--sk) 4%, var(--bg-card));
}
/* โหมดแก้ไขต้องดูต่างจากโหมดสร้างใหม่อย่างชัดเจน — ของเดิมกดแก้ไขแล้วค่ามาโผล่
   ในฟอร์มเฉย ๆ ไม่มีอะไรบอกว่ากำลังทับของเก่าอยู่ */
.xs-formwrap.is-edit {
  border-style: solid; border-color: var(--gold-ink);
  background: color-mix(in srgb, var(--gold) 8%, var(--bg-card));
}
.xs-form-hd {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-bottom: 10px; font-size: .92rem; font-weight: 800; color: var(--text-main);
}
html.dark .xs-card, html.dark .xs-formwrap { background: rgba(255,255,255,.03); }
@media (max-width: 575px) {
  .xs-card-act { gap: 6px; }
  .xs-pick-i { font-size: .82rem; }
}

/* ═══════════════════════════════════════════════════════════════════════
   หน้าแจ้งเตือนผ่าน LINE `.ln-*` (ออกแบบใหม่ 20 ส.ค. 2569)
   ═══════════════════════════════════════════════════════════════════════
   ของเดิมเป็นการ์ดขาวโล่ง ๆ มีเครื่องหมายถูกจาง ๆ กับสวิตช์เล็ก ๆ กลางที่ว่าง
   สถานะจริง (เปิด/ปิดรับ) อ่านไม่ออกจนกว่าจะเพ่งดูว่าสวิตช์อยู่ท่าไหน */
.ln-hero {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 18px 20px; margin-bottom: 14px; border-radius: var(--r-lg);
  border: 1px solid var(--border-color); background: var(--bg-card);
  position: relative; overflow: hidden;
}
/* แถบสีซ้ายบอกสถานะจากหางตา — เขียว = กำลังส่งอยู่จริง */
.ln-hero::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 5px;
  background: var(--text-muted);
}
.ln-hero.is-on { background: color-mix(in srgb, var(--green) 8%, var(--bg-card));
  border-color: color-mix(in srgb, var(--green) 34%, var(--border-color)); }
.ln-hero.is-on::before { background: var(--green-ink); }
.ln-hero.is-off { background: var(--bg-soft); }

.ln-hero-ic {
  flex: none; display: grid; place-items: center; width: 54px; height: 54px;
  border-radius: var(--r-md); font-size: 1.7rem;
  background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-muted);
}
.ln-hero.is-on .ln-hero-ic {
  background: #06C755; border-color: #06C755; color: #fff;   /* เขียว LINE ของจริง */
}
.ln-hero-b { flex: 1 1 240px; min-width: 0; }
.ln-hero-t { font-size: 1.05rem; font-weight: 800; color: var(--text-main); line-height: 1.35; }
.ln-hero-s { font-size: .84rem; color: var(--text-muted); margin-top: 2px; }
.ln-hero-sw { flex: none; }
.ln-hero-sw .form-check-label { font-weight: 700; font-size: .88rem; color: var(--text-main); }

/* ── เลือกเรื่องที่จะรับ ─────────────────────────────────────────────────── */
.ln-group + .ln-group { margin-top: 14px; }
.ln-group-hd {
  display: flex; align-items: center; gap: 7px; margin-bottom: 8px;
  font-size: .82rem; font-weight: 700; color: var(--text-muted);
}
.ln-group-hd > .bi { color: var(--sk-ink); }
.ln-group-all {
  margin-left: auto; border: 0; background: transparent; cursor: pointer;
  font: inherit; font-size: .74rem; font-weight: 600; color: var(--sk-ink);
  padding: .1rem .4rem; border-radius: var(--r-xs);
}
.ln-group-all:hover { background: var(--sk-wash); }

.ln-types { display: grid; grid-template-columns: repeat(auto-fill, minmax(178px, 1fr)); gap: 7px; }
.ln-type {
  display: flex; align-items: center; gap: 9px; padding: .5rem .65rem; cursor: pointer;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-card); transition: border-color .15s, background .15s;
}
.ln-type:hover { border-color: var(--sk); }
/* ติ๊กแล้วเปลี่ยนพื้นตามโทนของชนิดนั้น (--nt มาจาก .t-* เดียวกับกล่องแจ้งเตือน)
   จึงอ่านออกจากหางตาว่าเปิดอะไรไว้ โดยไม่ต้องไล่ดูวงกลมทีละอัน */
.ln-type:has(input:checked) {
  border-color: color-mix(in srgb, var(--nt, var(--sk)) 55%, transparent);
  background: color-mix(in srgb, var(--nt, var(--sk)) 10%, var(--bg-card));
}
.ln-type-ic {
  flex: none; display: grid; place-items: center; width: 26px; height: 26px;
  border-radius: var(--r-xs); font-size: .82rem;
  background: var(--bg-soft); color: var(--text-muted);
}
.ln-type:has(input:checked) .ln-type-ic {
  background: color-mix(in srgb, var(--nt, var(--sk)) 20%, transparent); color: var(--nt, var(--sk-ink));
}
.ln-type-t { font-size: .84rem; font-weight: 600; color: var(--text-main); line-height: 1.3; }
/* ไม่ติ๊ก = จางลงแต่ยังอ่านออก — ซ่อนไปเลยจะทำให้นับไม่ตรงกับตัวเลขบนแถบสถานะ */
.ln-type:has(input:not(:checked)) .ln-type-t { color: var(--text-muted); }

html.dark .ln-hero-ic { background: rgba(255,255,255,.05); }
html.dark .ln-type { background: rgba(255,255,255,.03); }
@media (max-width: 575px) {
  .ln-types { grid-template-columns: 1fr; }
  .ln-hero-sw { width: 100%; }
}
@media (prefers-reduced-motion: reduce) { .ln-type { transition: none; } }

/* ── สีของปุ่มดาวน์โหลด (เจ้าของสั่ง 20 ส.ค. 2569: "สีสามารถเปลี่ยนได้") ──────
   ทุกแบบตั้งครบสามค่าเสมอ: `--dl-face` (พื้น) · `--dl-ink` (หมึก) · `--dl-line`
   (ขอบ+เงา+ไอคอน) · **ห้ามเว้นตัวใดตัวหนึ่ง** ไม่งั้นพื้นมาจากกฎหนึ่งหมึกมาจาก
   อีกกฎหนึ่ง = ปุ่มพื้นเขียวหมึกน้ำเงินที่ไม่มีอะไรฟ้อง (บทเรียนเดียวกับ .rd-stat)
   ค่าที่เลือกเป็นระดับ **ink** ทั้งหมด เพราะขาวต้องทับได้และตัวมันเองต้องอยู่บน
   พื้นขาวได้ด้วย (ระดับ fill อย่าง --green #16a34a ขาวทับได้ 3.3 = ตก AA) */
.sg-dlbtn.is-green { --dl-face: var(--green-ink); --dl-ink: #fff; --dl-line: #0b5c2c;
                     --dl-sub: color-mix(in srgb, #000 14%, var(--green-ink)); }
.sg-dlbtn.is-gold  { --dl-face: var(--gold); --dl-ink: #3a2900; --dl-line: #8a5e08;
                     --dl-sub: color-mix(in srgb, #000 12%, var(--gold)); }
/* แดงตรึงเป็นค่าคงที่ทั้งสองโหมด — `--red-ink` พลิกเป็นสีอ่อนในโหมดมืด
   ตัวหนังสือขาวบนนั้นจะเหลือ 2.4 (เหตุผลเดียวกับ .sg-pop-danger) */
.sg-dlbtn.is-red   { --dl-face: #c22a2f; --dl-ink: #fff; --dl-line: #7f1d20;
                     --dl-sub: #a3232a; }
.sg-dlbtn.is-green .sg-dlbtn__icon svg,
.sg-dlbtn.is-red   .sg-dlbtn__icon svg { fill: #fff; }
.sg-dlbtn.is-gold  .sg-dlbtn__icon svg { fill: #3a2900; }
/* `html.dark a` (0,2,1) ชนะ `.sg-dlbtn.is-*` (0,2,0) เท่ากันแต่มาทีหลัง — ทวงคืน */
html.dark a.sg-dlbtn.is-green, html.dark a.sg-dlbtn.is-green:hover,
html.dark a.sg-dlbtn.is-red,   html.dark a.sg-dlbtn.is-red:hover   { color: #fff; }
html.dark a.sg-dlbtn.is-gold,  html.dark a.sg-dlbtn.is-gold:hover  { color: #3a2900; }

/* ใช้เป็นปุ่มเปิดเมนูเลือกรูปแบบไฟล์ได้ด้วย (ส่งออก Excel/PDF/CSV)
   ลูกศรลงเล็ก ๆ บอกว่ากดแล้วมีตัวเลือก ไม่ใช่โหลดทันที — ถ้าไม่มี ผู้ใช้จะคาดว่า
   ไฟล์จะลงเครื่องเลย แล้วเจอเมนูโผล่มาแทน ซึ่งอ่านว่าปุ่มทำงานผิด */
.sg-dlbtn.is-menu::after {
  content: ""; position: absolute; right: calc(var(--dl-pad) + 6px);
  width: 0; height: 0; border: 4px solid transparent;
  border-top-color: currentColor; opacity: .55; margin-top: 3px;
}
.sg-dlbtn.is-menu { padding-right: calc(var(--dl-pad) + 24px); }

/* ── ขนาดเล็กพิเศษ + แบบไอคอนล้วน ─────────────────────────────────────────
   ปุ่มดาวน์โหลดในรายการไฟล์ที่ส่งงานอยู่ในแถวตาราง/การ์ดที่แน่นมาก
   ปุ่มเต็มรูปแบบสูง 40px จะดันความสูงแถวทั้งตาราง (บทเรียนเดียวกับ `.btn-push`
   ที่ห้ามใส่ในแถวตาราง) จึงมีสองขนาดย่อยไว้ให้ใช้แทน */
.sg-dlbtn.is-xs {
  height: 28px; font-size: .74rem; --dl-pad: 28px;
  padding: 0 calc(var(--dl-pad) + 9px) 0 11px; border-width: 1.5px;
  border-radius: 8px; box-shadow: 2px 2px 0 var(--dl-line);
}
.sg-dlbtn.is-xs .sg-dlbtn__icon svg { width: 13px; height: 13px; }
.sg-dlbtn.is-xs:active { transform: translate(2px, 2px); }

/* ไอคอนล้วน — ไม่มีข้อความ แผงไอคอนจึงกินทั้งปุ่มตั้งแต่แรก
   (`title` เป็นตัวบอกว่าโหลดไฟล์อะไร เพราะไม่มีป้ายให้อ่าน) */
.sg-dlbtn.is-icon {
  width: 34px; height: 34px; padding: 0; --dl-pad: 100%;
  border-radius: 9px; box-shadow: 2px 2px 0 var(--dl-line);
  justify-content: center;
}
.sg-dlbtn.is-icon .sg-dlbtn__icon {
  position: static; width: 100%; height: 100%; border-left: 0; background: transparent;
}
.sg-dlbtn.is-icon:hover .sg-dlbtn__icon { background: var(--dl-sub); }
.sg-dlbtn.is-icon .sg-dlbtn__icon svg { width: 15px; height: 15px; }
.sg-dlbtn.is-icon:active { transform: translate(2px, 2px); }
.sg-dlbtn.is-sm.is-icon { height: 32px; width: 32px; }

/* ชิปกลุ่มการสอนในแคตตาล็อกรายวิชา (admin_courses) — ทางเข้าไปจัดการวิชานั้น */
.ac-sec {
  display: inline-flex; align-items: center; gap: 5px; padding: .3rem .6rem;
  background: var(--sk-wash); color: var(--sk-ink) !important;
  border: 1px solid color-mix(in srgb, var(--sk) 32%, transparent);
  font-size: .76rem; font-weight: 700;
  transition: background .15s, border-color .15s, transform .15s cubic-bezier(.2,1.2,.4,1);
}
.ac-sec:hover { background: color-mix(in srgb, var(--sk) 22%, var(--bg-card));
                border-color: var(--sk-dk); transform: translateY(-1px); }
.ac-sec .bi { font-size: .72rem; opacity: .7; }
.ac-sec-n {
  padding: 0 5px; border-radius: var(--r-pill); font-size: .68rem;
  background: color-mix(in srgb, var(--sk) 26%, transparent); font-variant-numeric: tabular-nums;
}
/* `html.dark a` (0,2,1) ชนะ `.ac-sec` (0,1,0) — ทวงสีคืน (กับดักเดิมของ .btn-brand) */
html.dark a.ac-sec, html.dark a.ac-sec:hover { color: var(--sk-ink) !important; }

/* ── ภาพรวมระบบ (admin_overview) — อ่านง่ายขึ้น ─────────────────────────── */
.ao-chips { display: flex; gap: 5px; }
.ao-chip {
  padding: .22rem .7rem; border-radius: var(--r-pill); cursor: pointer;
  border: 1px solid var(--border-color); background: var(--bg-card);
  color: var(--text-muted); font: inherit; font-size: .78rem; font-weight: 700;
  transition: background .15s, border-color .15s, color .15s;
}
.ao-chip:hover { border-color: var(--sk); color: var(--sk-dk); }
.ao-chip.is-on { background: var(--sk-wash); border-color: var(--sk-dk); color: var(--sk-ink); }
/* ชิปที่เลือกใช้สีของความหมายนั้น ไม่ใช่สีแบรนด์ — สีตรงนี้เข้ารหัสข้อมูล
   เหมือนแถบในแถว (กฎเดียวกับชิปกรองในหน้ากลุ่มเสี่ยง) */
.ao-chip.c-warn.is-on { background: var(--rd-wash-gold, color-mix(in srgb, var(--gold) 18%, transparent));
                        border-color: var(--gold-ink); color: var(--gold-ink); }
.ao-chip.c-none.is-on { background: var(--bg-soft); border-color: var(--text-muted); color: var(--text-main); }

.ao-bar {
  display: inline-block; vertical-align: middle; width: 74px; height: 7px;
  border-radius: 50px; background: var(--bg-soft); overflow: hidden; margin-right: 6px;
}
.ao-bar > i { display: block; height: 100%; border-radius: 50px; background: var(--sk-dk); }
.ao-bar.is-ok  > i { background: var(--green-ink); }
.ao-bar.is-bad > i { background: var(--red-ink); }
.ao-num { font-size: .8rem; font-variant-numeric: tabular-nums; color: var(--text-main); }
/* แถวที่ต้องดู — แถบซ้ายบาง ๆ พอให้สะดุดตาโดยไม่กลบตัวเลข */
.ao-row.is-risk td:first-child { box-shadow: inset 3px 0 0 var(--gold-ink); }

/* ไฟล์แนบจากครูหลายไฟล์ (migration_v36) — เรียงลงมาทีละบรรทัด
   ไม่ใช่ต่อกันในบรรทัดเดียว เพราะชื่อไฟล์ไทยยาวจนอ่านไม่ออกเมื่อวางเรียงกัน */
.asg-files { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }

/* ── สั่งงานถึงใคร + ไฟล์แนบเดิม (migration_v36) ─────────────────────────── */
.asg-targets {
  border: 1px solid color-mix(in srgb, var(--sk) 30%, var(--border-color));
  border-radius: var(--r-md); padding: 10px; background: color-mix(in srgb, var(--sk) 5%, var(--bg-card));
}
.asg-tg-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 8px; }
.asg-tg-n { font-size: .82rem; font-weight: 700; color: var(--sk-ink); }
.asg-tg-n b { font-family: var(--font-display); font-size: 1rem; }
/* รายชื่อยาวเท่าจำนวนนักเรียนทั้งห้อง (54 คน) — ต้องเลื่อนในกล่องของตัวเอง
   ไม่งั้นโมดัลยาวจนปุ่มบันทึกหลุดออกนอกจอ */
.asg-tg-list {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 4px;
  max-height: 210px; overflow-y: auto; padding-right: 4px;
}
.asg-tg-i {
  display: flex; align-items: center; gap: 7px; padding: .3rem .5rem; cursor: pointer;
  border-radius: var(--r-sm); font-size: .84rem; transition: background .14s;
}
.asg-tg-i:hover { background: var(--bg-soft); }
.asg-tg-i:has(input:checked) { background: var(--sk-wash); }
.asg-tg-i[hidden] { display: none; }
.asg-tg-no {
  flex: none; min-width: 22px; text-align: center; font-size: .72rem; font-weight: 700;
  color: var(--text-muted); font-variant-numeric: tabular-nums;
}
.asg-tg-nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.asg-tg-i:has(input:disabled) { opacity: .5; cursor: default; }

.asg-oldfiles { display: flex; flex-direction: column; gap: 5px; }
.asg-oldfile {
  display: flex; align-items: center; gap: 8px; padding: .35rem .6rem;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-card); font-size: .82rem;
}
.asg-oldfile > .bi { color: var(--sk-ink); flex: none; }
.asg-oldfile .n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
html.dark .asg-tg-i:hover { background: rgba(255,255,255,.05); }
@media (max-width: 575px) { .asg-tg-list { grid-template-columns: 1fr; } }

/* ══════════════════════════════════════════════════════════════════════════
   ตรวจข้อสอบกลางภาค/ปลายภาค `.xr-*` (migration_v44)
   หน้าเดียวใช้ทั้งฝั่งครูและฝั่งผู้ตรวจ — สิ่งที่ต่างกันคือ "ปุ่มที่โผล่"
   ไม่ใช่หน้าตาของข้อสอบ เพื่อให้ทั้งสองฝ่ายเถียงกันบนภาพเดียวกัน
══════════════════════════════════════════════════════════════════════════ */

/* ตัวเลขงานค้างบนเมนูซ้าย — ถ้าไม่มีตัวเลข ผู้ตรวจต้องเปิดเข้าไปดูเองทุกวัน */
.sidebar-badge {
  margin-left: auto; flex: none; min-width: 20px; padding: 1px 6px;
  border-radius: var(--r-pill); background: var(--red); color: #fff;
  font-size: .68rem; font-weight: 700; text-align: center; line-height: 1.5;
  font-variant-numeric: tabular-nums;
}
.sidebar-link.active .sidebar-badge { background: rgba(0,0,0,.28); }

/* ── หัวเรื่องย่อยของแต่ละส่วน ─────────────────────────────────────────── */
.xr-secttl {
  display: flex; align-items: center; gap: 8px; margin: 0 0 10px;
  font-weight: 700; color: var(--text-main);
}
.xr-secttl > i { color: var(--sk-dk); }
.xr-count {
  min-width: 22px; padding: 0 7px; border-radius: var(--r-pill);
  background: var(--bg-soft); color: var(--text-muted);
  font-size: .74rem; font-weight: 700; font-variant-numeric: tabular-nums;
}

/* ── แถวในกล่องงาน ────────────────────────────────────────────────────── */
.xr-row {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  background: var(--bg-card); border: 1px solid var(--border-color);
  border-radius: var(--r-lg); text-decoration: none; color: inherit;
  transition: border-color .16s, transform .16s;
}
.xr-row:hover { border-color: var(--sk-lt); transform: translateY(-1px); color: inherit; }
.xr-row-ic {
  flex: none; width: 38px; height: 38px; border-radius: var(--r-md);
  display: grid; place-items: center; font-size: 1.05rem;
  background: var(--bg-soft); color: var(--text-muted);
}
.xr-row-ic.is-head   { background: var(--sk-wash);    color: var(--sk-ink); }
.xr-row-ic.is-assess { background: var(--gold-wash);  color: var(--gold-ink); }
.xr-row-ic.is-ok     { background: var(--green-wash); color: var(--green-ink); }
.xr-row-ic.is-back   { background: var(--red-wash);   color: var(--red-ink); }
.xr-row-main { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 3px; }
.xr-row-t { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-weight: 700; }
.xr-row-s { font-size: .8rem; color: var(--text-muted); }
.xr-row-r { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; text-align: right; }
.xr-row-st { font-size: .76rem; font-weight: 700; color: var(--sk-ink); }
.xr-row-w  { font-size: .74rem; color: var(--text-muted); }

/* ── หัวข้อสอบ ─────────────────────────────────────────────────────────── */
.xr-hd {
  display: flex; gap: 16px; flex-wrap: wrap; justify-content: space-between;
  padding: 16px 18px; margin-bottom: 14px;
  background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--r-lg);
}
.xr-hd-main { min-width: 0; flex: 1 1 320px; }
.xr-hd-t { margin: 0 0 8px; font-weight: 700; }
.xr-hd-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.xr-hd-s { font-size: .84rem; color: var(--text-muted); }
.xr-hd-nums { display: flex; gap: 22px; flex: none; align-items: flex-start; }
.xr-num { text-align: center; }
.xr-num b { display: block; font-size: 1.4rem; font-weight: 700; line-height: 1.1;
  color: var(--text-main); font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.xr-num span { font-size: .74rem; color: var(--text-muted); }

/* ── แถบขั้นตอน ───────────────────────────────────────────────────────── */
.xr-flow {
  display: flex; flex-wrap: wrap; gap: 6px 4px; list-style: none;
  margin: 0 0 14px; padding: 0;
}
.xr-step {
  display: flex; align-items: center; gap: 7px; flex: 1 1 150px;
  padding: 9px 12px; border-radius: var(--r-md);
  background: var(--bg-soft); color: var(--text-muted); font-size: .82rem; font-weight: 600;
}
.xr-step-n {
  flex: none; width: 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center; font-size: .74rem; font-weight: 700;
  background: var(--bg-card); color: var(--text-muted);
}
.xr-step.is-done { background: var(--green-wash); color: var(--green-ink); }
.xr-step.is-done .xr-step-n { background: var(--green-ink); color: #fff; }
.xr-step.is-now  { background: var(--sk-wash); color: var(--sk-ink); }
.xr-step.is-now .xr-step-n { background: var(--sk-dk); color: #fff; }
.xr-step.is-back { background: var(--red-wash); color: var(--red-ink); }
.xr-step.is-back .xr-step-n { background: var(--red-ink); color: #fff; }

/* ── แถบเตือน/สั่งงาน ─────────────────────────────────────────────────── */
.xr-alert {
  padding: 13px 16px; margin-bottom: 14px; border-radius: var(--r-lg);
  border: 1px solid var(--border-color); background: var(--bg-card);
}
.xr-alert.is-back { border-color: var(--red); background: var(--red-wash); }
.xr-alert.is-warn { border-color: var(--gold); background: var(--gold-wash); }
/* แจ้งให้รู้เฉย ๆ ไม่ใช่คำเตือน — ใช้กับ "ข้อสอบชุดนี้มีอยู่หลายห้อง"
   ซึ่งเป็นข้อเท็จจริงที่ครูต้องรู้ก่อนเริ่มแก้ ไม่ใช่ความผิดพลาดที่ต้องไปจัดการ */
.xr-alert.is-info { border-color: var(--sk); background: var(--sk-wash); }
.xr-alert-t { display: flex; align-items: center; gap: 8px; font-weight: 700; margin-bottom: 4px; }
.xr-alert.is-back .xr-alert-t { color: var(--red-ink); }
.xr-alert.is-warn .xr-alert-t { color: var(--gold-ink); }
.xr-alert.is-info .xr-alert-t { color: var(--sk-ink); }
/* ป้ายห้องพี่น้องเป็นลิงก์ — ต้องดูกดได้ ไม่ใช่ป้ายเฉย ๆ */
.xr-alert.is-info a.rd-tag { text-decoration: none; border: 1px solid var(--sk); }
.xr-alert.is-info a.rd-tag:hover { background: var(--sk); color: #fff; }
.xr-alert-b { font-size: .86rem; color: var(--text-main); }

/* แถบเตือนของหน้าตรวจข้อสอบ
   ─────────────────────────────────────────────────────────────────────────
   **ห้ามกลับไปเป็น `display: flex`** (เจ้าของแจ้ง 4 ก.ย. 2569 ว่า "อ่านแล้วงง"
   พร้อมภาพประโยคที่แตกเป็นเสี่ยง) — ในกล่อง flex ข้อความเปล่า ๆ ที่คั่นระหว่าง
   `<span>` กับ `<b>` จะกลายเป็น **anonymous flex item** ทุกท่อน ประโยคเดียว
   จึงถูกหั่นเป็นคอลัมน์เรียงข้างกันแล้วตัดคำคนละที่ อ่านไม่รู้เรื่อง
   อาการโผล่เฉพาะแถบที่มีป้าย/ตัวหนาปนอยู่ในประโยค แถบที่เป็นข้อความล้วนดูปกติ
   จึงไม่มีใครสังเกตมาตลอด

   ไอคอนวางแบบ absolute แทน — ตัวกล่องเป็น block ธรรมดา ข้อความไหลเป็นย่อหน้า
   เดียวตามปกติ ใช้ได้กับทุกแถบทั้งที่มีอยู่และที่จะเพิ่มมาทีหลัง โดยไม่ต้อง
   ไปห่อ `<div>` เพิ่มทีละที่ (มี 12 ที่ในหน้าตรวจข้อสอบอย่างเดียว)          */
.xr-warn {
  position: relative;
  display: block;
  margin-bottom: 8px;
  padding: 7px 10px 7px 31px;
  border-radius: var(--r-sm);
  background: var(--gold-wash); color: var(--gold-ink);
  font-size: .8rem; font-weight: 600; line-height: 1.6;
}
.xr-warn > i { position: absolute; left: 10px; top: 9px; }

/* ── ใบปะหน้า (มุมมองอ่านอย่างเดียว) ──────────────────────────────────── */
.xr-dl { display: grid; grid-template-columns: 190px minmax(0,1fr); gap: 8px 14px; margin: 0; }
.xr-dl dt { font-size: .8rem; font-weight: 600; color: var(--text-muted); }
.xr-dl dd { margin: 0; font-size: .88rem; color: var(--text-main); }
.xr-bl { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 8px; }
.xr-bl label { display: flex; flex-direction: column; gap: 3px; font-size: .76rem; color: var(--text-muted); font-weight: 600; }

/* ── ตัวข้อสอบ + เฉลย ─────────────────────────────────────────────────── */
.xr-q.has-note { border-color: color-mix(in srgb, var(--red) 55%, var(--border-color)); }
.xr-stim > summary { cursor: pointer; font-size: .84rem; font-weight: 600; color: var(--sk-ink); }
.xr-stim-b { margin-top: 6px; padding: 10px 12px; border-radius: var(--r-sm);
  background: var(--bg-soft); font-size: .86rem; white-space: pre-wrap; }

.xr-ch { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.xr-ch > li {
  display: flex; align-items: flex-start; gap: 8px; padding: 7px 10px;
  border-radius: var(--r-sm); background: var(--bg-soft); font-size: .88rem;
}
/* เฉลยต้องเห็นจากหางตา — ผู้ตรวจไล่หลายสิบข้อ ถ้าต้องเพ่งอ่านทีละบรรทัดว่า
   ข้อไหนถูก จะพลาดข้อที่ไม่มีตัวถูกเลย หรือข้อที่ถูกสองตัว ซึ่งคือบั๊กที่กำลังตรวจหา */
.xr-ch > li.is-key { background: var(--green-wash); color: var(--green-ink); font-weight: 600; }
.xr-ch-n { flex: none; font-weight: 700; opacity: .75; }
.xr-ch-ok { margin-left: auto; color: var(--green-ink); }
.xr-ord { margin: 0; padding-left: 22px; font-size: .88rem; }
.xr-ord > li { margin-bottom: 3px; }
.xr-tb .xr-key { color: var(--green-ink); font-weight: 600; }
.xr-ans {
  padding: 9px 12px; border-radius: var(--r-sm); background: var(--green-wash);
  color: var(--green-ink); font-size: .88rem;
}
.xr-ans > span { display: block; font-size: .72rem; font-weight: 700; opacity: .8; margin-bottom: 2px; }

/* ── ความเห็นของผู้ตรวจ ───────────────────────────────────────────────── */
.xr-notes { margin-top: 12px; padding-top: 11px; border-top: 1px dashed var(--border-color);
  display: flex; flex-direction: column; gap: 8px; }
.xr-note {
  padding: 9px 12px; border-radius: var(--r-sm);
  background: var(--red-wash); border-left: 3px solid var(--red);
}
/* ที่แก้แล้วยังอยู่ครบ แค่จางลง — ลบทิ้งเมื่อกดแก้แล้ว จะทำให้รอบถัดไป
   ไม่มีใครรู้ว่าเคยถูกติงอะไรไปบ้าง ซึ่งคือสิ่งที่ผู้ตรวจคนที่สองต้องอ่าน */
.xr-note.is-done { background: var(--bg-soft); border-left-color: var(--green); opacity: .72; }
.xr-note-hd { display: flex; justify-content: space-between; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.xr-note-who { display: flex; align-items: center; gap: 6px; font-size: .8rem; font-weight: 700; color: var(--text-main); }
.xr-note-stage { font-weight: 600; font-size: .72rem; color: var(--text-muted); }
.xr-note-t { font-size: .72rem; color: var(--text-muted); }
.xr-note-b { font-size: .88rem; color: var(--text-main); }
.xr-note-ft { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 6px; }
.xr-note-ok { display: inline-flex; align-items: center; gap: 4px; font-size: .74rem; font-weight: 700; color: var(--green-ink); }
.xr-mini {
  display: inline-flex; align-items: center; gap: 4px; padding: 2px 0;
  background: none; border: 0; cursor: pointer;
  font-size: .76rem; font-weight: 600; color: var(--sk-ink);
}
.xr-mini:hover { text-decoration: underline; }
.xr-mini.is-del { color: var(--red-ink); }
.xr-noteform { display: flex; gap: 8px; align-items: flex-start; }
.xr-noteform textarea { flex: 1 1 auto; min-width: 0; }
.xr-noteform .btn { flex: 0 0 auto; white-space: nowrap; }

/* ── ไทม์ไลน์ ─────────────────────────────────────────────────────────── */
.xr-tl { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.xr-tl-i { display: flex; gap: 10px; }
.xr-tl-ic {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center; font-size: .78rem;
  background: var(--bg-soft); color: var(--text-muted);
}
.xr-tl-i.is-green .xr-tl-ic { background: var(--green-wash); color: var(--green-ink); }
.xr-tl-i.is-red   .xr-tl-ic { background: var(--red-wash);   color: var(--red-ink); }
.xr-tl-i.is-blue  .xr-tl-ic { background: var(--sk-wash);    color: var(--sk-ink); }
.xr-tl-i.is-gold  .xr-tl-ic { background: var(--gold-wash);  color: var(--gold-ink); }
.xr-tl-t { font-size: .86rem; font-weight: 700; color: var(--text-main); }
.xr-tl-s { font-size: .76rem; color: var(--text-muted); }
.xr-tl-n { margin-top: 4px; padding: 6px 9px; border-radius: var(--r-sm);
  background: var(--bg-soft); font-size: .82rem; color: var(--text-main); }

/* รางขวาเกาะไว้ — ผู้ตรวจเลื่อนอ่านข้อสอบ 40 ข้อแล้วต้องเลื่อนกลับขึ้นมากดปุ่ม
   ไม่ได้ · เกาะเฉพาะจอกว้าง บนมือถือรางไปอยู่ท้ายหน้าตามปกติ */
@media (min-width: 992px) {
  .xr-rail { position: sticky; top: 16px; }
}
@media (max-width: 700px) {
  .xr-dl { grid-template-columns: 1fr; gap: 2px 0; }
  .xr-dl dd { margin-bottom: 8px; }
  .xr-bl { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .xr-noteform { flex-direction: column; }
  .xr-noteform .btn { width: 100%; justify-content: center; }
  .xr-row { flex-wrap: wrap; }
  .xr-row-r { align-items: flex-start; text-align: left; }
}
@media (prefers-reduced-motion: reduce) { .xr-row { transition: none; } }

/* เลือกข้อเข้าตัวชี้วัด — ข้อสอบปลายภาคมี 40-60 ข้อ รายการต้องเลื่อนในกล่อง
   ของตัวเอง ไม่งั้นปุ่มบันทึกหลุดออกนอกจอ (บทเรียนเดียวกับ .asg-tg-list) */
.xr-qpick {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 4px;
  max-height: 320px; overflow-y: auto; padding-right: 4px;
}
.xr-qpick-i {
  display: flex; align-items: center; gap: 8px; padding: .35rem .5rem; cursor: pointer;
  border-radius: var(--r-sm); font-size: .84rem; transition: background .14s;
}
.xr-qpick-i:hover { background: var(--bg-soft); }
.xr-qpick-i:has(input:checked) { background: var(--sk-wash); }
.xr-qpick-n {
  flex: none; min-width: 24px; text-align: center; font-size: .74rem; font-weight: 700;
  color: var(--text-muted); font-variant-numeric: tabular-nums;
}
.xr-qpick-b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
html.dark .xr-qpick-i:hover { background: rgba(255,255,255,.05); }
@media (max-width: 575px) { .xr-qpick { grid-template-columns: 1fr; } }

/* ปุ่มเผยแพร่ที่ถูกด่านตรวจข้อสอบกั้นไว้ — แสดงเป็นข้อความล็อก ไม่ใช่ปุ่มที่กดแล้วเด้ง
   error เพราะปุ่มที่กดได้แต่ไม่มีวันสำเร็จ อ่านว่า "ระบบพัง" ไม่ใช่ "ยังไม่ถึงคิว" */
.xh-lockmsg { display: inline-flex; align-items: center; gap: 4px; font-size: .78rem;
  font-weight: 600; color: var(--text-muted); cursor: help; }

/* ══════════════════════════════════════════════════════════════════════════
   ห้องสอบจริง `.pr-*` (migration_v45) — ฝั่งครูประจำวิชา
   (คอนโซลของครูผู้คุมมี CSS ของตัวเองอยู่ในไฟล์ pages/proctor.php ทั้งก้อน
    เพราะหน้านั้นไม่โหลด theme.css เลย — ผู้คุมไม่ได้ล็อกอิน)
══════════════════════════════════════════════════════════════════════════ */

.pr-linkbox { display: flex; gap: 6px; align-items: center; }
.pr-linkbox input {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .74rem;
  background: var(--bg-soft); border-color: var(--border-color); color: var(--text-muted);
}

/* ใบคุมสอบในโมดัล — หน้าจอกับหน้ากระดาษใช้มาร์กอัปชุดเดียวกัน (prPrint()
   คัดลอก innerHTML ไปหน้าต่างใหม่) สไตล์ตรงนี้จึงมีไว้ให้ "พรีวิว" อ่านออก */
.pr-slip { color: var(--text-main); }
.pr-slip-h { text-align: center; font-size: .9rem; color: var(--text-muted); }
.pr-slip-t { text-align: center; margin: 4px 0 16px; font-weight: 700; }
.pr-slip-tb { width: 100%; border-collapse: collapse; margin-bottom: 14px; font-size: .9rem; }
.pr-slip-tb th { text-align: left; width: 110px; padding: 5px 0; color: var(--text-muted); font-weight: 600; vertical-align: top; }
.pr-slip-tb td { padding: 5px 0; }
.pr-slip-qr { text-align: center; margin: 8px 0; }
/* QRCode.js วาดทั้ง <canvas> และ <img> ซ้อนกัน — ปล่อยไว้จะได้ QR สองอันเรียงกัน */
.pr-slip-qr canvas { display: none; }
.pr-slip-qr img { display: inline-block; }
.pr-slip-url { text-align: center; font-size: .74rem; word-break: break-all;
  color: var(--text-muted); margin-bottom: 12px; }
.pr-slip-steps { font-size: .86rem; line-height: 1.75; padding-left: 20px; margin: 0; }
.pr-slip-warn { margin-top: 12px; padding: 8px 12px; border-radius: var(--r-sm);
  background: var(--gold-wash); color: var(--gold-ink); font-size: .82rem; font-weight: 600; }

/* รหัสเข้าสอบแบบคงที่บนหน้าครู — ต้องอ่านออกจากที่ที่ครูยืนเขียนกระดาน */
.pr-bigcode {
  font-size: clamp(2.4rem, 9vw, 3.4rem); font-weight: 800; letter-spacing: .18em;
  font-variant-numeric: tabular-nums; color: var(--text-main); line-height: 1.2;
}
.pr-code-cell { font-size: 1rem; font-weight: 700; letter-spacing: .08em; color: var(--sk-ink); }

/* ══════════════════════════════════════════════════════════════════════════
   ช่องเลือกวัน+เวลา `.dtf` — dateTimeField() (functions.php) + main.js
   แทน <input type="datetime-local"> ที่เจ้าของแจ้งว่าใช้ยาก
   ช่องวันกับช่องเวลาแยกกัน เป้ากดใหญ่ ปุ่มลัดอยู่ใต้ช่อง และมีบรรทัดยืนยัน
   เป็นภาษาไทย พ.ศ. เพราะการกรอกผิดปีคือความผิดพลาดที่ไม่มีอะไรฟ้องเลย
══════════════════════════════════════════════════════════════════════════ */
/* ── ความกว้างขั้นต่ำของช่องวันที่ ต้องมี ────────────────────────────────────
   `<input type=date>` เป็นวิดเจ็ตของเบราว์เซอร์ มันไม่ย่อข้อความข้างในตาม
   ความกว้างช่อง — บีบเมื่อไหร่มัน **เฉือนตัวเลขทิ้งเงียบ ๆ** ("09/02/2…")
   ของเดิมเขียน `flex: 1 1 58%` + `min-width: 0` ซึ่งอนุญาตให้ย่อได้ไม่จำกัด
   ในคอลัมน์ `col-md-4` ของโมดัล (กว้าง 261px) ช่องวันได้ไป 112px ทั้งที่
   วิดเจ็ตต้องการ 136px — วันที่เลยถูกตัดหายตลอดบนจอใหญ่ (จอเล็กกลับไม่เป็น
   เพราะคอลัมน์เต็มความกว้าง)
   ตั้งขั้นต่ำไว้แล้วให้ **ขึ้นบรรทัดใหม่แทนการบีบ** เมื่อที่ไม่พอ           */
.dtf-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: stretch; }
.dtf-d { flex: 1 1 150px; min-width: 150px; }
.dtf-t { flex: 1 1 110px; min-width: 110px; }
.dtf-row input { font-variant-numeric: tabular-nums; }

/* ปุ่มวัน/เวลา — หน้าตาเหมือนช่องกรอก แต่เป็นปุ่มเปิดปฏิทิน/นาฬิกาของเรา */
.dtf-d, .dtf-t {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  text-align: left; cursor: pointer; font-variant-numeric: tabular-nums;
}
.dtf-d > i, .dtf-t > i { color: var(--sk-dk); flex: none; font-size: .9rem; }
.dtf-d.is-empty .dtf-btx, .dtf-t.is-empty .dtf-btx { color: var(--text-muted); font-weight: 400; }
.dtf-btx { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── ปฏิทิน/นาฬิกาของเราเอง ─────────────────────────────────────────────────
   ของเบราว์เซอร์ใช้ยากสำหรับครูไทย: เดือนเป็นภาษาอังกฤษ · ปีเป็น ค.ศ. ·
   เวลาเป็น AM/PM ที่ต้องเลื่อนหาทีละบรรทัดในคอลัมน์แคบ ๆ
   อยู่ที่ `document.body` ตัวเดียวทั้งหน้า แล้วย้ายตำแหน่งไปใต้ปุ่มที่กด —
   ไม่ต้องกลัวถูก `overflow:hidden` ของการ์ด/โมดัลเฉือน                      */
.dtf-pop {
  position: absolute; z-index: 1080;          /* เหนือ .modal (1055) */
  background: var(--bg-card); border: 1px solid var(--border-color);
  border-radius: var(--r-md); box-shadow: 0 10px 30px rgba(0,0,0,.18);
  padding: 10px;
}
.dtf-cal-hd { display: flex; align-items: center; gap: 2px; margin-bottom: 8px; }
.dtf-cal-ti {
  flex: 1 1 auto; text-align: center; font-weight: 700; font-size: .9rem;
  font-family: var(--font-display);
}
.dtf-nav {
  flex: none; width: 28px; height: 28px; border: 0; border-radius: var(--r-sm);
  background: var(--bg-soft); color: var(--text-main); cursor: pointer;
  font-size: 1rem; line-height: 1;
}
.dtf-nav:hover { background: var(--sk-wash); color: var(--sk-ink); }
.dtf-cal-gr { display: grid; grid-template-columns: repeat(7, 34px); gap: 2px; }
.dtf-cal-dh {
  display: grid; place-items: center; height: 24px;
  font-size: .7rem; font-weight: 700; color: var(--text-muted);
}
.dtf-cal-d {
  height: 32px; border: 0; border-radius: var(--r-sm); cursor: pointer;
  background: transparent; color: var(--text-main); font-size: .85rem; font-weight: 600;
  font-family: inherit; font-variant-numeric: tabular-nums;
}
.dtf-cal-d:hover { background: var(--sk-wash); color: var(--sk-ink); }
.dtf-cal-d.is-today { box-shadow: inset 0 0 0 1.5px var(--sk); }
.dtf-cal-d.is-sel { background: var(--sk-dk); color: #fff; }
.dtf-cal-ft {
  display: flex; justify-content: space-between; gap: 8px;
  margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border-color);
}
.dtf-cal-x {
  border: 0; background: transparent; cursor: pointer; font-family: inherit;
  font-size: .8rem; font-weight: 700; color: var(--sk-ink); padding: 2px 4px;
}
.dtf-cal-x:hover { text-decoration: underline; }

/* นาฬิกา — 24 ชั่วโมง เป้ากดใหญ่ ไม่มี AM/PM ให้ต้องแปลงในหัว */
.dtf-tm { display: flex; gap: 8px; }
.dtf-tm-col {
  max-height: 216px; overflow-y: auto; scrollbar-width: thin;
  display: flex; flex-direction: column; gap: 2px; min-width: 62px;
}
.dtf-tm-lb {
  position: sticky; top: 0; background: var(--bg-card); z-index: 1;
  font-size: .7rem; font-weight: 700; color: var(--text-muted);
  text-align: center; padding-bottom: 4px;
}
.dtf-tm-i {
  flex: none; height: 32px; border: 0; border-radius: var(--r-sm); cursor: pointer;
  background: var(--bg-soft); color: var(--text-main);
  font-size: .85rem; font-weight: 600; font-family: inherit; font-variant-numeric: tabular-nums;
}
.dtf-tm-i:hover { background: var(--sk-wash); color: var(--sk-ink); }
.dtf-tm-i.is-sel { background: var(--sk-dk); color: #fff; }
.dtf-clear {
  flex: 0 0 auto; width: 38px; border: 1px solid var(--border-color);
  background: var(--bg-card); color: var(--text-muted);
  border-radius: var(--r-sm); cursor: pointer; font-size: .8rem; line-height: 1;
}
.dtf-clear:hover { background: var(--red-wash); color: var(--red-ink); border-color: var(--red); }

.dtf-chips { display: flex; flex-wrap: wrap; gap: 5px 10px; margin-top: 6px; }
/* กลุ่มปุ่มวัน / กลุ่มปุ่มเวลา — อยู่ด้วยกันเสมอแม้พันบรรทัด */
.dtf-chipgrp { display: flex; flex-wrap: wrap; gap: 5px; }
.dtf-chip {
  border: 1px solid var(--border-color); background: var(--bg-card); color: var(--text-muted);
  border-radius: var(--r-pill); padding: 3px 11px; font-size: .76rem; font-weight: 600;
  cursor: pointer; font-family: inherit; font-variant-numeric: tabular-nums;
}
.dtf-chip:hover { border-color: var(--sk); color: var(--sk-ink); background: var(--sk-wash); }
/* ปุ่มเวลาแยกโทนจากปุ่มวัน — สองแถวนี้ทำคนละเรื่องแต่หน้าตาเหมือนกันจะกดสลับกัน */
.dtf-chip.is-time { background: var(--bg-soft); }

.dtf-pv {
  margin-top: 7px; font-size: .84rem; color: var(--text-main); font-weight: 600;
  min-height: 1.4em;
}
.dtf-pv .dtf-none { color: var(--text-muted); font-weight: 400; }
.dtf-pv .dtf-rel  { color: var(--text-muted); font-weight: 400; }
.dtf-pv .dtf-past { color: var(--red-ink); }

@media (max-width: 480px) {
  /* บนมือถือช่องวันที่ของ Chrome กว้างกว่าที่คิด — ให้ขึ้นบรรทัดใหม่แทนการบีบ */
  .dtf-row { flex-wrap: wrap; }
  .dtf-d { flex: 1 1 100%; }
  .dtf-t { flex: 1 1 calc(100% - 44px); }
}

/* ── นำเข้าข้อสอบจากไฟล์ `.xe-imp-*` (exam_edit) ───────────────────────────
   สามทางเข้า (Word · Excel · วางข้อความ) ต้องอยู่ในกล่องเดียวกันและไปจบที่หน้า
   ตัวอย่างชุดเดียวกัน — แยกเป็นสามแท็บจะทำให้ครูต้องเลือกก่อนว่าจะใช้ทางไหน
   ทั้งที่หลายคนมีทั้งไฟล์และข้อความปนกัน */

/* ── ออกแบบใหม่ 9 ก.ย. 2569 ─────────────────────────────────────────────────
   เจ้าของแจ้งว่าแผงนี้ *"ดูยาก และดูปน ๆ กันกับ PISA"* · ของเดิมกางทุกอย่าง
   ออกมาพร้อมกัน (กล่องลากไฟล์ + คำเตือนสองย่อหน้า + เทมเพลตสามปุ่ม + คำอธิบาย
   รูปแบบไฟล์สองแบบ + ตัวอย่าง + ช่องวางข้อความ) ครูที่แค่จะลากไฟล์มาวางจึงต้อง
   อ่านผ่านทั้งหมดก่อนถึงจะเจอปุ่ม · ตอนนี้เหลือ **สองทางเลือกวางคู่กัน** แล้ว
   คำอธิบายทั้งหมดยุบลงกล่องช่วยเหลือกล่องเดียวที่พับอยู่                     */
.xe-imp-lead {
  display: flex; gap: 12px; align-items: flex-start; margin-bottom: 14px;
  padding: 12px 14px; border-radius: var(--r-md);
  background: color-mix(in srgb, var(--sk) 8%, var(--bg-card));
  border: 1px solid color-mix(in srgb, var(--sk) 34%, transparent);
}
.xe-imp-lead-ic {
  flex: none; display: grid; place-items: center; width: 38px; height: 38px;
  border-radius: var(--r-sm); background: var(--sk-wash); color: var(--sk-ink); font-size: 1.05rem;
}
.xe-imp-lead b { display: block; font-size: .92rem; color: var(--text-main); }
.xe-imp-lead p { margin: 3px 0 0; font-size: .82rem; color: var(--text-muted); line-height: 1.6; }

/* สองทางเข้าวางคู่กัน — ครูหลายคนมีทั้งไฟล์และข้อความปนกัน การบังคับให้เลือก
   แท็บก่อนคือการถามคำถามที่ยังตอบไม่ได้ */
.xe-imp-two { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.xe-imp-way {
  border: 1px solid var(--border-color); border-radius: var(--r-md); padding: 12px;
  background: var(--bg-card); min-width: 0;
}
.xe-imp-way-t {
  display: flex; align-items: center; gap: 8px; margin-bottom: 9px;
  font-size: .86rem; font-weight: 800; color: var(--text-main);
}
.xe-imp-way-t .n {
  flex: none; display: grid; place-items: center; width: 22px; height: 22px;
  border-radius: 50%; background: var(--sk-dk); color: #fff; font-size: .72rem;
}
@media (max-width: 767px) { .xe-imp-two { grid-template-columns: 1fr; } }

/* กล่องช่วยเหลือกล่องเดียว — พับอยู่ ไม่ได้ตัดทิ้ง คนที่ไฟล์เข้าไม่ได้จะกดเปิดเอง */
.xe-imp-help {
  margin-top: 12px; border: 1px solid var(--border-color); border-radius: var(--r-md);
  background: var(--bg-soft);
}
.xe-imp-help > summary {
  cursor: pointer; list-style: none; padding: 10px 13px;
  font-size: .84rem; color: var(--sk-ink); font-weight: 700;
}
.xe-imp-help > summary::-webkit-details-marker { display: none; }
.xe-imp-help > summary:hover { color: var(--sk-dk); }
.xe-imp-help-b { padding: 0 13px 13px; }

/* รูปแบบเลขข้อ/ตัวเลือกที่รับได้ — เขียนไว้ตรง ๆ เพราะครูที่ไฟล์เข้าไม่ได้จะ
   แก้ไฟล์ตัวเองให้ตรงกับที่เห็น ถ้าไม่มีอะไรให้ดูก็ได้แต่เดาแล้วเลิกใช้ */
.xe-fmt-forms { display: flex; flex-direction: column; gap: 8px; margin: 10px 0; }
.xe-fmt-forms > div { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; }
.xe-fmt-forms code {
  padding: 2px 7px; border-radius: var(--r-xs); font-size: .78rem;
  background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-main);
}
.xe-fmt-k {
  flex: 0 0 auto; min-width: 132px; font-size: .8rem; font-weight: 700; color: var(--text-muted);
}
/* กฎรวม — สำคัญกว่าตารางตัวอย่าง เพราะครูที่ไฟล์ต่างจากตารางนิดเดียวจะเลิกใช้ */
.xe-fmt-rule {
  margin-top: 4px; padding: 8px 11px; border-radius: var(--r-sm);
  background: var(--sk-wash); color: var(--sk-ink); font-size: .8rem; line-height: 1.6;
}
.xe-fmt-rule code { background: var(--bg-card); padding: 1px 5px; border-radius: var(--r-xs); }

/* ช่องติ๊กที่ **เปลี่ยนความหมายของช่องกรอกข้างล่าง** (เช่น "ต้องตอบให้ครบทุกคำ"
   ของข้อเติมคำ) — ต้องอ่านออกว่าเป็นสวิตช์ ไม่ใช่ตัวเลือกเสริมที่มองข้ามได้
   จึงเป็นแถบเต็มความกว้างที่กดได้ทั้งแถบ ไม่ใช่ checkbox เปล่า ๆ ลอยอยู่ */
.xe-chk {
  display: flex; align-items: center; gap: 10px;
  margin-top: 10px; padding: 9px 12px;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-soft); cursor: pointer; font-size: .88rem;
}
.xe-chk:hover { border-color: var(--brand); }
.xe-chk input { width: 17px; height: 17px; flex: none; cursor: pointer; accent-color: var(--brand); }
.xe-chk input:disabled { cursor: not-allowed; }
.xe-chk:has(input:disabled) { opacity: .55; cursor: not-allowed; }

/* ── แถบ "ลบหลายข้อ" ในรายการคำถาม (11 ก.ย. 2569) ──────────────────────────
   ติดอยู่บนสุดของรายการขณะเลื่อน เพราะครูมักเลือกข้อที่กระจายอยู่คนละหน้าจอ
   ถ้าปุ่มลบอยู่ตายที่เดียว ต้องเลื่อนกลับขึ้นไปกดทุกครั้ง · โผล่เฉพาะตอนเลือกแล้ว */
.xe-qbulk {
  position: sticky; top: 8px; z-index: 5;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-bottom: 12px; padding: 8px 14px; border-radius: var(--r-md);
  border: 1px solid color-mix(in srgb, var(--red) 35%, transparent);
  background: color-mix(in srgb, var(--red) 9%, var(--bg-card));
  box-shadow: var(--shadow);
}
.xe-qbulk-all { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; font-size: .85rem; font-weight: 600; }
.xe-qbulk-all input { width: 17px; height: 17px; cursor: pointer; accent-color: var(--red); }
.xe-qbulk-n { font-size: .84rem; color: var(--text-muted); }
.xe-qbulk-n b { color: var(--red-ink); font-size: .95rem; }
/* ช่องติ๊กบนหัวการ์ดของแต่ละข้อ */
.xe-qpick { width: 17px; height: 17px; flex: none; cursor: pointer; accent-color: var(--red); }

/* ── แผงเพิ่ม/แก้คำถาม (exam_edit) — ต้องมีแถบเลื่อนของตัวเอง ────────────────
   เจ้าของแจ้ง (2 ก.ย. 2569) ว่า "เลื่อนลงด้านล่างยาก อยากให้มีแถบเลื่อน"
   แผงนี้ยาวกว่าความสูงจอเสมอ (โจทย์ + การ์ดชนิดคำถาม 7 ใบ + ตัวเลือก +
   ปุ่มท้าย) · `position:sticky` เดิมตรึงหัวแผงไว้ก็จริง แต่ **ส่วนท้ายของแผง
   ตกไปอยู่นอกจอแล้วเลื่อนไปไม่ถึงเลย** เพราะตัวแผงไม่ได้เลื่อน มีแต่หน้าที่เลื่อน
   และแผงก็ตรึงตามหน้าไปด้วย
   จำกัดความสูงเท่าจอ (ลบแถบบน 62px) แล้วให้ตัวเนื้อแผงเลื่อนเอง            */
@media (min-width: 992px) {
  .xe-qpanel {
    position: sticky; top: 18px;
    display: flex; flex-direction: column;
    /* 62px แถบบน + 18px ช่องบน + 40px `padding-bottom` ของ .page-inner
       ตัวสุดท้ายจำเป็น: พอเลื่อนถึงท้ายหน้า คอลัมน์ที่เป็นกล่องอ้างอิงของ sticky
       จะจบก่อนขอบล่างของพื้นที่เลื่อนเท่ากับ padding นั้น แผงที่สูงเกินกว่านั้น
       จะถูกดันขึ้นจนหัวแผงโผล่พ้นจอไป */
    max-height: calc(100vh - 120px);
    /* **ต้องเป็น 0** — `margin-bottom: 22px` ของ .rd-panel นับรวมอยู่ในกล่อง
       sticky ด้วย ทำให้ 702+22+18 = 742 ล้นพื้นที่เลื่อน (738) ไป 4px
       พอล้นเมื่อไหร่ เบราว์เซอร์เลิกตรึงด้านบนแล้วไปตรึงด้านล่างแทน
       ผลคือ **หัวแผงถูกเฉือนหายขึ้นไปนอกจอ** ทั้งที่ตั้ง top:18px ไว้ */
    margin-bottom: 0;
  }
    /* ── **ห้ามใส่ `overflow: hidden` กลับเข้ามา** (เจ้าของแจ้ง 10 ก.ย. 2569) ──
       `.rd-panel` ตั้ง `overflow:hidden` ไว้เพื่อตัดขอบมน · พอมาเจอกับ
       `position:sticky` + `max-height` + ตัวเลื่อนของตัวเองข้างใน กลายเป็นชุดที่
       Chrome **อัปเดตกล่องตัดไม่ครบเวลาความสูงเนื้อในเปลี่ยน** — ครูกดสลับชนิด
       คำถาม/เกณฑ์คะแนนไปมาแล้วครึ่งล่างของแผงเหลือแต่สีพื้นการ์ด (ขาว)
       และ **ปุ่ม "บันทึกคำถาม" ซึ่งอยู่ท้ายสุดก็หายไปด้วย** — ครูรายงานว่า
       "ไม่มีปุ่มให้บันทึก" ซึ่งคืออาการเดียวกันกับ "หน้าขาว" ไม่ใช่คนละเรื่อง

       เคยพยายามแก้ด้วยการบังคับให้วาดใหม่ฝั่ง JS สองรุ่นแล้ว **ผิดทางทั้งคู่**
       (รายละเอียดอยู่ใน `pages/exam_edit.php` ตรงที่เคยมี `xeRepaintPanel()`)
       ตัวที่แก้จริงคือเอากล่องตัดออกจากแผงนี้ แล้วให้หัวแผง/ตัวแผงมนขอบของตัวเอง
       — ไม่มีกล่องตัดบน sticky ก็ไม่มีอะไรให้อัปเดตพลาด                        */
  .xe-qpanel { overflow: visible; }
  /* หัวแผงอยู่กับที่ — ปุ่ม "ยกเลิกแก้ไข" ต้องกดได้ตลอดเวลาที่กำลังแก้อยู่ */
  .xe-qpanel > .rd-panel-hd {
    flex: none;
    border-top-left-radius: var(--r-lg); border-top-right-radius: var(--r-lg);
  }
  .xe-qpanel > .rd-pbody {
    /* **ต้องเขียน `flex`/`min-height` ให้ชัด** — ค่าปริยายของลูกใน flex column คือ
       `0 1 auto` + `min-height:auto` ซึ่งแปลว่า "ห้ามหดต่ำกว่าเนื้อใน" · ตอนนี้มัน
       รอดมาได้เพราะ `overflow-y:auto` ทำให้ค่าต่ำสุดอัตโนมัติกลายเป็น 0 ตามสเปก
       ซึ่งเป็นรายละเอียดที่มองไม่เห็นและพังทันทีถ้ามีใครแก้ `overflow` ทีหลัง */
    flex: 1 1 auto; min-height: 0;
    overflow-y: auto;
    border-bottom-left-radius: var(--r-lg); border-bottom-right-radius: var(--r-lg);
    /* เลื่อนจนสุดแผงแล้วไม่ให้ไปดันหน้าข้างหลังเลื่อนต่อ */
    overscroll-behavior: contain;
    scrollbar-width: thin;
  }
  .xe-qpanel > .rd-pbody::-webkit-scrollbar { width: 10px; }
  .xe-qpanel > .rd-pbody::-webkit-scrollbar-thumb {
    background: color-mix(in srgb, var(--text-muted) 32%, transparent);
    border-radius: 99px; border: 3px solid var(--bg-card);
  }
  .xe-qpanel > .rd-pbody::-webkit-scrollbar-thumb:hover {
    background: color-mix(in srgb, var(--text-muted) 55%, transparent);
  }
}

/* ── แก้ข้อความ/เฉลย ตรงในการ์ดของข้อ (exam_edit · 2 ก.ย. 2569) ─────────────
   งานที่ครูทำบ่อยที่สุดคือแก้คำผิดกับกาเฉลยใหม่ · เดิมต้องโยนทั้งข้อขึ้นฟอร์ม
   ด้านขวาแล้วเลื่อนหาปุ่มบันทึกท้ายฟอร์ม — แก้ตรงที่เห็นสั้นกว่านั้นมาก      */
.xq-inline {
  border: 1px solid var(--sk); border-radius: var(--r-md);
  background: var(--sk-wash); padding: 14px;
}
.xq-inline-ch { display: flex; flex-direction: column; gap: 6px; }
.xq-inline-c { display: flex; align-items: center; gap: 8px; }
.xq-inline-c .k {
  flex: none; display: grid; place-items: center; width: 22px; height: 22px;
  border-radius: var(--r-xs); background: var(--bg-card); color: var(--text-muted);
  font-size: .72rem; font-weight: 700;
}
.xq-inline-c:has(input:checked) .k {
  background: color-mix(in srgb, var(--green) 22%, transparent); color: var(--green-ink);
}
.xq-inline-c .b { font-size: .88rem; }
.xq-inline-c input[type=text] { flex: 1 1 auto; min-width: 0; }

/* ── แบ่งหน้ารายการคำถาม (exam_edit · 2 ก.ย. 2569) ──────────────────────────
   เจ้าของแจ้งว่าข้อสอบ 50 ข้อในหน้าเดียว "ครูเลื่อนหาข้อสอบจนเหนื่อย"
   เป้ากดต้องใหญ่พอกดด้วยนิ้วโป้งบนมือถือได้ (38px) เพราะครูแก้ข้อสอบบนมือถือจริง */
.xe-pager {
  display: flex; flex-wrap: wrap; gap: 6px; justify-content: center;
  margin: 4px 0 26px; scroll-margin-top: 90px;
}
.xe-pg {
  min-width: 38px; height: 38px; padding: 0 10px;
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-card); color: var(--text-main);
  font-size: .86rem; font-weight: 700; text-decoration: none;
  font-variant-numeric: tabular-nums;
}
.xe-pg:hover { border-color: var(--sk); color: var(--sk-ink); background: var(--sk-wash); }
.xe-pg.is-cur { background: var(--sk-dk); border-color: var(--sk-dk); color: #fff; }
/* ปุ่มที่กดไม่ได้ต้อง **ดูกดไม่ได้จริง** — จาง ๆ อย่างเดียวยังชวนให้กดอยู่ */
.xe-pg.is-off { opacity: .38; pointer-events: none; }


/* ══════════════════════════════════════════════════════════════════════════
   สี่หน้าที่เพิ่มมาใหม่ ปรับให้เข้าชุดกับที่เหลือ (2 ก.ย. 2569)
   ─────────────────────────────────────────────────────────────────────────
   เจ้าของแจ้งว่า "หน้าตามันดู AI มาก" — ต้นเหตุคือทั้งสี่หน้าเขียนหน้าตาเอง
   ด้วยคลาส Bootstrap ล้วน (`card border-0 shadow-sm rounded-4` ·
   `card-header bg-danger text-white` · badge ภาษาอังกฤษ · ย่อหน้าอธิบายยาว ๆ)
   แทนที่จะใช้ของที่ทั้งเว็บใช้อยู่แล้ว: `.dash-hero` · `statCards()` ·
   `termPicker()` · `.table-dark-custom` · ชิปกรองแบบ `.rk-chip`

   ที่นี่มีเฉพาะชิ้นที่ยังไม่มีในระบบ · prefix: pa-(ตรวจก่อนพิมพ์ ปพ.) ·
   rmd-(แก้ผลการเรียน) · cr-(ดูแลช่วยเหลือ) · ib-(คลังข้อสอบ)
══════════════════════════════════════════════════════════════════════════ */

/* ── แถบเครื่องมือ/ตัวกรองของหน้าเหล่านี้ (ทรงเดียวกับ .rk-toolbar) ─────────── */
.pa-bar, .rmd-bar, .cr-bar, .ib-bar {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 12px; margin-bottom: 18px;
}

/* ── แท็บของหน้า — ใช้ชิปแบบเดียวกับหน้าคัดกรองกลุ่มเสี่ยง ไม่ใช่ nav-tabs ดิบ ─
   nav-tabs มาตรฐานของ Bootstrap เป็นชิ้นเดียวในเว็บนี้ที่ยังเป็นเส้นขีดล่าง
   ทุกที่อื่นเป็นชิปกลม จึงดูเหมือนหลุดมาจากคนละเว็บ */
.sgtabs { display: flex; flex-wrap: wrap; gap: 7px; border: 0; margin-bottom: 18px; }
.sgtabs .nav-link,
.sgtabs > button {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  padding: .44rem 1rem; border-radius: var(--r-pill); font-size: .86rem; font-weight: 700;
  border: 2px solid var(--border-color); background: var(--bg-card); color: var(--text-muted);
  transition: background .18s cubic-bezier(.16,1,.3,1), color .18s, border-color .18s;
}
.sgtabs .nav-link:hover { border-color: var(--sk); color: var(--sk-dk); }
.sgtabs .nav-link.active { background: var(--sk-dk); border-color: var(--sk-dk); color: #fff; }
.sgtabs .nav-link .sgtabs-n {
  font-variant-numeric: tabular-nums; font-size: .78rem; font-weight: 800;
  background: color-mix(in srgb, currentColor 16%, transparent);
  border-radius: var(--r-pill); padding: 0 7px; min-width: 22px; text-align: center;
}
.sgtabs .nav-link.tone-red.active   { background: var(--red-ink);  border-color: var(--red-ink); }
.sgtabs .nav-link.tone-gold.active  { background: var(--gold-ink); border-color: var(--gold-ink); }
.sgtabs .nav-link.tone-green.active { background: var(--green-ink);border-color: var(--green-ink); }

/* ── การ์ดหัวข้อปัญหา (หน้าตรวจก่อนพิมพ์) ─────────────────────────────────────
   ของเดิมเป็นหัวการ์ดทึบสีแดง/เหลืองเต็มแถบพร้อมคำอธิบายยาวต่อท้ายชื่อเรื่อง
   แถบทึบแปดแถบเรียงกันอ่านเป็นเสียงเตือนเดียวยาว ๆ จนไม่มีอะไรเด่นกว่ากัน */
.pa-issue { border-left: 4px solid var(--pa-c, var(--border-color)); }
.pa-issue.is-critical { --pa-c: var(--red-ink); }
.pa-issue.is-warning  { --pa-c: var(--gold-ink); }
.pa-issue > .card-header {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--bg-soft); border-bottom: 1px solid var(--border-color);
}
.pa-issue > .card-header .pa-ic { color: var(--pa-c); font-size: 1.05rem; }
.pa-issue > .card-header b { font-size: .96rem; }
.pa-issue .pa-n {
  font-size: .76rem; font-weight: 800; color: var(--pa-c); font-variant-numeric: tabular-nums;
  background: color-mix(in srgb, var(--pa-c) 12%, var(--bg-card));
  border: 1px solid color-mix(in srgb, var(--pa-c) 34%, transparent);
  border-radius: var(--r-pill); padding: 1px 9px;
}
.pa-issue .pa-fix { font-size: .8rem; color: var(--text-muted); }
.pa-issue .pa-more { font-size: .78rem; color: var(--text-muted); padding: 8px 16px;
                     border-top: 1px dashed var(--border-color); }

/* สรุปผลตรวจหนึ่งบรรทัด — แทนกล่อง alert สามใบที่พูดเรื่องเดียวกัน */
.pa-verdict { display: flex; align-items: center; gap: 10px; padding: 12px 16px;
  border-radius: var(--r-lg); border: 1px solid var(--border-color);
  background: var(--bg-card); margin-bottom: 18px; font-size: .9rem; }
.pa-verdict i { font-size: 1.3rem; line-height: 1; }
.pa-verdict.is-ok   { border-color: color-mix(in srgb, var(--green-ink) 40%, transparent); }
.pa-verdict.is-ok i { color: var(--green-ink); }
.pa-verdict.is-warn { border-color: color-mix(in srgb, var(--gold-ink) 40%, transparent); }
.pa-verdict.is-warn i { color: var(--gold-ink); }
.pa-verdict.is-bad  { border-color: color-mix(in srgb, var(--red-ink) 42%, transparent); }
.pa-verdict.is-bad i { color: var(--red-ink); }

/* ── ชิปประเภทที่ต้องแก้ (หน้าแก้ผลการเรียน) ─────────────────────────────── */
.rmd-types { display: flex; flex-wrap: wrap; gap: 7px; }
.rmd-type {
  display: inline-flex; align-items: baseline; gap: 7px; text-decoration: none;
  padding: .4rem .95rem; border-radius: var(--r-pill); font-size: .84rem; font-weight: 700;
  border: 2px solid var(--border-color); background: var(--bg-card); color: var(--text-muted);
}
.rmd-type b { color: var(--rmd-c, var(--sk-dk)); font-variant-numeric: tabular-nums; }
.rmd-type:hover { border-color: var(--rmd-c, var(--sk)); color: var(--text-main); }
.rmd-type.is-on { border-color: var(--rmd-c, var(--sk-dk)); color: var(--text-main);
                  background: color-mix(in srgb, var(--rmd-c, var(--sk)) 10%, var(--bg-card)); }
.rmd-type.t-0  { --rmd-c: var(--red-ink); }
.rmd-type.t-r  { --rmd-c: var(--gold-ink); }
.rmd-type.t-ms { --rmd-c: var(--sk-dk); }
.rmd-type.t-mf { --rmd-c: var(--text-muted); }

/* ── การ์ดข้อสอบในคลัง ────────────────────────────────────────────────────── */
.ib-item { border-left: 3px solid transparent; }
.ib-item:hover { border-left-color: var(--sk); }
.ib-tags { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.ib-tag {
  font-size: .74rem; font-weight: 700; border-radius: var(--r-xs); padding: 2px 8px;
  background: color-mix(in srgb, var(--sk) 12%, var(--bg-card));
  color: var(--sk-ink); border: 1px solid var(--border-color);
}
.ib-tag.is-shared { background: color-mix(in srgb, var(--green-ink) 12%, var(--bg-card));
                    color: var(--green-ink); }
.ib-tag.is-mine   { background: var(--bg-soft); color: var(--text-muted); }
.ib-body { white-space: pre-wrap; font-size: .95rem; font-weight: 600; margin: 8px 0; }
.ib-choice { display: flex; gap: 8px; align-items: flex-start; font-size: .86rem;
  border: 1px solid var(--border-color); border-radius: var(--r-sm); padding: 7px 10px;
  background: var(--bg-card); }
.ib-choice.is-ok { border-color: color-mix(in srgb, var(--green-ink) 45%, transparent);
  background: color-mix(in srgb, var(--green-ink) 8%, var(--bg-card)); font-weight: 700; }
.ib-choice .ib-l { font-weight: 800; color: var(--text-muted); flex-shrink: 0; }
.ib-choice.is-ok .ib-l { color: var(--green-ink); }
.ib-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  border-top: 1px dashed var(--border-color); margin-top: 10px; padding-top: 9px;
  font-size: .8rem; color: var(--text-muted); }
.ib-stat { font-variant-numeric: tabular-nums; font-weight: 700; }

/* ── ตารางรายชื่อของงานดูแลช่วยเหลือ ─────────────────────────────────────── */
.cr-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block;
          background: var(--border-color); }
.cr-dot.is-on { background: var(--green-ink); }
.cr-3 { display: inline-flex; gap: 5px; align-items: center; }

/* ── ทางกลับไปแก้ไขข้อสอบ ในกล่องงานตรวจข้อสอบ (3 ก.ย. 2569) ────────────────
   เจ้าของแจ้งว่าเปิดกล่องงานมาแล้ว "ไม่มีที่ให้กดกลับหน้าแก้ไขข้อสอบ"
   ต้องอยู่นอกการ์ดที่เป็นลิงก์ เพราะซ้อน <a> ใน <a> ไม่ได้ */
.xr-rowlinks {
  display: flex; flex-wrap: wrap; gap: 14px;
  margin: -4px 0 6px 54px;      /* เยื้องให้ตรงกับเนื้อความในการ์ด ไม่ใช่ขอบซ้ายสุด */
}
.xr-rowlinks a {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: .8rem; font-weight: 700; color: var(--sk-ink); text-decoration: none;
}
.xr-rowlinks a:hover { text-decoration: underline; }
@media (max-width: 575px) { .xr-rowlinks { margin-left: 8px; } }

/* ── กลุ่มสาระของครู — เลือกได้หลายกลุ่ม (3 ก.ย. 2569) ──────────────────────
   เจ้าของแจ้งว่า "ครู 1 คนอาจได้สอนหลายกลุ่มสาระ" · ครูคอมฯ สอนวิทยาการคำนวณ
   กับการงานอาชีพพร้อมกันเป็นเรื่องปกติ การบังคับเลือกกลุ่มเดียวคือการบังคับให้
   กรอกผิดตั้งแต่แรก */
.tg-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 6px; }
.tg-item {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  padding: 8px 10px; font-size: .86rem; background: var(--bg-card);
}
.tg-item:hover { border-color: var(--sk); background: var(--sk-wash); }
.tg-item.on, .tg-item:has(input:checked) {
  border-color: var(--sk); background: var(--sk-wash); font-weight: 700; color: var(--sk-ink);
}

/* ค่าเดิมที่พิมพ์เองไว้ (ไม่ตรง 9 กลุ่ม) — ติ๊กไว้ให้ ไม่ใช่ทิ้งเงียบ */
.tg-item.tg-old { border-style: dashed; }
.tg-item.tg-old small { opacity: .7; font-weight: 400; }
.tg-item input { flex-shrink: 0; margin: 0; }
.tg-item span { line-height: 1.25; }

/* ตัวพับสำหรับการ์ดแคบ (หน้าเข้าสู่ระบบ กว้าง 360px) — กางแล้วเลื่อนในกรอบ
   ไม่ดันปุ่มลงทะเบียนตกขอบการ์ด */
.tg-pick { width: 100%; }
.tg-pick > summary {
  cursor: pointer; list-style: none;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  padding: 11px 14px; font-size: .9rem; font-weight: 600;
  color: var(--text-muted); background: var(--bg-card);
}
.tg-pick > summary::-webkit-details-marker { display: none; }
.tg-pick > summary::after { content: '▾'; float: right; opacity: .6; }
.tg-pick[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.tg-pick[open] > summary::after { content: '▴'; }
.tg-grid.tg-compact {
  grid-template-columns: 1fr; gap: 4px;
  max-height: 190px; overflow-y: auto;
  border: 1px solid var(--border-color); border-top: 0;
  border-radius: 0 0 var(--r-sm) var(--r-sm);
  padding: 6px;
}
.tg-grid.tg-compact .tg-item { padding: 7px 9px; font-size: .82rem; }

/* ── ไฟล์ตัวอย่างสำหรับนำเข้าข้อสอบ + คำอธิบายรูปแบบไฟล์ (3 ก.ย. 2569) ────────
   เจ้าของแจ้งว่าคำอธิบายเดิม "พวกตามรูปอะมันใช้ยาก" — ตารางคอลัมน์ A/B–F/G
   อ่านแล้วต้องแปลงเป็นภาพในหัวเอง · ไฟล์ตัวอย่างที่โหลดไปเปิดดูได้เลยสอนเร็วกว่า
   ปุ่มโหลดจึงต้องเด่นกว่าคำอธิบาย ไม่ใช่ซ่อนอยู่ในลิงก์เล็ก ๆ ท้ายย่อหน้า */
.xe-tpl {
  margin-top: 12px; padding: 11px 12px;
  border: 1px solid var(--sk-line, var(--border-color));
  border-radius: var(--r-md); background: var(--sk-wash, var(--bg-soft));
}
.xe-tpl-hd { font-size: .84rem; font-weight: 700; color: var(--sk-ink); margin-bottom: 9px; }
.xe-tpl-hd i { margin-right: 6px; }
/* ปุ่มในนี้เป็น `.sg-dlbtn` มาตรฐานแล้ว (3 ก.ย. 2569) — เหลือแค่จัดแถว
   ห้ามกำหนดสี/ขนาดทับ ไม่งั้นปุ่มดาวน์โหลดหน้านี้จะเพี้ยนจากที่อื่นทั้งระบบ */
.xe-tpl-btns { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.xe-tpl-note { margin-top: 8px; font-size: .76rem; color: var(--text-muted); }

/* คำอธิบายรูปแบบไฟล์ — แยกเป็นก้อนต่อชนิดไฟล์ ครูอ่านเฉพาะก้อนของไฟล์ที่ตัวเองมี */
.xe-fmt { margin-bottom: 16px; }
.xe-fmt:last-child { margin-bottom: 0; }
.xe-fmt-t {
  font-size: .86rem; font-weight: 700; color: var(--text-main);
  padding-bottom: 6px; margin-bottom: 8px; border-bottom: 1px solid var(--border-color);
}
.xe-fmt-t i { margin-right: 7px; color: var(--sk-dk); }
.xe-fmt-d { font-size: .82rem; color: var(--text-muted); margin-bottom: 8px; }
.xe-fmt-pre {
  background: var(--bg-soft); border: 1px solid var(--border-color);
  border-radius: var(--r-sm); padding: 10px 12px; margin-bottom: 9px;
  font-size: .8rem; line-height: 1.75; color: var(--text-main);
  white-space: pre; overflow-x: auto;
}
.xe-fmt-pre b { color: var(--red-ink, #c22a2f); }
.xe-fmt-pre span { color: var(--text-muted); font-size: .74rem; }
.xe-fmt-ul { margin: 0; padding-left: 1.1rem; font-size: .8rem; color: var(--text-muted); }
.xe-fmt-ul li { margin-bottom: 4px; }
.xe-fmt-ul b { color: var(--text-main); }
.xe-fmt-ul code, .xe-fmt-d code {
  background: var(--bg-soft); border: 1px solid var(--border-color); border-radius: 4px;
  padding: 1px 5px; font-size: .78rem; color: var(--sk-ink);
}
/* ตารางตัวอย่างของ Excel — ให้หน้าตาเหมือนชีตจริง ครูจะได้เทียบกับไฟล์ตัวเองได้ทันที */
.xe-fmt-tb { font-size: .78rem; }
.xe-fmt-tb th {
  background: var(--bg-soft); text-align: center; font-weight: 700;
  color: var(--text-muted); width: 1%; white-space: nowrap;
}
.xe-fmt-tb td { vertical-align: middle; }
.xe-fmt-tb tr.xe-fmt-hd td { font-weight: 700; background: var(--sk-wash, var(--bg-soft)); color: var(--sk-ink); }
.xe-fmt-empty { text-align: center; color: var(--text-muted); font-style: italic; }

/* ── ระบบสอบ (หน้ารวม `exam_hub`) — 3 ก.ย. 2569 ─────────────────────────────
   เจ้าของยกระบบสอบขึ้นเป็นเมนูหลัก หน้านี้จึงเป็นหน้าแรกที่ครูเห็นเมื่อคิดถึง
   เรื่องสอบ · ลำดับความสำคัญบนหน้า: กำลังสอบอยู่ตอนนี้ > ของค้าง > รายการรวม */
.xh-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; margin-bottom: 14px; }
.xh-stat {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 16px; border-radius: var(--r-lg); text-decoration: none;
  border: 1px solid var(--border-color); background: var(--bg-card); color: var(--text-main);
  transition: border-color .15s, transform .15s, box-shadow .15s;
}
a.xh-stat:hover { transform: translateY(-2px); box-shadow: var(--shadow); color: var(--text-main); }
.xh-stat.is-on { border-color: var(--sk); box-shadow: 0 0 0 2px var(--sk-wash); }
.xh-stat i { font-size: 1.5rem; flex-shrink: 0; }
.xh-stat b { font-size: 1.6rem; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums; }
.xh-stat span { font-size: .78rem; color: var(--text-muted); display: block; margin-top: 3px; }
.xh-stat > b + span, .xh-stat b { display: block; }
.xh-stat.s-green i { color: var(--green-dk); }
.xh-stat.s-sky   i { color: var(--sk-dk); }
.xh-stat.s-gold  i { color: var(--gold-dk); }
.xh-stat.s-red   i { color: var(--red-ink); }

/* เครื่องมือของระบบสอบ — ปุ่มใหญ่พร้อมคำอธิบาย ครูไม่ต้องเดาว่าอันไหนทำอะไร */
.xh-tools { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; margin-bottom: 16px; }
.xh-tool {
  position: relative; display: flex; align-items: center; gap: 13px;
  padding: 14px 16px; border-radius: var(--r-lg); text-decoration: none;
  border: 1px solid var(--border-color); background: var(--bg-card); color: var(--text-main);
  transition: border-color .15s, transform .15s;
}
.xh-tool:hover { border-color: var(--sk); transform: translateY(-2px); color: var(--text-main); }
.xh-tool i { font-size: 1.5rem; color: var(--sk-dk); flex-shrink: 0; }
.xh-tool b { display: block; font-size: .92rem; }
.xh-tool span { display: block; font-size: .76rem; color: var(--text-muted); margin-top: 2px; line-height: 1.4; }
.xh-tool-n {
  position: absolute; top: 10px; right: 12px; font-style: normal;
  min-width: 22px; height: 22px; padding: 0 6px; border-radius: 11px;
  background: var(--warm-red); color: #fff; font-size: .74rem; font-weight: 700;
  display: grid; place-items: center;
}

/* ── ปุ่มดินสอในหน้ารวม = กางแผงจัดการใต้แถว (9 ก.ย. 2569) ─────────────────
   เจ้าของสั่ง *"ให้มันแบบขยายลงมา"* พร้อมภาพการ์ดของหน้า `exams` · รอบก่อนผมทำ
   เป็นโมดัล ซึ่งปิดตารางทั้งหน้าและเล็กเกินกว่าจะใส่สถานะ+ความคืบหน้า+ปุ่มได้ครบ
   แถวขยายใช้การ์ด `.xh-card` **ตัวเดียวกับหน้า `exams`** จึงไม่มีหน้าตาชุดที่สอง
   ให้ดูแล — ที่เพิ่มมาคือกรอบของแถวเท่านั้น                                    */
.xh-row.is-open > td { border-bottom-color: transparent; }
.xh-drawer > td {
  padding: 0 12px 14px !important;
  background: color-mix(in srgb, var(--sk) 4%, var(--bg-card));
}
.xh-drawer .xh-card { margin: 0; box-shadow: var(--shadow); }
/* ปุ่มดินสอตอนกางอยู่ต้องดูออกว่า "อันนี้แหละที่เปิดอยู่"

   **`!important` ที่สีตัวอักษรจำเป็น ห้ามถอด** — `.btn-outline-secondary`
   (บรรทัด ~56) ตั้ง `color: var(--sk-dk) !important` ไว้ทั้งเว็บ ถ้าไม่ทวงคืน
   ตรงนี้ ตัวอักษรจะยังเป็นสีเดียวกับพื้นที่เพิ่งเปลี่ยนเป็น `--sk-dk` พอดี
   → **ไอคอนดินสอหายไปทั้งดวง** ตอนแถวกางอยู่ (เจ้าของแจ้ง 9 ก.ย. 2569
   ว่า "ดินสอหาย" พร้อมภาพปุ่มทึบไม่มีรูป) · เห็นอีกทีตอนเอาเมาส์ไปชี้เท่านั้น
   เพราะกฎ `:hover` ของบรรทัด 57 มี `!important` อยู่แล้ว — จึงหาสาเหตุยาก */
.xh-open.is-on {
  background: var(--sk-dk); border-color: var(--sk-dk); color: #fff !important;
}
.xh-open.is-on:hover { background: var(--sk-deep); border-color: var(--sk-deep); color: #fff !important; }
/* `.xh-facts` / `.xh-when` นิยามไว้แล้วในบล็อกการ์ดข้อสอบข้างบน — แถวขยาย
   ใช้ตัวเดียวกัน **ห้ามนิยามซ้ำที่นี่** ไม่งั้นกฎที่อยู่ทีหลังจะทับของเดิมเงียบ ๆ
   แล้วการ์ดในหน้า `exams` เปลี่ยนหน้าตาตามไปด้วยโดยไม่มีใครตั้งใจ */
/* กำลังสอบอยู่ตอนนี้ — แถวเดียวจบ ผู้คุมกดเข้าห้องสอบได้ระหว่างเด็กนั่งรอ */
.xh-live { border-color: var(--green); }
.xh-live-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 0; border-bottom: 1px solid var(--border-color);
}
.xh-live-row:last-child { border-bottom: 0; padding-bottom: 0; }
.xh-live-t { flex: 1 1 240px; min-width: 0; }
.xh-live-t b { display: block; }
.xh-live-t span { font-size: .78rem; color: var(--text-muted); }

/* ชิปเลือกวิชา */
.xh-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 12px; }
.xh-chip {
  flex-shrink: 0; padding: 7px 13px; border-radius: var(--r-pill);
  border: 1px solid var(--border-color); background: var(--bg-card);
  color: var(--text-muted); text-decoration: none; font-size: .82rem; font-weight: 600;
}
.xh-chip:hover { border-color: var(--sk); color: var(--sk-ink); }
.xh-chip.is-on { background: var(--sk); border-color: var(--sk); color: #fff; }
.xh-chip span { opacity: .75; font-weight: 400; margin-left: 4px; }

.xh-tb td { vertical-align: middle; }
.xh-name { font-weight: 700; }
/* รหัสวิชาในรายการข้อสอบ — ป้ายเล็กนำหน้าชื่อวิชา (10 ก.ย. 2569)
   ต้องอ่านออกว่าเป็น "รหัส" ไม่ใช่ส่วนหนึ่งของชื่อวิชา จึงใส่พื้นและ tabular-nums
   ให้ตัวเลขเรียงตรงกันทุกแถว เวลาครูกวาดตาหาวิชาเดียวกันจะเจอเร็ว */
.xh-code {
  display: inline-block; margin-right: 5px; padding: 1px 6px;
  border-radius: var(--r-xs); background: var(--sk-wash); color: var(--sk-ink);
  font-size: .72rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.xh-sub { font-size: .74rem; color: var(--text-muted); }

/* สร้างข้อสอบชุดใหม่ — เลือกวิชาก่อน */
.xh-newgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; }
.xh-new {
  display: block; padding: 12px 14px; border-radius: var(--r-md);
  border: 1px dashed var(--border-color); background: var(--bg-card);
  color: var(--text-main); text-decoration: none; transition: border-color .15s, background .15s;
}
.xh-new:hover { border-style: solid; border-color: var(--sk); background: var(--sk-wash); color: var(--sk-ink); }
.xh-new b { display: block; font-size: .88rem; }
.xh-new span { display: block; font-size: .74rem; color: var(--text-muted); margin-top: 2px; }
.xh-new em { display: block; font-size: .72rem; color: var(--text-muted); font-style: normal; margin-top: 3px; }

/* ── แถบสรุป "เวลา" ในโมดัลตั้งค่าข้อสอบ (3 ก.ย. 2569) ────────────────────────
   เจ้าของท้วงว่า "ปิดสอบ (บังคับส่ง) กับเวลาในการทำ มันคนละอันกันนะ" — ระบบแยก
   ถูกอยู่แล้ว แต่หน้าตั้งค่าไม่เคยพูดออกมา ครูจึงต้องเดาเอง · รูปแบบเดียวกับแถบ
   สรุปด่านเข้าสอบ (`#exGateSum`) เพื่อให้อ่านเป็นภาษาเดียวกันทั้งโมดัล */
.xe-timesum {
  margin: -4px 0 14px; padding: 11px 13px;
  border: 1px solid var(--sk-line, var(--border-color)); border-left: 3px solid var(--sk);
  border-radius: var(--r-sm); background: var(--sk-wash, var(--bg-soft));
  font-size: .82rem; line-height: 1.65; color: var(--text-main);
}
.xe-timesum b { color: var(--sk-ink); }
.xe-timesum b.text-danger { color: var(--red-ink) !important; }
.xe-timesum-n { color: var(--text-muted); font-size: .78rem; }

/* ══ ห้องที่จะใช้ข้อสอบ · จัดกลุ่มตามตัวชี้วัด (migration_v54 · 3 ก.ย. 2569) ══
   เจ้าของสั่ง: ผู้ตรวจต้องรู้ว่าข้อสอบที่ตรวจจะเอาไปใช้กับห้องไหน · และเวลาคน
   อ่านข้อสอบ (ครู/ผู้ตรวจ/ผู้บริหาร) ให้แบ่งข้อสอบตามตัวชี้วัด
   นักเรียนไม่เคยเห็นคลาสพวกนี้ — ห้องสอบใช้ CSS ของ includes/exam_head.php  */

/* ป้าย "แก้แล้วต้องตรวจใหม่" — ต้องอ่านออกก่อนกด ไม่ใช่รู้ตอนกดไปแล้ว */
.xr-mat {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px 8px; border-radius: var(--r-pill);
  background: var(--gold-wash); color: var(--gold-ink);
  font-size: .68rem; font-weight: 700; vertical-align: middle; white-space: nowrap;
}
.xr-mat::before { content: "\F33A"; font-family: bootstrap-icons; font-size: .72rem; }

.xr-rooms { display: flex; flex-wrap: wrap; gap: 8px; }
.xr-room {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 5px 11px; border-radius: var(--r-pill);
  border: 1px solid var(--border-color); background: var(--bg-soft);
  font-size: .84rem; font-weight: 600; color: var(--text-main);
}
.xr-room.is-own { border-color: var(--sk); background: var(--sk-wash); color: var(--sk-ink); }
.xr-room > i { font-size: .8rem; opacity: .7; }
.xr-room em { font-style: normal; font-size: .7rem; font-weight: 700; color: var(--sk-ink); }
.xr-room small { font-size: .72rem; font-weight: 400; color: var(--text-muted); }

.xr-roompick { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 8px; }
.xr-roomopt {
  display: flex; align-items: flex-start; gap: 9px; cursor: pointer;
  padding: 9px 11px; border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-card); transition: border-color .15s, background .15s;
}
.xr-roomopt:hover { border-color: var(--sk); background: var(--sk-wash); }
/* ห้องหลักติ๊กออกไม่ได้ — ต้องดูเหมือนกดไม่ได้จริง ๆ ไม่ใช่ดูเหมือนกดได้แล้วเงียบ */
.xr-roomopt.is-own { cursor: default; background: var(--bg-soft); }
.xr-roomopt.is-own:hover { border-color: var(--border-color); background: var(--bg-soft); }
.xr-roomopt > span { min-width: 0; display: flex; flex-direction: column; gap: 2px; font-size: .84rem; }
.xr-roomopt em { font-style: normal; font-size: .7rem; font-weight: 700; color: var(--sk-ink); }
.xr-roomopt small { font-size: .74rem; color: var(--text-muted); }

/* ── รูปแบบข้อสอบในโมดัลตั้งค่า (migration_v68 · 13 ก.ย. 2569) ─────────────────
   หน้าตาชุดเดียวกับ .xr-roomopt ที่อยู่ในโมดัลเดียวกัน · ตัวที่เลือกอยู่ต้องเห็นชัด
   เพราะเลือกผิดแล้วห้องสอบเปลี่ยนทั้งหน้า */
.xe-fmtpick { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 8px; }
.xe-fmt {
  display: flex; align-items: flex-start; gap: 9px; cursor: pointer;
  padding: 10px 12px; border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-card); transition: border-color .15s, background .15s;
}
.xe-fmt:hover { border-color: var(--sk); background: var(--sk-wash); }
.xe-fmt:has(input:checked) { border-color: var(--sk-dk); background: var(--sk-wash); box-shadow: inset 0 0 0 1px var(--sk-dk); }
.xe-fmt > input { flex: none; margin-top: 3px; }
.xe-fmt > span { min-width: 0; display: flex; flex-direction: column; gap: 3px; font-size: .86rem; }
.xe-fmt small { font-size: .74rem; color: var(--text-muted); line-height: 1.5; }

/* ── แผงไฟล์ข้อสอบ PDF ในหน้าแก้ไขข้อสอบ (migration_v68) ── */
.xe-pdf-now { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; font-size: .9rem; }
.xe-pdf-thumbs { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 10px; margin: 10px 0 14px; }
.xe-pdf-th { display: flex; flex-direction: column; gap: 4px; color: var(--text-muted); font-size: .74rem; min-width: 0; }
.xe-pdf-th > a { display: block; text-decoration: none; }
/* พื้นขาวแม้โหมดมืด — หน้าสแกนเป็นพื้นขาว ถ้าโปร่งจะกลืนกับพื้นมืด */
.xe-pdf-th img { display: block; width: 100%; aspect-ratio: 1 / 1.414; object-fit: cover; object-position: top; background: #fff; border: 1px solid var(--border-color); border-radius: var(--r-xs, 6px); }
.xe-pdf-th > a:hover img, .xe-pdf-th > a:focus-visible img { border-color: var(--sk); }
.xe-pdf-th-ft { display: flex; align-items: center; justify-content: space-between; gap: 4px; min-height: 26px; }
/* ปุ่มลบหน้า — เงียบตอนปกติ ขึ้นแดงตอนชี้ (บทเรียนเดียวกับที่จับขยายช่อง: ปุ่มลบ 40 ปุ่มสีแดงทั้งกริดดังเกิน) */
.xe-pdf-drop { display: inline-flex; align-items: center; gap: 3px; padding: 2px 7px; border: 1px solid transparent; border-radius: 99px; background: transparent; color: var(--text-muted); font-size: .72rem; line-height: 1.4; cursor: pointer; }
.xe-pdf-drop .bi { font-size: .66rem; }
.xe-pdf-drop:hover, .xe-pdf-drop:focus-visible { color: var(--red-ink); border-color: currentColor; background: var(--bg-soft); }
.xe-pdf-drop:disabled { opacity: .5; cursor: default; }
.xe-pdf-empty { padding: 18px; border: 1px dashed var(--border-color); border-radius: var(--r-sm); color: var(--text-muted); text-align: center; margin-bottom: 12px; }
.xe-pdf-up { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.xe-pdf-prog { margin-top: 12px; }
.xe-pdf-bar { height: 8px; border-radius: 99px; background: var(--bg-soft); overflow: hidden; }
.xe-pdf-bar > span { display: block; height: 100%; width: 0; background: var(--sk-dk); transition: width .25s; }
.xe-pdf-msg { margin-top: 6px; font-size: .84rem; color: var(--text-main); }
.xe-pdf-msg[data-tone="bad"] { color: var(--red-ink); font-weight: 600; }
.xe-pdf-msg[data-tone="ok"]  { color: var(--green-ink); font-weight: 600; }

/* ── กระดาษคำตอบ (pdf_sheet) — ตารางกาเฉลย ── */
.xe-sheet-wrap { overflow-x: auto; }
/* ปุ่มลบรายข้อ (14 ก.ย. 2569) — เงียบตอนปกติ ขึ้นแดงตอนชี้ เหมือน .xe-pdf-drop (ปุ่มแดง 40 แถวดังเกินไป) */
.xe-sheet th.del, .xe-sheet td.del { width: 1%; white-space: nowrap; text-align: right; }
.xe-sheet-del { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border: 1px solid var(--border-color); border-radius: 99px; background: var(--bg-card); color: var(--text-muted); font-size: .78rem; line-height: 1.3; cursor: pointer; }
.xe-sheet-del .bi { font-size: .7rem; }
.xe-sheet-del:hover, .xe-sheet-del:focus-visible { color: var(--red-ink); border-color: currentColor; background: color-mix(in srgb, var(--red) 10%, var(--bg-card)); }
.xe-sheet-del:disabled { opacity: .5; cursor: progress; }
.xe-sheet { width: 100%; border-collapse: separate; border-spacing: 0; }
.xe-sheet th { padding: 6px 8px; font-size: .74rem; font-weight: 700; color: var(--text-muted); text-align: left; border-bottom: 1px solid var(--border-color); }
.xe-sheet td { padding: 6px 8px; border-bottom: 1px solid var(--border-color); vertical-align: middle; }
.xe-sheet .no { width: 52px; text-align: center; font-weight: 800; font-variant-numeric: tabular-nums; }
.xe-sheet .ty { width: 170px; }
.xe-sheet .sc { width: 96px; }
.xe-sheet tr.is-miss .no { color: var(--red-ink); background: var(--red-wash); }
.xe-keys { display: flex; flex-wrap: wrap; gap: 5px; }
/* **`position: relative` ที่ตัวห่อ** — radio ข้างในเป็น absolute ถ้าไม่มีกล่องอ้างอิงจะอ้างทั้งหน้าต่าง
   แล้วยืดหน้าให้เลื่อนลงไปในที่ว่างได้ (docs/ui.md · บั๊ก 13 ก.ย. 2569 ที่หน้าตรวจข้อสอบ) */
.xe-key { position: relative; margin: 0; }
.xe-key input { position: absolute; opacity: 0; pointer-events: none; }
.xe-key span {
  display: inline-grid; place-items: center; min-width: 38px; height: 34px; padding: 0 10px;
  border: 1px solid var(--border-color); border-radius: var(--r-sm); background: var(--bg-card);
  font-weight: 700; cursor: pointer; user-select: none; -webkit-user-select: none;
}
.xe-key:hover span { border-color: var(--sk); }
.xe-key input:checked + span { background: var(--sk-dk); border-color: var(--sk-dk); color: #fff; }
.xe-key input:focus-visible + span { outline: 2px solid var(--sk); outline-offset: 2px; }
/* ปุ่มบันทึกเกาะขอบล่าง — ตาราง 40 ข้อยาวกว่าจอเสมอ กาเฉลยข้อ 40 แล้วต้องกดบันทึกได้ทันที */
/* ── เฉลย + คำตอบของนักเรียนในแผงวิเคราะห์รายข้อ (exam_result · 14 ก.ย. 2569) ─────────────
   แถบของตัวที่เป็นเฉลยเป็นสีเขียว ตัวอื่นเทาจาง — ครูมองหาว่า "ตัวลวงตัวไหนดึงคนไปเยอะ" ได้ในแวบเดียว */
/* แถววิเคราะห์ (`.rd-segrow`) เป็น grid สองคอลัมน์ `1fr auto` — ก้อนใหม่ต้อง **กินเต็มแถว + min-width:0**
   ไม่งั้นถูกวางลงคอลัมน์ใดคอลัมน์หนึ่ง และตารางรายคน (ความกว้างขั้นต่ำ ~400px) ดันคอลัมน์ให้กว้างเกินจอ
   บนมือถือ 375px แผงตัด (`overflow:hidden`) ครึ่งขวาหายไปทั้งแถว — วัดเจอตอนทดสอบจริง 14 ก.ย. 2569 */
.rd-segrow > .xi-key, .rd-segrow > .xi-dist, .rd-segrow > .xi-people { grid-column: 1 / -1; min-width: 0; }
.xi-key { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; margin-top: 10px; font-size: .86rem; }
.xi-dist { display: grid; gap: 4px; margin-top: 8px; }
.xi-opt { display: grid; grid-template-columns: minmax(80px, 38%) minmax(60px, 1fr) auto; align-items: center; gap: 8px; font-size: .8rem; }
.xi-lb { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.xi-lb .bi { margin-right: 4px; color: var(--text-muted); }
.xi-bar { height: 8px; border-radius: var(--r-pill, 99px); background: var(--bg-soft); overflow: hidden; }
.xi-bar > i { display: block; height: 100%; background: var(--text-muted); opacity: .4; }
.xi-opt.is-ok .xi-lb { font-weight: 700; color: var(--green-ink); }
.xi-opt.is-ok .xi-lb .bi { color: var(--green-ink); }
.xi-opt.is-ok .xi-bar > i { background: var(--green-ink); opacity: .9; }
.xi-opt.is-bad .xi-lb .bi { color: var(--red-ink); }
.xi-opt.is-bad .xi-bar > i { background: var(--red-ink); opacity: .5; }
.xi-opt.is-blank { color: var(--text-muted); font-style: italic; }
.xi-n { color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.xi-more { font-size: .76rem; color: var(--text-muted); }
.xi-people { margin-top: 8px; }
.xi-people > summary { cursor: pointer; font-size: .8rem; font-weight: 600; color: var(--sk-ink); }
.xi-ptable { max-height: 380px; overflow: auto; margin-top: 6px; border: 1px solid var(--border-color); border-radius: var(--r-sm); background: var(--bg-card); }
.xi-ptable table { font-size: .8rem; }
.xi-ptable thead th { position: sticky; top: 0; z-index: 1; background: var(--bg-card); }
.xi-ans { min-width: 160px; max-width: 460px; white-space: pre-wrap; word-break: break-word; }

/* เติมคำในกระดาษคำตอบ (14 ก.ย. 2569) — รูปแบบ · ค่าคลาดเคลื่อน · ชุดคำตอบ อยู่บรรทัดบน ช่องเฉลยบรรทัดล่าง */
.xe-sheet-short { display: flex; flex-direction: column; gap: 5px; min-width: 240px; }
.xe-sheet-short-opt { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.xe-sheet-short-opt > select { width: auto; max-width: 100%; }
.xe-sheet-tol { display: inline-flex; align-items: center; gap: 4px; margin: 0; font-size: .76rem; color: var(--text-muted); white-space: nowrap; }
.xe-sheet-tol input { width: 86px; }
.xe-sheet-set { display: inline-flex; align-items: center; gap: 5px; margin: 0; font-size: .76rem; cursor: pointer; }
.xe-sheet-set:has(input:disabled) { opacity: .55; cursor: default; }
.xe-sheet-keypad:empty { display: none; }
.xe-sheet-note { display: flex; gap: 8px; margin: 10px 0 0; padding: 8px 10px; border-radius: var(--r-sm); background: var(--bg-soft); font-size: .8rem; color: var(--text-muted); }
.xe-sheet-note code { color: var(--text-color); }
.xe-sheet-foot { position: sticky; bottom: 0; z-index: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 10px 0; background: var(--bg-card); }
.xe-sheet-more > summary { cursor: pointer; font-size: .86rem; font-weight: 600; color: var(--sk-ink); }

/* ── กรอกคะแนนแบบเขียนตอบบนกระดาษ (หน้าผลสอบ) — ปุ่มบันทึกเกาะขอบล่าง
   ห้องหนึ่ง 40 คน กรอกคนสุดท้ายแล้วต้องกดบันทึกได้ทันที ไม่ใช่เลื่อนหาปุ่ม ── */
.xe-paper-save { position: sticky; bottom: 0; z-index: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 10px 16px; background: var(--bg-card); border-top: 1px solid var(--border-color); }

/* ── หน้าข้อสอบ PDF ในหน้าตรวจข้อสอบ (migration_v68) — ผู้ตรวจต้องอ่านได้ ไม่ใช่รูปย่อ ── */
.xr-pdf > summary { list-style: none; cursor: pointer; }
.xr-pdf > summary::-webkit-details-marker { display: none; }
.xr-pdf-pages { display: flex; flex-direction: column; align-items: center; gap: 14px; max-height: 80vh; overflow-y: auto; padding: 4px; }
.xr-pdf-pages figure { margin: 0; width: 100%; max-width: 820px; }
/* พื้นขาวเสมอ — หน้าสแกนเป็นพื้นขาว ขอบจะหายไปกับพื้นมืดในโหมดมืด */
.xr-pdf-pages img { display: block; width: 100%; height: auto; background: #fff; border: 1px solid var(--border-color); border-radius: var(--r-xs, 6px); }
.xr-pdf-pages figcaption { margin-top: 4px; text-align: center; font-size: .74rem; color: var(--text-muted); }

/* ปุ่มสลับมุมมองข้อสอบ (แยกตามตัวชี้วัด ↔ เรียงตามข้อ) */
.xr-qtoggle { margin-left: auto; display: inline-flex; gap: 4px; }
.xr-qtoggle > a {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 11px; border-radius: var(--r-pill);
  border: 1px solid var(--border-color); background: var(--bg-card);
  font-size: .76rem; font-weight: 600; color: var(--text-muted); text-decoration: none;
}
.xr-qtoggle > a:hover { border-color: var(--sk); color: var(--sk-ink); }
.xr-qtoggle > a.is-on { border-color: var(--sk-dk); background: var(--sk-dk); color: #fff; }

/* กลุ่มของตัวชี้วัดหนึ่งตัว */
.xr-indgrp { margin-bottom: 18px; }
.xr-indgrp-hd {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 12px;
  padding: 10px 14px; margin-bottom: 10px;
  border-left: 3px solid var(--sk); border-radius: var(--r-sm);
  background: var(--sk-wash);
}
.xr-indgrp-code {
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 700; font-size: .92rem; color: var(--sk-ink);
}
.xr-indgrp-code.is-none { color: var(--gold-ink); }
.xr-indgrp-hd:has(.is-none) { border-left-color: var(--gold); background: var(--gold-wash); }
.xr-indgrp-t { flex: 1 1 240px; min-width: 0; font-size: .84rem; color: var(--text-main); }
.xr-indgrp-n { font-size: .76rem; color: var(--text-muted); white-space: nowrap; }

/* ข้อที่วัดหลายตัวชี้วัด — วาดเต็มครั้งเดียว ที่เหลือชี้กลับไปที่การ์ดใบนั้น */
.xr-qref {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  padding: 9px 13px; margin-bottom: 10px;
  border: 1px dashed var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-soft); font-size: .82rem; font-weight: 600;
  color: var(--text-main); text-decoration: none;
}
.xr-qref:hover { border-color: var(--sk); background: var(--sk-wash); color: var(--sk-ink); }
.xr-qref > span { flex: 1 1 200px; min-width: 0; font-weight: 400; color: var(--text-muted); }
.xr-qref em { font-style: normal; font-size: .72rem; color: var(--text-muted); }
.xr-secttl { flex-wrap: wrap; }

/* ══ ปุ่ม "แก้ไข" — `.sg-edit` (3 ก.ย. 2569) ═══════════════════════════════
   เจ้าของสั่งรอบแรกว่า "ปุ่มที่ใช้สำหรับ edit แก้ไข ก็ให้เหมือนกันทั้งเว็บ" แล้วขยาย
   ความว่า *"หมายถึงให้มันเป็นเงาแล้วดูกดได้ เฉย ๆ พวกสี พวกคำ icon ก็เอาตามเดิม"*
   → คลาสนี้จึงเป็น **ชั้นเคลือบอย่างเดียว** ไม่ได้แทนที่ปุ่ม

   **ห้ามใส่ background / color / ขนาด / เนื้อในกลับเข้ามาที่นี่** สีของปุ่มแต่ละใบ
   มาจากคลาส Bootstrap เดิมของหน้านั้น (`btn-outline-secondary` · `btn-warning` · …)
   ถ้าคลาสนี้ทับสี ปุ่มทั้งเว็บจะกลายเป็นสีเดียวกันหมด ซึ่งคือสิ่งที่ถูกสั่งให้เอาออก
   (รอบแรกทำแบบนั้นไปแล้วแล้วโดนแก้กลับ)

   เงาเป็น **ขอบล่างแข็ง** ไม่ใช่เงาฟุ้ง เพราะสิ่งที่ทำให้ "ดูกดได้" คือตัวปุ่มจมลงไป
   กลบขอบนั้นตอน `:active` — เงาฟุ้งจมแล้วตาไม่เห็นความต่าง · สีเงาเป็นดำโปร่ง
   ไม่ใช่เฉดเข้มของปุ่มเอง เพราะคลาสนี้ไม่รู้ (และไม่ควรรู้) ว่าปุ่มใบไหนสีอะไร */
.sg-edit {
  --edit-sh: rgba(15, 23, 42, .28);
  position: relative;
  box-shadow: 0 3px 0 var(--edit-sh);
  transform: translateY(0);
  transition: transform .12s ease, box-shadow .12s ease;
  /* เงาไม่กินที่ในเลย์เอาต์ — พอปุ่มในแถวเดียวกันตกบรรทัด (จอแคบ/ช่องตารางแคบ)
     ขอบล่างจะทับปุ่มบรรทัดถัดไปจนดูเหมือนปุ่มซ้อนกัน (เจ้าของแจ้ง 3 ก.ย. 2569)
     กันด้วย margin เท่าความสูงของขอบ ไม่ใช่ไปไล่ใส่ gap ทีละที่ที่ใช้ปุ่มนี้ */
  margin-bottom: 3px;
}
html.dark .sg-edit { --edit-sh: rgba(0, 0, 0, .55); }
.sg-edit:hover  { box-shadow: 0 4px 0 var(--edit-sh); transform: translateY(-1px); }
.sg-edit:active { box-shadow: 0 0 0 var(--edit-sh); transform: translateY(3px); }
.sg-edit:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.sg-edit:disabled, .sg-edit.disabled { box-shadow: none; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .sg-edit { transition: none; }
  .sg-edit:hover, .sg-edit:active { transform: none; }
}

/* ══ ป้ายชื่อปุ่มไอคอน — `.sg-tip` (4 ก.ย. 2569) ═══════════════════════════
   เจ้าของสั่ง: *"พวกปุ่มที่มีแค่ icon ต้องเอาเมาส์ไปชี้สักพักถึงจะขึ้นข้อความ ...
   พวกปุ่มเอาแบบเดิม แต่เวลาเอา mouse ไปชี้แล้วมันจะขึ้นสี แล้วมีข้อความขึ้นแบบนี้"*

   **มีตัวเดียวทั้งหน้า อยู่ท้าย `<body>`** — ปุ่มพวกนี้เกือบทั้งหมดอยู่ในช่อง
   "จัดการ" ของตารางที่อยู่ใน `.table-responsive` ซึ่งตั้ง `overflow-x: auto`
   ทำให้ `overflow-y` กลายเป็น `auto` ตามสเปก ป้ายที่ลอยเหนือปุ่มจะถูกตัดหาย
   ทันที ถ้าทำเป็น `::after` ของตัวปุ่มเอง (main.js อธิบายไว้ละเอียดกว่านี้)

   **สีมาจาก JS ไม่ได้ตั้งที่นี่** (`--tip-bg` / `--tip-fg`) เพราะ CSS ไม่มีทางรู้
   ว่าปุ่มใบไหนสีอะไร และกฎเดิมของ `.sg-edit` ห้ามทับสีปุ่มทั้งเว็บให้เป็นสีเดียว */
.sg-tip {
  --tip-bg: #0f172a;
  --tip-fg: #fff;
  --tip-ax: 50%;                     /* ตำแหน่งลูกศร — ชี้กลางปุ่มเสมอ */
  position: fixed;
  z-index: 2000;                     /* เหนือ modal-backdrop (1050) และ sticky ทุกตัว */
  left: 0; top: 0;
  max-width: min(260px, 90vw);
  padding: 5px 9px;
  border-radius: 7px;
  background: var(--tip-bg);
  color: var(--tip-fg);
  font-size: .78rem;
  line-height: 1.35;
  font-weight: 600;
  white-space: nowrap;
  box-shadow: 0 6px 16px rgba(0, 0, 0, .18);
  /* ป้ายอยู่ใต้เมาส์พอดี ถ้ารับ event จะกะพริบไม่หยุด (ชี้โดนป้าย = ออกจากปุ่ม
     = ป้ายหาย = ชี้โดนปุ่มอีก = ป้ายขึ้น วนไปเรื่อย ๆ) */
  pointer-events: none;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity .12s ease, transform .12s ease;
}
.sg-tip.is-on { opacity: 1; transform: translateY(0); }
.sg-tip::before {
  content: "";
  position: absolute;
  left: var(--tip-ax);
  bottom: -4px;
  width: 9px; height: 9px;
  margin-left: -4.5px;
  background: var(--tip-bg);
  transform: rotate(45deg);
  border-radius: 1px;
}
/* ปุ่มแถวบนสุดไม่มีที่ให้ป้ายอยู่ข้างบน — พลิกลงล่างแล้วลูกศรต้องพลิกตาม */
.sg-tip.is-below { transform: translateY(-4px); }
.sg-tip.is-below.is-on { transform: translateY(0); }
.sg-tip.is-below::before { bottom: auto; top: -4px; }

@media (prefers-reduced-motion: reduce) {
  .sg-tip { transition: none; transform: none; }
  .sg-tip.is-on, .sg-tip.is-below, .sg-tip.is-below.is-on { transform: none; }
}
/* จอสัมผัสไม่มี hover — การแตะปุ่มคือการสั่งงาน ไม่ใช่การถาม
   ปล่อยให้ป้ายขึ้นจะบังปุ่มใบข้าง ๆ ตอนที่นิ้วกำลังจะกดพอดี */
@media (hover: none) {
  .sg-tip { display: none; }
}

/* รายการตัวชี้วัดบนใบปะหน้า — อ่านอย่างเดียว (ที่กรอกอยู่หน้าแก้ไขข้อสอบ) */
.xr-stdlist { margin: 0 0 .25rem; padding-left: 1.1rem; }
.xr-stdlist li { font-size: .86rem; line-height: 1.55; }
.xr-stdlist li + li { margin-top: .2rem; }
/* ชิปตัวชี้วัดบนหัวข้อในหน้าแก้ไขข้อสอบเป็นปุ่มจริง — ต้องไม่มีขอบ/พื้นของ <button> */
/* `font: inherit` ไม่ได้ — shorthand จะรีเซ็ต font-size/weight ของ .rd-tag ทิ้ง
   (button.rd-tag ชนะ .rd-tag ด้วย specificity) ชิปจะกลายเป็นตัวใหญ่ผิดขนาด */
button.rd-tag { border: 0; font-family: inherit; cursor: pointer; line-height: inherit; }

/* ══ ที่จับยืดช่องข้อความ — `.sg-ta` (main.js เป็นคนใส่ให้) ══════════════════
   แถบเต็มความกว้างใต้ช่อง เขียนบอกตรง ๆ ว่าลากได้ · ที่จับเดิมของเบราว์เซอร์เป็น
   สามเหลี่ยมจาง 7×7px มุมขวาล่าง ครูส่วนใหญ่มองไม่เห็นและบนแท็บเล็ตลากไม่ติด */
.sg-ta { position: relative; }
.sg-ta > textarea { display: block; width: 100%; margin-bottom: 0 !important;
  border-bottom-left-radius: 0 !important; border-bottom-right-radius: 0 !important; }
/* **เงียบตอนปกติ ขึ้นสีตอนชี้/ลาก** (เจ้าของสั่งรอบสาม 8 ก.ย. 2569: "เด่นเกินไป")
   เคยเป็นแถบสีธีมทึบเต็มความกว้าง ซึ่งแก้ปัญหา "มองไม่เห็น" ได้จริง แต่กลายเป็น
   สิ่งที่เด่นที่สุดในฟอร์ม ทั้งที่เป็นแค่ที่จับ — ตอนนี้ใช้โทนกลางเดียวกับขอบช่อง
   กรอก แล้วให้สีของธีมโผล่ตอน hover/ลากแทน ซึ่งเป็นจังหวะที่ต้องการคำยืนยันจริง ๆ

   **สิ่งที่ห้ามลดตาม: สูง 28px กับข้อความ** — เหตุผลตั้งต้นของแถบนี้คือครูมองไม่เห็น
   ที่จับ 7×7px ของเบราว์เซอร์ และนิ้วบนแท็บเล็ตกดไม่โดน · จางลงได้ แต่เล็กลงไม่ได้ */
.sg-ta-grip {
  display: flex; align-items: center; justify-content: center; gap: .5rem;
  min-height: 28px; padding: 4px 8px; margin-top: -1px;
  border: 1px solid var(--border-color); border-top: 0;
  border-radius: 0 0 var(--r-md, 10px) var(--r-md, 10px);
  background: var(--bg-base); color: var(--text-muted);
  font-size: .7rem; font-weight: 500; line-height: 1;
  /* -webkit- ต้องมี — iPad/Safari ยังไม่รู้จัก user-select ตัวเปล่า และแถบนี้
     มีไว้เพื่อแท็บเล็ตโดยเฉพาะ ถ้าขาดไปการลากจะกลายเป็นการลากเลือกข้อความ */
  cursor: ns-resize; -webkit-user-select: none; user-select: none; touch-action: none;
  transition: background .15s, color .15s, border-color .15s;
}
.sg-ta-grip:hover, .sg-ta.is-drag .sg-ta-grip {
  background: var(--sk-wash); color: var(--sk-ink); border-color: var(--sk);
}
/* โหมดมืด: `--sk-wash` เป็นฟ้าอ่อนตายตัวทั้งสองโหมด ถ้าใช้ตรง ๆ แถบจะสว่างวาบ
   ตอนเมาส์ผ่าน ซึ่งเด่นกว่าตอนที่มันเป็นสีทึบเสียอีก — ผสมสีธีมลงบนพื้นการ์ดแทน */
html.dark .sg-ta-grip:hover, html.dark .sg-ta.is-drag .sg-ta-grip {
  background: color-mix(in srgb, var(--sk) 20%, var(--bg-card));
  color: var(--text-main); border-color: var(--sk);
}
/* ขีดสองข้างเป็นแค่สัญญาณว่า "ลากได้" ไม่ใช่ของที่ต้องอ่าน — จางกว่าตัวหนังสือเสมอ */
.sg-ta-bar { width: 18px; height: 2px; border-radius: 2px; background: currentColor; opacity: .4; }
.sg-ta > textarea { border-bottom-color: var(--border-color) !important; }
/* ลากอยู่ = อย่าให้ตัวชี้กลายเป็น text cursor ตอนเลยขอบแถบออกไป */
.sg-ta.is-drag, .sg-ta.is-drag * { cursor: ns-resize !important; }
@media print { .sg-ta-grip { display: none; } }

/* การ์ดทั้งใบกดได้ — ต้องมีสัญญาณว่ากดได้ ไม่ใช่กดได้เงียบ ๆ (main.js) */
[data-card-href] { cursor: pointer; }
/* ช่องเวลาข้อที่ถูกปิดเพราะข้อนี้อยู่ในชุดสถานการณ์ */
.xe-extra.is-off { opacity: .5; }
/* ช่อง "จัดการ" ของตาราง — ปุ่มอยู่บรรทัดเดียวเสมอ (main.js ติดคลาสให้)
   ตกบรรทัดในช่องแคบ = ปุ่มเรียงลงมาชนกันจนดูเหมือนซ้อน (เจ้าของแจ้ง 3 ก.ย. 2569) */
td.sg-actcell { white-space: nowrap; }
td.sg-actcell > .d-flex, td.sg-actcell > div { flex-wrap: nowrap !important; }

/* ══ ป้ายที่ทำหน้าที่เป็นปุ่มตัวเลือก — ห้ามลากเลือกข้อความได้ ═══════════════
   เจ้าของแจ้ง 3 ก.ย. 2569 ว่ากดสลับ "ถูกเท่าไหร่ได้เท่านั้น"/"ต้องถูกทั้งหมด"
   แล้วมี "อะไรขาว ๆ มาบัง" · ในภาพที่ส่งมา ข้อความบนป้ายถูกไฮไลต์เป็นแถบน้ำเงิน
   พร้อมไอคอน Google Translate อยู่ข้าง ๆ — ตัวที่มาบังคือป็อปอัปของส่วนขยาย
   แปลภาษา ซึ่งเด้งขึ้นมาเพราะ **การคลิกป้ายไปลากเลือกข้อความในป้าย**

   ป้ายพวกนี้คือปุ่ม ไม่ใช่เนื้อความให้อ่านแล้วคัด · `user-select:none` จึงทั้งแก้
   อาการนี้และแก้อาการดั้งเดิมที่ดับเบิลคลิกเร็ว ๆ แล้วข้อความกลายเป็นแถบน้ำเงิน */
.xe-gm-c, .xe-tcard, .att-st, .cc-sw, .xr-qpick-i { user-select: none; -webkit-user-select: none; }
/* ช่องเลขหน่วยการเรียนรู้ — แคบ ๆ อยู่หน้าชื่อหน่วย (ใบปะหน้าข้อสอบ) */
.xe-unit-no {
  width: 68px; flex: 0 0 68px; text-align: center;
  border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-input); color: var(--text-main);
  padding: .4rem .3rem; font-weight: 700;
}
.xe-unit-no:focus { outline: none; border-color: var(--sk); }
.xe-pair-t { min-width: 0 !important; padding: 0 .15rem; font-weight: 600; color: var(--text-muted); }

/* คำอธิบายใต้การ์ดเกณฑ์การให้คะแนน — จองที่ไว้ 2 บรรทัด
   ข้อความสองแบบยาวไม่เท่ากัน ถ้าปล่อยให้สูงเปลี่ยนตามข้อความ ทุกอย่างใต้มัน
   (คะแนน · เวลา · สื่อ · ปุ่มบันทึก) จะกระโดดทุกครั้งที่ครูสลับตัวเลือก และ
   การเปลี่ยนความสูงกลางแผง sticky ที่มีตัวเลื่อนของตัวเองคือต้นเหตุของ
   "พื้นที่ขาวค้าง" ที่เจ้าของแจ้งไว้ 3 ก.ย. 2569 (ดู xeRepaintPanel ใน exam_edit) */
#qGmNote { min-height: 3em; }

/* ══════════════════════════════════════════════════════════════════════════
   ตรวจข้อสอบ — รอบแก้ไข · เช็คลิสต์ก่อนส่ง · สรุปก่อนตัดสินใจ (8 ก.ย. 2569)
   ต่อจากบล็อก `.xr-*` ด้านบน ใช้โทเคนชุดเดียวกันทั้งหมด ไม่มีสีดิบ
══════════════════════════════════════════════════════════════════════════ */

/* ── โทเคนพื้นอ่อนสามสีที่บล็อก `.xr-*` เรียกใช้มาตั้งแต่ต้นแต่ไม่เคยมีใครนิยาม ──
   `--red-wash` / `--gold-wash` / `--green-wash` ถูกใช้ 20 กว่าที่ (แถบขั้นตอน ·
   กล่องเตือน · ไอคอนของคิวงาน) แต่ไม่มีอยู่จริงในไฟล์ไหนเลย ค่าที่ได้จึงเป็น
   "ไม่มีค่า" = พื้นโปร่งใส · อาการคือป้ายสถานะสีจาง ๆ ที่ดูเหมือนตั้งใจ จึงไม่มี
   ใครสังเกต · ชี้ไปที่ชุด `--rd-wash-*` ซึ่งมีค่าโหมดสว่าง/มืดครบอยู่แล้ว
   (theme.css บรรทัด 782-783) แทนที่จะตั้งสีดิบชุดใหม่ให้ต้องดูแลอีกชุด        */
:root {
  --red-wash:   var(--rd-wash-red);
  --gold-wash:  var(--rd-wash-gold);
  --green-wash: var(--rd-wash-green);
}

/* บรรทัด "ตอนนี้อยู่ที่ใคร" — คำตอบแรกที่ทุกบทบาทต้องได้จากหน้านี้ */
.xr-now {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 12px; padding: 9px 13px; border-radius: var(--r-sm);
  background: var(--sk-wash); color: var(--sk-ink); font-size: .88rem;
}
.xr-now > i { flex: none; }
.xr-now > span { color: var(--text-muted); font-size: .8rem; }

/* หัวข้อย่อยของกอง (ต้องแก้ไข / กำลังจัดทำ / รอตรวจ / อนุมัติแล้ว) */
.xr-secttl.is-sub { font-size: .95rem; margin-top: 6px; }

/* แถบนับจำนวนของแต่ละกอง — กดแล้วกระโดดไปหัวข้อของกองนั้น */
.xr-bkbar { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.xr-bk {
  display: flex; align-items: center; gap: 8px; padding: 8px 14px;
  border: 1px solid var(--border-color); border-radius: var(--r-md);
  background: var(--bg-card); color: var(--text-main); text-decoration: none;
  transition: border-color .14s, transform .14s;
}
.xr-bk:hover { border-color: var(--sk-lt); transform: translateY(-1px); color: inherit; }
.xr-bk > i { color: var(--sk-dk); }
.xr-bk b { font-size: 1.1rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.xr-bk span { font-size: .8rem; color: var(--text-muted); }
.xr-bk.is-zero { opacity: .55; }

/* ปุ่มหลักของแต่ละแถวในกล่องงาน — น้ำหนักมากกว่าลิงก์ข้าง ๆ แต่ยังไม่ใช่ปุ่มเต็ม */
.xr-rowgo { font-weight: 700; color: var(--sk-ink) !important; }
.xr-rowgo.is-warn { color: var(--red-ink) !important; }
.xr-rowgo.is-ok   { color: var(--green-ink) !important; }

/* ── เช็คลิสต์ "ส่งตรวจได้หรือยัง" ────────────────────────────────────────
   รายการมาจาก examReviewReadiness() ทั้งหมด · แต่ละบรรทัดมีทางไปแก้ตรงจุด
   ไม่ใช่บอกว่าขาดแล้วปล่อยให้ครูหาเอง                                      */
.xr-check { list-style: none; margin: 0 0 10px; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.xr-check > li {
  display: flex; align-items: flex-start; gap: 10px; padding: 9px 12px;
  border-radius: var(--r-sm); border-left: 3px solid var(--border-color); background: var(--bg-soft);
}
.xr-check > li > i { flex: none; margin-top: 2px; }
.xr-check > li > div { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.xr-check > li b { font-size: .88rem; font-weight: 700; color: var(--text-main); }
.xr-check > li span { font-size: .78rem; color: var(--text-muted); }
.xr-check > li .btn { flex: none; align-self: center; }
.xr-check > li.is-block { border-left-color: var(--red); }
.xr-check > li.is-block > i { color: var(--red-ink); }
.xr-check > li.is-warn { border-left-color: var(--gold); }
.xr-check > li.is-warn > i { color: var(--gold-ink); }
.xr-check-ok {
  display: flex; align-items: center; gap: 8px; margin-bottom: 10px;
  padding: 9px 12px; border-radius: var(--r-sm);
  background: var(--green-wash); color: var(--green-ink); font-size: .86rem; font-weight: 600;
}

/* ── สรุปก่อนตัดสินใจ (ผู้ตรวจ/ผู้บริหาร) ───────────────────────────────── */
.xr-brief-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px 16px;
  margin-bottom: 12px;
}
.xr-brief-grid > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.xr-brief-grid span { font-size: .74rem; color: var(--text-muted); font-weight: 600; }
.xr-brief-grid b { font-size: .9rem; color: var(--text-main); overflow-wrap: anywhere; }
.xr-stagerow { display: flex; flex-wrap: wrap; gap: 8px; }
.xr-stagechip {
  display: flex; align-items: center; gap: 7px; padding: 7px 11px; border-radius: var(--r-sm);
  background: var(--bg-soft); color: var(--text-muted); font-size: .78rem;
}
.xr-stagechip b { display: block; color: var(--text-main); font-size: .8rem; }
.xr-stagechip.is-ok  { background: var(--green-wash); color: var(--green-ink); }
.xr-stagechip.is-ok b { color: var(--green-ink); }
.xr-stagechip.is-now { background: var(--sk-wash); color: var(--sk-ink); }
.xr-stagechip.is-now b { color: var(--sk-ink); }

/* ── รอบแก้ไข: รายการที่ต้องแก้ + คำตอบของครู ───────────────────────────── */
.xr-roundsep {
  margin: 12px 0 8px; font-size: .78rem; font-weight: 700; color: var(--text-muted);
  border-top: 1px dashed var(--border-color); padding-top: 8px;
}
.xr-roundsep:first-child { margin-top: 0; border-top: 0; padding-top: 0; }
.xr-roundsep em { font-weight: 400; font-style: normal; opacity: .8; }
.xr-todo { display: flex; gap: 10px; align-items: flex-start; }
.xr-todo-where { flex: 0 0 96px; padding-top: 10px; font-size: .8rem; font-weight: 700; }
.xr-todo-where a { display: inline-flex; align-items: center; gap: 4px; color: var(--sk-ink); text-decoration: none; }
.xr-todo-where a:hover { text-decoration: underline; }
.xr-todo-where span { display: inline-flex; align-items: center; gap: 4px; color: var(--text-muted); font-weight: 600; }
.xr-todo-body { flex: 1 1 auto; min-width: 0; }

/* ชนิดของความเห็น — สีขอบซ้ายบอกได้ตั้งแต่ยังไม่อ่านข้อความ */
.xr-note.is-fix     { border-left-color: var(--red); }
.xr-note.is-suggest { border-left-color: var(--gold); }
.xr-note.is-done    { border-left-color: var(--green); }
.xr-note-t { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

/* คำตอบของครูอยู่ใต้เรื่องที่มันตอบ ไม่ใช่รายการแยก */
.xr-reply {
  margin: 8px 0 0 14px; padding: 8px 11px; border-radius: var(--r-sm);
  border-left: 2px solid var(--sk-lt); background: var(--bg-soft);
}
.xr-reply-hd { font-size: .74rem; font-weight: 700; color: var(--sk-ink); margin-bottom: 3px; }
.xr-reply-b { font-size: .84rem; color: var(--text-main); }
.xr-replybox { display: inline-block; }
.xr-replybox > summary { list-style: none; cursor: pointer; }
.xr-replybox > summary::-webkit-details-marker { display: none; }
.xr-replybox[open] { display: block; width: 100%; }

/* เลือกชนิดความเห็นก่อนพิมพ์ — ป้ายที่ทำหน้าที่เป็นปุ่ม */
.xr-noteform-in { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.xr-kind { display: flex; gap: 6px; flex-wrap: wrap; }
/* **`label` ต้องเป็น `position:relative`** (เจ้าของแจ้ง 13 ก.ย. 2569 "เลื่อนลงด้านล่างได้
   ทั้งที่ไม่มีข้อมูลแล้ว") — ช่อง radio ข้างล่างเป็น `position:absolute` ถ้าไม่มีบรรพบุรุษ
   ที่ตั้ง position ไว้ มันจะอ้างกล่องของ **ทั้งหน้าต่าง** ไม่ใช่ `.page-main` ที่เป็นตัวเลื่อน
   ช่องของข้อท้าย ๆ (y≈9000px) จึงไปยืด **เอกสารทั้งหน้า** ให้ยาวตาม ทั้งที่ `.app-shell`
   สูงเท่าจอ → หน้าต่างเลื่อนลงไปในที่ว่างได้ ขณะที่เนื้อหาใน `.page-main` ไม่ขยับ
   วัดซ้ำ: ก่อนแก้ `documentElement.scrollHeight` 9233px · หลังแก้ = ความสูงจอ */
.xr-kind label { position: relative; }
.xr-kind input { position: absolute; opacity: 0; pointer-events: none; }
.xr-kind span {
  display: inline-flex; align-items: center; gap: 5px; padding: .22rem .6rem;
  border: 1px solid var(--border-color); border-radius: 999px;
  font-size: .76rem; font-weight: 600; color: var(--text-muted); cursor: pointer;
  user-select: none; -webkit-user-select: none;
}
.xr-kind label:has(input:checked) span { border-color: var(--sk); background: var(--sk-wash); color: var(--sk-ink); }
.xr-kind input:focus-visible + span { outline: 2px solid var(--sk); outline-offset: 2px; }

/* ── แถบลงมือบนมือถือ — ทางลัดไปยังแผงจริง ไม่ใช่ปุ่มชุดที่สอง ───────────── */
.xr-mobar { display: none; }
@media (max-width: 991.98px) {
  .xr-mobar {
    position: sticky; bottom: 0; z-index: 5;
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    margin: 14px -12px -12px; padding: 10px 14px;
    border-top: 1px solid var(--border-color); background: var(--bg-card);
    box-shadow: 0 -6px 18px rgba(0,0,0,.08);
  }
  .xr-mobar-t { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
  .xr-mobar-t b { font-size: .86rem; }
  .xr-mobar-t span { font-size: .74rem; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .xr-todo { flex-direction: column; gap: 2px; }
  .xr-todo-where { flex: none; padding-top: 0; }
}
html.dark .xr-mobar { box-shadow: 0 -6px 18px rgba(0,0,0,.4); }

/* ══════════════════════════════════════════════════════════════════════════
   `<details class="sg-more">` — ส่วนตั้งค่าที่ "ไม่บังคับ" พับเก็บไว้ในโมดัล
   (8 ก.ย. 2569 · เจ้าของแจ้งว่าโมดัลก็อปรายวิชา "ตัวตั้งค่ามันเกินทำให้ตั้งยาก")

   หัวข้อต้องดูเป็น **ปุ่มกดได้** ไม่ใช่ข้อความธรรมดา — ของที่พับไว้แล้วมองไม่ออก
   ว่ากดได้ คือของที่ผู้ใช้ไม่มีวันหาเจอ ซึ่งแย่กว่าเลื่อนยาว ๆ
══════════════════════════════════════════════════════════════════════════ */
.sg-more { border: 1px solid var(--border-color); border-radius: var(--r-sm); background: var(--bg-soft); }
.sg-more > summary {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap; cursor: pointer;
  padding: .55rem .8rem; font-size: .9rem; font-weight: 600; color: var(--text-main);
  list-style: none; user-select: none; -webkit-user-select: none;
}
.sg-more > summary::-webkit-details-marker { display: none; }
/* ลูกศรบอกสถานะ วางไว้ท้ายบรรทัดเสมอ ไม่ให้ดันข้อความ */
.sg-more > summary::after {
  content: "\F282"; font-family: bootstrap-icons; margin-left: auto;
  font-size: .8rem; color: var(--text-muted); transition: transform .15s;
}
.sg-more[open] > summary::after { transform: rotate(180deg); }
.sg-more > summary:hover { background: var(--sk-wash); border-radius: var(--r-sm); }
.sg-more[open] > summary { border-bottom: 1px solid var(--border-color); border-radius: var(--r-sm) var(--r-sm) 0 0; }
.sg-more > :not(summary) { padding: 0 .8rem; }
.sg-more > :not(summary):last-child { padding-bottom: .8rem; }
.sg-more[open] > :not(summary):first-of-type { padding-top: .8rem; }
@media (prefers-reduced-motion: reduce) { .sg-more > summary::after { transition: none; } }

/* ══════════════════════════════════════════════════════════════════════════
   ตัวเลือกคำตอบเป็นรูป (migration_v60 · 8 ก.ย. 2569)
   เจ้าของสั่ง: "ทำให้คำตอบมันเพิ่มรูปได้หน่อย ในกรณีที่ครูมีข้อคำตอบเป็นรูป"

   แถวตัวเลือกในหน้าแก้ไขข้อสอบกลายเป็น `.xe-crow` = [ label ][ ปุ่มรูป ][ ลบ ]
   **ปุ่มรูปอยู่นอก `<label>`** เพราะคลิกใน label = ติ๊ก "ข้อที่ถูก" ของแถวนั้น
   ถ้าเอาไว้ข้างใน ครูกดใส่รูปให้ ค. แล้วเฉลยจะเปลี่ยนเป็น ค. เงียบ ๆ
══════════════════════════════════════════════════════════════════════════ */
.xe-crow { display: flex; align-items: center; gap: 6px; }
/* ชุดรูปหนึ่งชุด (ปุ่มรูป + ปุ่มเอารูปออก) — ข้อจับคู่มีสองชุดในแถวเดียว (v62)
   จึงต้องเป็นกล่องของตัวเอง ไม่ใช่ปุ่มสองปุ่มที่ลอยอยู่ในแถว ไม่งั้น JS หา
   "ชุดของฝั่งนี้" ไม่เจอ แล้วรูปทุกใบจะไปลงที่ฝั่งซ้ายหมด */
.qc-imgset { display: inline-flex; align-items: center; gap: 5px; flex: none; }

/* แถบเดินหน้าในหน้าตรวจข้อเขียน — ครูตรวจ 40 คนติดกัน ปุ่มเดินหน้าจึงต้องอยู่
   ทั้งหัวหน้า (ข้ามคนที่ไม่ต้องแก้) และท้ายฟอร์ม (จบคนนี้แล้วไปต่อทันที) */
.xg-nav { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.xg-nav-pos { display: inline-flex; align-items: center; gap: 8px; margin: 0 auto;
  font-size: .86rem; font-weight: 700; color: var(--text-muted); }
.xg-save { display: flex; gap: 10px; flex-wrap: wrap; }
.xe-crow > .xe-choice { flex: 1 1 auto; min-width: 0; margin-bottom: 0; }
/* ปุ่มใส่รูป — ว่างอยู่เป็นไอคอนจาง ๆ · มีรูปแล้วกลายเป็นรูปย่อที่กดเปลี่ยนได้ */
.xe-cimg {
  flex: none; width: 38px; height: 38px; padding: 0; overflow: hidden;
  display: grid; place-items: center; cursor: pointer;
  border: 1px dashed var(--border-color); border-radius: var(--r-sm);
  background: var(--bg-card); color: var(--text-muted); transition: border-color .14s, color .14s;
}
.xe-cimg:hover { border-color: var(--sk); color: var(--sk-ink); }
.xe-cimg.has-img { border-style: solid; border-color: var(--sk); }
.xe-cimg.has-img > i { display: none; }          /* มีรูปแล้วไม่ต้องมีไอคอนซ้อน */
.xe-cimg > img { width: 100%; height: 100%; object-fit: cover; }
.xe-cimg-x {
  flex: none; width: 26px; height: 26px; padding: 0; border: 0; border-radius: 50%;
  background: var(--rd-wash-red); color: var(--red-ink); cursor: pointer; font-size: .68rem;
}
.xe-cimg-x:hover { background: var(--red); color: #fff; }
@media (max-width: 575px) {
  .xe-cimg { width: 34px; height: 34px; }
}

/* ══ แถวที่มีของหลายชิ้น ต้องยอมให้ตกบรรทัด (เจ้าของแจ้ง 9 ก.ย. 2569) ═══════
   *"ตรงพิมพ์ข้อความมันเล็ก"* — แถวของ "จำแนกลงกลุ่ม" มีหกชิ้นในแถวเดียว
   (เลข · ช่องพิมพ์ · ปุ่มรูป · ปุ่มลบรูป · ลูกศร · ช่องเลือกกลุ่ม · ปุ่มลบแถว)
   และแผงฟอร์มอยู่คอลัมน์ขวาซึ่งแคบอยู่แล้ว · `flex:1 1 0` เดิมยอมให้ช่องพิมพ์
   หดได้ไม่จำกัด ช่องจึงเหลือกว้างไม่กี่ตัวอักษรจนพิมพ์แล้วอ่านไม่ออก

   **ตั้งความกว้างขั้นต่ำแล้วให้ของที่เหลือตกบรรทัดแทน** — ช่องพิมพ์คือสิ่งที่
   ครูใช้จริงทุกแถว ส่วนปุ่มรูป/ช่องเลือกกลุ่มอยู่บรรทัดล่างก็ยังกดได้เหมือนเดิม
   ทำที่จอทุกขนาด ไม่ใช่เฉพาะมือถือ เพราะแผงฟอร์มแคบตั้งแต่บนจอคอมแล้ว        */
/* `.xe-ordrow` ต้องได้กฎเดียวกัน (เจ้าของแจ้งซ้ำ 9 ก.ย. 2569 พร้อมรูปแผง
   "รายการที่ให้นักเรียนจัดเรียง") — แถวเรียงลำดับมี [เลข][ช่องพิมพ์][ปุ่มรูป]
   [ลบรูป][ขึ้น][ลง][ลบแถว] = เจ็ดชิ้น มากกว่าแถวจำแนกกลุ่มอีก ช่องพิมพ์จึงเหลือ
   กว้างไม่ถึงสิบตัวอักษรจนอ่านสิ่งที่ตัวเองพิมพ์ไม่ออก · แก้ที่ `.xe-pair` อย่างเดียว
   เมื่อรอบก่อนจึงยังเหลือแผงนี้ค้างไว้                                        */
.xe-pair, .xe-ordrow { flex-wrap: wrap; row-gap: 6px; }
.xe-pair > .xe-pair-in,
.xe-ordrow > .xe-pair-in { flex: 1 1 clamp(150px, 55%, 320px); min-width: 150px; }
/* ปุ่มเลื่อนขึ้น/ลง/ลบ ของแถวเรียงลำดับต้องเกาะกลุ่มกันไปทั้งชุดเวลาตกบรรทัด
   ไม่ใช่กระจายทีละปุ่ม — `margin-left:auto` ดันชุดปุ่มไปชิดขวาของบรรทัดที่มันอยู่ */
.xe-ordrow > .xe-rowmv:first-of-type { margin-left: auto; }
/* ช่องเลือกกลุ่มไม่ต้องกว้างคงที่ ปล่อยให้ยืดเต็มบรรทัดที่มันตกลงไป */
#qClassItems .cls-of { flex: 1 1 160px; max-width: none !important; }
@media (max-width: 575px) {
  .xe-pair > .xe-pair-in,
  .xe-ordrow > .xe-pair-in { flex-basis: 100%; }
}

/* รูปตัวเลือกในหน้าตรวจข้อสอบ — ผู้ตรวจต้องเห็นรูปจริง ไม่ใช่แค่รู้ว่า "มีรูป" */
.xr-cimg {
  display: block; margin-top: 6px; max-width: 220px; max-height: 150px;
  object-fit: contain; border: 1px solid var(--border-color); border-radius: var(--r-sm);
  background: #fff; padding: 3px;
}
